Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(dsl): Support Function Score Query #224

Merged
merged 18 commits into from
May 30, 2023
Merged
189 changes: 189 additions & 0 deletions docs/overview/queries/elastic_query_function_score.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
id: elastic_query_function_score
title: "Function Score Query"
---

The `FunctionScore` allows you to modify the score of documents that are retrieved by a query.

In order to use the `FunctionScore` query and create needed `FunctionScoreFunction` import the following:
```scala
import zio.elasticsearch.query.FunctionScoreQuery
import zio.elasticsearch.query.FunctionScoreFunction._
import zio.elasticsearch.ElasticQuery._
```

For creating `FunctionScore` query you require `FunctionScoreFunction` or multiple of them.
You can create these functions in following way.

<br/>

You can create `DecayFunction` with `DecayFunctionType.Exp` using the `expDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Any] = expDecayFunction("field", origin = "11, 12", scale = "2km")
```
You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema)
`DecayFunction` with `DecayFunctionType.Exp` using the `expDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Document] = expDecayFunction(field = Document.field, origin = "11, 12", scale = "2km")
```

<br/>

You can create `DecayFunction` with `DecayFunctionType.Gauss` using the `gaussDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Any] = gaussDecayFunction(field = "field", origin = "11, 12", scale = "2km")
```
You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema)
`DecayFunction` with `DecayFunctionType.Gauss` using the `gaussDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Document] = gaussDecayFunction(field = Document.field, origin = "11, 12", scale = "2km")
```

<br/>

You can create `DecayFunction` with `DecayFunctionType.Linear` using the `linearDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Any] = linearDecayFunction(field = "field", origin = "11, 12", scale = "2km")
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema)
`DecayFunction` with `DecayFunctionType.Linear` using the `expDecayFunction` method with origin and scale in the following manner:
```scala
val function: DecayFunction[Document] = linearDecayFunction(field = Document.field, origin = "11, 12", scale = "2km")
```

<br/>

You can create `FieldValueFactor` using the `fieldValueFactor` method:

```scala
val function: FieldValueFactor[Any] = fieldValueFactor(field = "field")
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema)
`FieldValueFactor` using the `fieldValueFactor` method:

```scala
val function: FieldValueFactor[Document] = fieldValueFactor(field = Document.field)
```

<br/>

You can create `RandomScoreFunction` using the `randomScoreFunction` in three different ways depending on amount of parameters
you want to use:

```scala
val function: RandomScoreFunction[Any] = randomScoreFunction()

val function: RandomScoreFunction[Any] = randomScoreFunction(seed = 10)

val function: RandomScoreFunction[Any] = randomScoreFunction(seed = 10, field = "field")
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema)
`RandomScoreFunction` using the `randomScoreFunction` method:
```scala
val function: RandomScoreFunction = randomScoreFunction(seed = 10, field = Document.field)
```

<br/>

You can create `ScriptScoreFunction` using the `scriptScoreFunction` method with script in following manner:

```scala
val function: ScriptScoreFunction[Any] = scriptScoreFunction(script = Script("params.agg1 > 10"))
val function: ScriptScoreFunction[Any] = scriptScoreFunction(scriptSource = "params.agg1 > 10")
```

<br/>

You can create `WeightFunction` using the `weightFunction` method(you must provide `Any` type parameter when using):

```scala
val function: WeightFunction[Any] = scriptScoreFunction(weight = 10)
```

<br/><br/>

You can use these functions to create `FunctionScore` query using the `functionScore` method in the following manner:

```scala
val randomScoreFunction: RandomScoreFunction[Any] = randomScoreFunction()
val weightFunction: WeightFunction[Any] = scriptScoreFunction(weight = 10)
val query: FunctionScoreQuery[Any] = functionScore(randomScoreFunction, weightFunction)
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `FunctionScore` query
using the `functionScore`, if all functions are created type-safe, in the following manner:

