From 3af01baacc4e59c1d64ec0fefde17fd077a90b24 Mon Sep 17 00:00:00 2001 From: Vasilije Milic Date: Mon, 21 Aug 2023 15:08:33 +0200 Subject: [PATCH 1/3] Implement constant score query --- .../queries/elastic_query_constant_score.md | 30 +++++++++ .../zio/elasticsearch/HttpExecutorSpec.scala | 26 ++++++++ .../zio/elasticsearch/ElasticQuery.scala | 30 +++++++++ .../zio/elasticsearch/query/Queries.scala | 15 +++++ .../zio/elasticsearch/ElasticQuerySpec.scala | 65 +++++++++++++++++++ website/sidebars.js | 1 + 6 files changed, 167 insertions(+) create mode 100644 docs/overview/queries/elastic_query_constant_score.md diff --git a/docs/overview/queries/elastic_query_constant_score.md b/docs/overview/queries/elastic_query_constant_score.md new file mode 100644 index 000000000..51f054aae --- /dev/null +++ b/docs/overview/queries/elastic_query_constant_score.md @@ -0,0 +1,30 @@ +--- +id: elastic_query_constant_score +title: "Constant Score Query" +--- + +The `ConstantScore` query wraps a filter query and returns every matching document with a relevance score equal to the boost parameter value. + +In order to use the `ConstantScore` query import the following: +```scala +import zio.elasticsearch.query.ConstantScoreQuery +import zio.elasticsearch.ElasticQuery._ +``` + +You can create a `ConstantScore` query with arbitrary query(`MatchPhrase` in this example) using the `constantScore` method in the following manner: +```scala +val query: ConstantScoreQuery = constantScore(matchPhrase(field = "name", value = "test")) +``` + +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `ConstantScore` query with arbitrary query(`MatchPhrase` in this example) using the `constantScore` method in the following manner: +```scala +val query: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value ="test")) +``` + +If you want to change the `boost`, you can use `boost` method: +```scala +val queryWithBoost: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value ="test")).boost(2.2) +``` + +You can find more information about `ConstantScore` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-constant-score-query.html). + diff --git a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala index fb581911f..1ed2e263c 100644 --- a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala +++ b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala @@ -674,6 +674,32 @@ object HttpExecutorSpec extends IntegrationSpec { ) ), suite("searching for documents")( + test("search for a document using a constant score query") { + checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { + (firstDocumentId, firstDocument, secondDocumentId, secondDocument) => + for { + _ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll)) + document = firstDocument.copy(stringField = "this is a test") + _ <- + Executor.execute(ElasticRequest.upsert[TestDocument](firstSearchIndex, firstDocumentId, document)) + _ <- Executor.execute( + ElasticRequest + .upsert[TestDocument](firstSearchIndex, secondDocumentId, secondDocument) + .refreshTrue + ) + query = constantScore( + matchPhrase( + field = TestDocument.stringField, + value = "test" + ) + ).boost(2.1) + res <- Executor.execute(ElasticRequest.search(firstSearchIndex, query)).documentAs[TestDocument] + } yield assert(res)(Assertion.contains(document)) + } + } @@ around( + Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), + Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie + ), test("search for first 2 documents using range query") { checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument, genDocumentId, genTestDocument) { (firstDocumentId, firstDocument, secondDocumentId, secondDocument, thirdDocumentId, thirdDocument) => diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index f12cde6c6..074807133 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -25,6 +25,36 @@ import zio.schema.Schema object ElasticQuery { + /** + * Constructs a type-safe instance of [[zio.elasticsearch.query.ConstantScoreQuery]] with a specified query. + * [[zio.elasticsearch.query.ConstantScoreQuery]] wraps a filter query and returns every matching document with a + * relevance score equal to the boost parameter value. + * + * @param query + * query you wish to run + * @tparam S + * document for which field query is executed. An implicit `Schema` instance must be in scope + * @return + * an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] that represents the constant score query with query + * that must satisfy the criteria. + */ + final def constantScore[S: Schema](query: ElasticQuery[S]): ConstantScoreQuery[S] = + ConstantScore[S](query, boost = None) + + /** + * Constructs an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] with a specified query. + * [[zio.elasticsearch.query.ConstantScoreQuery]] wraps a filter query and returns every matching document with a + * relevance score equal to the boost parameter value. + * + * @param query + * query you wish to run + * @return + * an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] that represents the constant score query with query + * that must satisfy the criteria. + */ + final def constantScore(query: ElasticQuery[Any]): ConstantScoreQuery[Any] = + ConstantScore[Any](query, boost = None) + /** * Constructs a type-safe instance of [[zio.elasticsearch.query.WildcardQuery]] using the specified parameters. * [[zio.elasticsearch.query.WildcardQuery]] is used for matching documents containing a value that contains the diff --git a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala index 76a3e89a4..0a85b1a0e 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -182,6 +182,21 @@ private[elasticsearch] final case class Bool[S]( } } +sealed trait ConstantScoreQuery[S] extends ElasticQuery[S] with HasBoost[ConstantScoreQuery[S]] + +private[elasticsearch] final case class ConstantScore[S](query: ElasticQuery[S], boost: Option[Double]) + extends ConstantScoreQuery[S] { self => + def boost(value: Double): ConstantScoreQuery[S] = + self.copy(boost = Some(value)) + + private[elasticsearch] def toJson(fieldPath: Option[String]): Json = + Obj( + "constant_score" -> (Obj("filter" -> query.toJson(fieldPath)) merge boost.fold(Obj())(b => + Obj("boost" -> b.toJson) + )) + ) +} + sealed trait ExistsQuery[S] extends ElasticQuery[S] with HasBoost[ExistsQuery[S]] private[elasticsearch] final case class Exists[S](field: String, boost: Option[Double]) extends ExistsQuery[S] { self => diff --git a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala index 422571f7f..d88119766 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -333,6 +333,36 @@ object ElasticQuerySpec extends ZIOSpecDefault { ) } ), + test("constantScore") { + val query = constantScore(terms("stringField", "a", "b", "c")) + val queryTs = constantScore(terms(TestDocument.stringField, "a", "b", "c")) + val queryWithBoost = constantScore(terms(TestDocument.stringField, "a", "b", "c")).boost(2.2) + + assert(query)( + equalTo( + ConstantScore[Any]( + Terms(field = "stringField", values = Chunk("a", "b", "c"), boost = None), + boost = None + ) + ) + ) && + assert(queryTs)( + equalTo( + ConstantScore[TestDocument]( + Terms(field = "stringField", values = Chunk("a", "b", "c"), boost = None), + boost = None + ) + ) + ) && + assert(queryWithBoost)( + equalTo( + ConstantScore[TestDocument]( + Terms(field = "stringField", values = Chunk("a", "b", "c"), boost = None), + boost = Some(2.2) + ) + ) + ) + }, test("contains") { val query = contains("testField", "test") val queryTs = contains(TestDocument.stringField, "test") @@ -1873,6 +1903,41 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryWithAllParams.toJson(fieldPath = None))(equalTo(expectedWithAllParams.toJson)) } ), + test("constantScore") { + val query = constantScore(matchPhrase("stringField", value = "test")) + val queryTs = constantScore(matchPhrase(TestDocument.stringField, value = "test")) + val queryWithBoost = constantScore(matchPhrase(TestDocument.stringField, value = "test")).boost(1.5) + + val expected = + """ + |{ + | "constant_score": { + | "filter": { + | "match_phrase": { + | "stringField": "test" + | } + | } + | } + |} + |""".stripMargin + val expectedWithBoost = + """ + |{ + | "constant_score": { + | "filter": { + | "match_phrase": { + | "stringField": "test" + | } + | }, + | "boost": 1.5 + | } + |} + |""".stripMargin + + assert(query.toJson(fieldPath = None))(equalTo(expected.toJson)) && + assert(queryTs.toJson(fieldPath = None))(equalTo(expected.toJson)) && + assert(queryWithBoost.toJson(fieldPath = None))(equalTo(expectedWithBoost.toJson)) + }, test("contains") { val query = contains(TestDocument.stringField, "test") val queryWithBoost = contains(TestDocument.stringField, "test").boost(3.14) diff --git a/website/sidebars.js b/website/sidebars.js index 6f2f58d93..778b8f51a 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -15,6 +15,7 @@ module.exports = { items: [ 'overview/elastic_query', 'overview/queries/elastic_query_bool', + 'overview/queries/elastic_query_constant_score', 'overview/queries/elastic_query_exists', 'overview/queries/elastic_query_function_score', 'overview/queries/elastic_query_geo_distance', From faf9a507d7a4d4b58527000ec9e256a419e75edb Mon Sep 17 00:00:00 2001 From: Vasilije Milic Date: Thu, 24 Aug 2023 10:57:27 +0200 Subject: [PATCH 2/3] Fix code remarks --- .../queries/elastic_query_constant_score.md | 6 +++--- .../scala/zio/elasticsearch/HttpExecutorSpec.scala | 2 +- .../scala/zio/elasticsearch/ElasticQuery.scala | 14 +++++++------- .../scala/zio/elasticsearch/query/Queries.scala | 1 + .../scala/zio/elasticsearch/ElasticQuerySpec.scala | 6 +++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/overview/queries/elastic_query_constant_score.md b/docs/overview/queries/elastic_query_constant_score.md index 51f054aae..41a37ece5 100644 --- a/docs/overview/queries/elastic_query_constant_score.md +++ b/docs/overview/queries/elastic_query_constant_score.md @@ -16,14 +16,14 @@ You can create a `ConstantScore` query with arbitrary query(`MatchPhrase` in thi val query: ConstantScoreQuery = constantScore(matchPhrase(field = "name", value = "test")) ``` -You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `ConstantScore` query with arbitrary query(`MatchPhrase` in this example) using the `constantScore` method in the following manner: +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `ConstantScore` query with arbitrary [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) query(`MatchPhrase` in this example) using the `constantScore` method in the following manner: ```scala -val query: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value ="test")) +val query: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value = "test")) ``` If you want to change the `boost`, you can use `boost` method: ```scala -val queryWithBoost: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value ="test")).boost(2.2) +val queryWithBoost: ConstantScoreQuery = constantScore(matchPhrase(field = Document.name, value = "test")).boost(2.2) ``` You can find more information about `ConstantScore` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-constant-score-query.html). diff --git a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala index 1ed2e263c..fe5ac2409 100644 --- a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala +++ b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala @@ -694,7 +694,7 @@ object HttpExecutorSpec extends IntegrationSpec { ) ).boost(2.1) res <- Executor.execute(ElasticRequest.search(firstSearchIndex, query)).documentAs[TestDocument] - } yield assert(res)(Assertion.contains(document)) + } yield (assert(res)(Assertion.contains(document)) && assert(res)(!Assertion.contains(secondDocument))) } } @@ around( Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index 074807133..9900056fa 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -31,15 +31,15 @@ object ElasticQuery { * relevance score equal to the boost parameter value. * * @param query - * query you wish to run + * query to be wrapped inside of constant score query * @tparam S - * document for which field query is executed. An implicit `Schema` instance must be in scope + * document for which field query is specified for. An implicit `Schema` instance must be in scope * @return * an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] that represents the constant score query with query - * that must satisfy the criteria. + * that must satisfy the criteria to be performed. */ final def constantScore[S: Schema](query: ElasticQuery[S]): ConstantScoreQuery[S] = - ConstantScore[S](query, boost = None) + ConstantScore[S](query = query, boost = None) /** * Constructs an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] with a specified query. @@ -47,13 +47,13 @@ object ElasticQuery { * relevance score equal to the boost parameter value. * * @param query - * query you wish to run + * query to be wrapped inside of constant score query * @return * an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] that represents the constant score query with query - * that must satisfy the criteria. + * that must satisfy the criteria to be performed. */ final def constantScore(query: ElasticQuery[Any]): ConstantScoreQuery[Any] = - ConstantScore[Any](query, boost = None) + ConstantScore[Any](query = query, boost = None) /** * Constructs a type-safe instance of [[zio.elasticsearch.query.WildcardQuery]] using the specified parameters. diff --git a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala index 0a85b1a0e..70a296b82 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -186,6 +186,7 @@ sealed trait ConstantScoreQuery[S] extends ElasticQuery[S] with HasBoost[Constan private[elasticsearch] final case class ConstantScore[S](query: ElasticQuery[S], boost: Option[Double]) extends ConstantScoreQuery[S] { self => + def boost(value: Double): ConstantScoreQuery[S] = self.copy(boost = Some(value)) diff --git a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala index d88119766..9c726ca34 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -1904,9 +1904,9 @@ object ElasticQuerySpec extends ZIOSpecDefault { } ), test("constantScore") { - val query = constantScore(matchPhrase("stringField", value = "test")) - val queryTs = constantScore(matchPhrase(TestDocument.stringField, value = "test")) - val queryWithBoost = constantScore(matchPhrase(TestDocument.stringField, value = "test")).boost(1.5) + val query = constantScore(matchPhrase("stringField", "test")) + val queryTs = constantScore(matchPhrase(TestDocument.stringField, "test")) + val queryWithBoost = constantScore(matchPhrase(TestDocument.stringField, "test")).boost(1.5) val expected = """ From ebba966c712a5536d6b50122b4f94ab350c85488 Mon Sep 17 00:00:00 2001 From: Vasilije Milic Date: Mon, 28 Aug 2023 17:17:13 +0200 Subject: [PATCH 3/3] Fix scala doc --- .../library/src/main/scala/zio/elasticsearch/ElasticQuery.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index 9900056fa..d8cef601b 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -33,7 +33,7 @@ object ElasticQuery { * @param query * query to be wrapped inside of constant score query * @tparam S - * document for which field query is specified for. An implicit `Schema` instance must be in scope + * document for which field query is specified for. An implicit `Schema` instance must be provided in the scope * @return * an instance of [[zio.elasticsearch.query.ConstantScoreQuery]] that represents the constant score query with query * that must satisfy the criteria to be performed.