Skip to content

Commit

Permalink
2941/implement script score query (#2955)
Browse files Browse the repository at this point in the history
* [#2941] Implement script score query

Implements the script score query to fix #2941.
For now the ScriptScoreQueryBodyFn2 class has the terrible name because
I suspect that the current ScriptScoreQueryBodyFn is never exercised.
The ScriptScore that it accepts isn't actually a query, so will never
hit the pattern match?

* Remove ScriptScoreQueryBodyFn

From inspection I suspect this case would never be hit, because
ScriptScore doesn't implement the Query trait.
This was the only call site for ScriptScoreQueryBodyFn (which has an odd
name, given it accepted a ScriptScore and not a ScriptScoreQuery).
Therefore I've removed it, and put the ScriptScoreQueryBodyFn2 in its
place

* Add to query API and test

TODO: this test is failing currently

---------

Co-authored-by: Sam Briggs <[email protected]>
  • Loading branch information
sam-briggs-depop and Sam Briggs authored Nov 12, 2023
1 parent cf176de commit 962c515
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.sksamuel.elastic4s.requests.searches.queries.compound.BoolQuery
import com.sksamuel.elastic4s.requests.searches.queries.funcscorer.FunctionScoreQuery
import com.sksamuel.elastic4s.requests.searches.queries.geo.{GeoBoundingBoxQuery, GeoDistanceQuery, GeoPolygonQuery, GeoShapeQuery, Shape}
import com.sksamuel.elastic4s.requests.searches.queries.matches.{MatchAllQuery, MatchBoolPrefixQuery, MatchNoneQuery, MatchPhrasePrefixQuery, MatchPhraseQuery, MatchQuery, MultiMatchQuery}
import com.sksamuel.elastic4s.requests.searches.queries.{ArtificialDocument, BoostingQuery, CombinedFieldsQuery, ConstantScore, DisMaxQuery, DistanceFeatureQuery, ExistsQuery, FuzzyQuery, HasChildQuery, HasParentQuery, IdQuery, IntervalsQuery, IntervalsRule, MoreLikeThisItem, MoreLikeThisQuery, MultiTermQuery, NestedQuery, PercolateQuery, PinnedQuery, PrefixQuery, Query, QueryStringQuery, RangeQuery, RankFeatureQuery, RawQuery, RegexQuery, ScriptQuery, SimpleStringQuery}
import com.sksamuel.elastic4s.requests.searches.queries.{ArtificialDocument, BoostingQuery, CombinedFieldsQuery, ConstantScore, DisMaxQuery, DistanceFeatureQuery, ExistsQuery, FuzzyQuery, HasChildQuery, HasParentQuery, IdQuery, IntervalsQuery, IntervalsRule, MoreLikeThisItem, MoreLikeThisQuery, MultiTermQuery, NestedQuery, PercolateQuery, PinnedQuery, PrefixQuery, Query, QueryStringQuery, RangeQuery, RankFeatureQuery, RawQuery, RegexQuery, ScriptQuery, ScriptScoreQuery, SimpleStringQuery}
import com.sksamuel.elastic4s.requests.searches.span.{SpanContainingQuery, SpanFieldMaskingQuery, SpanFirstQuery, SpanMultiTermQuery, SpanNearQuery, SpanNotQuery, SpanOrQuery, SpanQuery, SpanTermQuery, SpanWithinQuery}
import com.sksamuel.elastic4s.requests.searches.term.{TermQuery, TermsLookupQuery, TermsQuery, TermsSetQuery, WildcardQuery}
import com.sksamuel.elastic4s.requests.searches.{GeoPoint, ScoreMode, TermsLookup, span, term}
Expand Down Expand Up @@ -191,6 +191,9 @@ trait QueryApi {
def scriptQuery(script: Script): ScriptQuery = ScriptQuery(script)
def scriptQuery(script: String): ScriptQuery = ScriptQuery(script)

def scriptScoreQuery(): ScriptScoreQuery = ScriptScoreQuery()
def scriptScoreQuery(query: Query): ScriptScoreQuery = ScriptScoreQuery().query(query)

def simpleStringQuery(q: String): SimpleStringQuery = SimpleStringQuery(q)
def stringQuery(q: String): QueryStringQuery = QueryStringQuery(q)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
package com.sksamuel.elastic4s.requests.searches.queries

case class ScriptScoreQuery(script: String) extends Query
import com.sksamuel.elastic4s.requests.script.Script

case class ScriptScoreQuery(query: Option[Query] = None, script: Option[Script] = None, minScore: Option[Double] = None, boost: Option[Double] = None) extends Query {
def boost(boost: Double): ScriptScoreQuery = copy(boost = Option(boost))
def minScore(min: Double): ScriptScoreQuery = copy(minScore = Option(min))
def query(query: Query): ScriptScoreQuery = copy(query = Some(query))
def script(script: Script): ScriptScoreQuery = copy(script = Some(script))
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ import com.sksamuel.elastic4s.requests.searches.queries.compound.BoolQuery
import com.sksamuel.elastic4s.requests.searches.queries.funcscorer.{FunctionScoreQuery, ScriptScore}
import com.sksamuel.elastic4s.requests.searches.queries.geo.{GeoBoundingBoxQuery, GeoDistanceQuery, GeoPolygonQuery, GeoShapeQuery}
import com.sksamuel.elastic4s.requests.searches.queries.matches.{MatchAllQuery, MatchBoolPrefixQuery, MatchNoneQuery, MatchPhrasePrefixQuery, MatchPhraseQuery, MatchQuery, MultiMatchQuery}
import com.sksamuel.elastic4s.requests.searches.queries.{BoostingQuery, CombinedFieldsQuery, ConstantScore, DisMaxQuery, DistanceFeatureQuery, ExistsQuery, FuzzyQuery, HasChildQuery, HasParentQuery, IdQuery, IntervalsQuery, MoreLikeThisQuery, NestedQuery, NoopQuery, ParentIdQuery, PercolateQuery, PinnedQuery, PrefixQuery, Query, QueryStringQuery, RangeQuery, RankFeatureQuery, RawQuery, RegexQuery, ScriptQuery, SimpleStringQuery}
import com.sksamuel.elastic4s.requests.searches.queries.{BoostingQuery, CombinedFieldsQuery, ConstantScore, CustomQuery, DisMaxQuery, DistanceFeatureQuery, ExistsQuery, FuzzyQuery, HasChildQuery, HasParentQuery, IdQuery, IntervalsQuery, MoreLikeThisQuery, NestedQuery, NoopQuery, ParentIdQuery, PercolateQuery, PinnedQuery, PrefixQuery, Query, QueryStringQuery, RangeQuery, RankFeatureQuery, RawQuery, RegexQuery, ScriptQuery, ScriptScoreQuery, SimpleStringQuery}
import com.sksamuel.elastic4s.requests.searches.span.{SpanContainingQuery, SpanFieldMaskingQuery, SpanFirstQuery, SpanMultiTermQuery, SpanNearQuery, SpanNotQuery, SpanOrQuery, SpanTermQuery, SpanWithinQuery}
import com.sksamuel.elastic4s.requests.searches.term.{TermQuery, TermsLookupQuery, TermsQuery, TermsSetQuery, WildcardQuery}
import com.sksamuel.elastic4s.requests.searches.queries.CustomQuery

object QueryBuilderFn {
def apply(q: Query): XContentBuilder = q match {
Expand Down Expand Up @@ -56,7 +55,7 @@ object QueryBuilderFn {
case q: RawQuery => RawQueryBodyFn(q)
case q: RegexQuery => RegexQueryBodyFn(q)
case q: ScriptQuery => ScriptQueryBodyFn(q)
case q: ScriptScore => ScriptScoreQueryBodyFn(q)
case q: ScriptScoreQuery => ScriptScoreQueryBodyFn(q)
case s: SimpleStringQuery => SimpleStringBodyFn(s)
case s: SpanContainingQuery => SpanContainingQueryBodyFn(s)
case s: SpanFirstQuery => SpanFirstQueryBodyFn(s)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.sksamuel.elastic4s.handlers.searches.queries

import com.sksamuel.elastic4s.handlers.script.ScriptBuilderFn
import com.sksamuel.elastic4s.json.{XContentBuilder, XContentFactory}
import com.sksamuel.elastic4s.requests.searches.queries.funcscorer.ScriptScore
import com.sksamuel.elastic4s.requests.searches.queries.ScriptScoreQuery

object ScriptScoreQueryBodyFn {

def apply(q: ScriptScore): XContentBuilder = {
def apply(q: ScriptScoreQuery): XContentBuilder = {
val builder = XContentFactory.jsonBuilder()
builder.rawField("script", q.script.script)
builder.startObject("script_score")
q.query.foreach(q => builder.rawField("query", QueryBuilderFn(q)))
q.script.foreach(s => builder.rawField("script", ScriptBuilderFn(s)))
q.boost.foreach(builder.field("boost", _))
q.minScore.foreach(builder.field("min_score", _))
builder.endObject()
builder
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.sksamuel.elastic4s.search.queries

import com.sksamuel.elastic4s.requests.script.Script
import com.sksamuel.elastic4s.testkit.DockerTests
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

import scala.util.Try

class ScriptScoreQueryTest extends AnyWordSpec with DockerTests with Matchers {

Try {
client.execute {
deleteIndex("person")
}.await
}

client.execute(
bulk(
indexInto("person") fields(
"name" -> "reese",
"age" -> 1.0
),
indexInto("person") fields(
"name" -> "finch",
"age" -> 1.0
),
indexInto("person") fields(
"name" -> "finch",
"age" -> 2.0
),
indexInto("person") fields(
"name" -> "finch",
"age" -> 3.0
)
).refreshImmediately
).await

"script score query" should {
"filter by query" in {
client.execute {
search("person") query {
scriptScoreQuery(termQuery("name", "finch"))
.script(Script("doc['age'].value % 3"))
}
}.await.result.totalHits shouldBe 3
}
"rank by custom score" in {
client.execute {
search("person") query {
scriptScoreQuery(termQuery("name", "finch"))
.script(Script("doc['age'].value % 3"))
}
}.await.result.hits.hits.map(_.sourceAsString) shouldBe Array(
"""{"name":"finch","age":2.0}""",
"""{"name":"finch","age":1.0}""",
"""{"name":"finch","age":3.0}"""
)
}
"return correct custom score" in {
client.execute {
search("person") query {
scriptScoreQuery(termQuery("name", "finch"))
.script(Script("doc['age'].value % 3"))
}
}.await.result.hits.hits.map(_.score) shouldBe Array(2.0, 1.0, 0.0)
}
}
}

0 comments on commit 962c515

Please sign in to comment.