```scala
val decayFunction: DecayFunction[Document] = expDecayFunction(field = Document.field, origin = "11, 12", scale = "2km")
val randomScoreFunction: RandomScoreFunction[Document] = randomScoreFunction(seed = 10, field = Document.field)
val weightFunction: WeightFunction[Any] = scriptScoreFunction(weight = 10)
val query: FunctionScoreQuery[Document] = functionScore(decayFunction, randomScoreFunction, weightFunction)
```

<br/>

If you want to change the `boost`, you can use `boost` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).boost(5.0)
```

<br/>

If you want to change the `boostMode`, you can use `boostMode` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).boostMode(FunctionScoreBoostMode.Max)
```

<br/>

If you want to change the `maxBoost`, you can use `maxBoost` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).maxBoost(5.0)
```

<br/>

If you want to change the `minScore`, you can use `minScore` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).minScore(5.0)
```

<br/>

If you want to change the `query`, you can use `query` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).query(matches(Document.field, "value"))
```

<br/>

If you want to change the `scoreMode`, you can use `scoreMode` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).scoreMode(FunctionScoreScoreMode.Max)
```

<br/>

If you want to add a one or multiple new `FunctionScoreFunction` you can use `withFunctions` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: FunctionScoreQuery[Document] = functionScore(randomScoreFunction(seed = 10, field = Document.field)).withFunctions(scriptScoreFunction(weight = 10))
```

You can find more information about `FunctionScore` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-function-score-query.html).
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import zio.elasticsearch.executor.response.{
MaxAggregationResponse,
TermsAggregationResponse
}
import zio.elasticsearch.query.FunctionScoreFunction.randomScoreFunction
import zio.elasticsearch.query.{FunctionScoreBoostMode, FunctionScoreFunction}
import zio.elasticsearch.query.sort.SortMode.Max
import zio.elasticsearch.query.sort.SortOrder._
import zio.elasticsearch.query.sort.SourceType.NumberType
Expand Down Expand Up @@ -1704,6 +1706,91 @@ object HttpExecutorSpec extends IntegrationSpec {
)
}
} @@ after(Executor.execute(ElasticRequest.deleteIndex(geoDistanceIndex)).orDie)
),
suite("search for documents using FunctionScore query")(
test("using randomScore function") {
checkOnce(genTestDocument, genTestDocument) { (firstDocument, secondDocument) =>
val secondDocumentUpdated = secondDocument.copy(stringField = firstDocument.stringField)
for {
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll))
_ <- Executor.execute(
ElasticRequest.create[TestDocument](firstSearchIndex, firstDocument).refreshTrue
)
_ <- Executor.execute(
ElasticRequest
.create[TestDocument](
firstSearchIndex,
secondDocumentUpdated
)
.refreshTrue
)
r1 <- Executor
.execute(
ElasticRequest.search(
firstSearchIndex,
ElasticQuery
.functionScore(randomScoreFunction())
.query(matches("stringField", firstDocument.stringField))
)
)
.documentAs[TestDocument]
} yield assert(r1)(
hasSameElements(Chunk(firstDocument, secondDocumentUpdated))
)
}
} @@ around(
Executor.execute(
ElasticRequest.createIndex(
firstSearchIndex,
"""{ "mappings": { "properties": { "subDocumentList": { "type": "nested" } } } }"""
)
),
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie
),
test("using randomScore function and weight function") {
checkOnce(genTestDocument, genTestDocument) { (firstDocument, secondDocument) =>
val secondDocumentUpdated = secondDocument.copy(stringField = firstDocument.stringField)
for {
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll))
_ <- Executor.execute(
ElasticRequest.create[TestDocument](firstSearchIndex, firstDocument).refreshTrue
)
_ <- Executor.execute(
ElasticRequest
.create[TestDocument](
firstSearchIndex,
secondDocumentUpdated
)
.refreshTrue
)
r1 <- Executor
.execute(
ElasticRequest.search(
firstSearchIndex,
ElasticQuery
.functionScore(
FunctionScoreFunction.randomScoreFunction(),
FunctionScoreFunction.weightFunction(2)
)
.query(matches("stringField", firstDocument.stringField))
.boost(2.0)
.boostMode(FunctionScoreBoostMode.Max)
)
)
.documentAs[TestDocument]
} yield assert(r1)(
hasSameElements(Chunk(firstDocument, secondDocumentUpdated))
)
}
} @@ around(
Executor.execute(
ElasticRequest.createIndex(
firstSearchIndex,
"""{ "mappings": { "properties": { "subDocumentList": { "type": "nested" } } } }"""
)
),
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie
)
)
) @@ nondeterministic @@ sequential @@ prepareElasticsearchIndexForTests @@ afterAll(
Executor.execute(ElasticRequest.deleteIndex(index)).orDie
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ object ElasticQuery {
Exists(field = field, boost = None)

/**
* Constructs an instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must satisfy the criteria using
* the specified parameters.
* Constructs a type-safe instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must satisfy the
* criteria using the specified parameters.
*
* @param queries
* a list of queries to add to `filter` inside of the `Bool` query
Expand Down Expand Up @@ -124,6 +124,54 @@ object ElasticQuery {
minimumShouldMatch = None
)

/**
* Constructs a type-safe instance of [[zio.elasticsearch.query.FunctionScore]] query with one or multiple
* [[zio.elasticsearch.query.FunctionScoreFunction]].
*
* @param functions
* [[zio.elasticsearch.query.FunctionScoreFunction]] functions that will be part of
* [[zio.elasticsearch.query.FunctionScore]] query
* @return
* an instance of [[zio.elasticsearch.query.FunctionScore]] that represents the Function Score Query with functions
* that are used to calculate score for result.
*/
final def functionScore[S: Schema](
functions: FunctionScoreFunction[S]*
): FunctionScoreQuery[S] =
FunctionScore[S](
functionScoreFunctions = Chunk.fromIterable(functions),
boost = None,
boostMode = None,
maxBoost = None,
minScore = None,
query = None,
scoreMode = None
)

/**
* Constructs an instance of [[zio.elasticsearch.query.FunctionScore]] query with one or multiple
* [[zio.elasticsearch.query.FunctionScoreFunction]].
*
* @param functions
* [[zio.elasticsearch.query.FunctionScoreFunction]] functions that will be part of
* [[zio.elasticsearch.query.FunctionScore]] query
* @return
* an instance of [[zio.elasticsearch.query.FunctionScore]] that represents the Function Score Query with functions
* that are used to calculate score for result.
*/
final def functionScore(
functions: FunctionScoreFunction[Any]*
): FunctionScoreQuery[Any] =
FunctionScore[Any](
functionScoreFunctions = Chunk.fromIterable(functions),
boost = None,
boostMode = None,
maxBoost = None,
minScore = None,
query = None,
scoreMode = None
)

/**
* Constructs a type-safe instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
Expand Down Expand Up @@ -381,8 +429,8 @@ object ElasticQuery {
MatchPhrase(field = field, value = value, boost = None)

/**
* Constructs an instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must satisfy the criteria using
* the specified parameters.
* Constructs a type-safe instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must satisfy the
* criteria using the specified parameters.
*
* @param queries
* a list of queries to add to `must` inside of the `Bool` query
Expand Down Expand Up @@ -423,8 +471,8 @@ object ElasticQuery {
)

/**
* Constructs an instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must not satisfy the criteria
* using the specified parameters.
* Constructs a type-safe instance of [[zio.elasticsearch.query.BoolQuery]] with queries that must not satisfy the
* criteria using the specified parameters.
*
* @param queries
* a list of queries to add to `mustNot` inside of the `Bool` query
Expand Down
Loading