From f796a8ce37516f2b17c9a191ba5307ab62e94403 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Fri, 10 Nov 2023 11:19:34 +0100 Subject: [PATCH 1/7] Support Disjunction max query --- .../queries/elastic_query_disjunction_max.md | 25 +++++++ .../zio/elasticsearch/HttpExecutorSpec.scala | 40 ++++++++++++ .../zio/elasticsearch/ElasticPrimitive.scala | 4 ++ .../zio/elasticsearch/ElasticQuery.scala | 18 ++++- .../zio/elasticsearch/query/Queries.scala | 65 +++++++++++++++++++ .../zio/elasticsearch/ElasticQuerySpec.scala | 56 ++++++++++++++++ website/sidebars.js | 1 + 7 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 docs/overview/queries/elastic_query_disjunction_max.md diff --git a/docs/overview/queries/elastic_query_disjunction_max.md b/docs/overview/queries/elastic_query_disjunction_max.md new file mode 100644 index 000000000..144f8e620 --- /dev/null +++ b/docs/overview/queries/elastic_query_disjunction_max.md @@ -0,0 +1,25 @@ +--- +id: elastic_query_disjunction_max +title: "Disjunction max Query" +--- + +The `Disjunction max` query returns documents that match one or more query clauses. For documents that match multiple query clauses, the relevance score is set to the highest relevance score from all matching query clauses. When the relevance scores of the returned documents are identical, tie breaker parameter gives more weight to documents that match multiple query clauses. + +In order to use the `Disjunction max` query import the following: +```scala +import zio.elasticsearch.query.DisjunctionMax +import zio.elasticsearch.ElasticQuery.disjunctionMax +``` + +You can create a `Disjunction max` query using the `disjunctionMax` method this way: +```scala +val query: DisjunctionMaxQuery = disjunctionMax( queries = Chunk( term( field = "stringField", value = "test"), exists( field = "intField"))) +``` + +If you want to change the `tieBreaker`, you can use `tieBreaker` method: +```scala +val queryWithPrefixLength: DisjunctionMaxQuery = disjunctionMax( queries = Chunk( queries = Chunk( exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f)) +``` + +You can find more information about `Disjunction max` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-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 16931dd7c..80032cd7e 100644 --- a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala +++ b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala @@ -1161,6 +1161,46 @@ object HttpExecutorSpec extends IntegrationSpec { Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie ), + test("search for a document using a disjunction max query ttt") { + checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { + (firstDocumentId, firstDocument, secondDocumentId, secondDocument) => + for { + _ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll)) + firstDocumentUpdated = + firstDocument.copy(stringField = s"this is a ${firstDocument.stringField} test.") + secondDocumentUpdated = + secondDocument.copy(stringField = + s"this is not a ${firstDocument.stringField} test. It is a ${secondDocument.stringField} test, but not ${firstDocument.stringField}" + ) + _ <- Executor.execute( + ElasticRequest + .upsert[TestDocument](firstSearchIndex, firstDocumentId, firstDocumentUpdated) + ) + _ <- + Executor.execute( + ElasticRequest + .upsert[TestDocument](firstSearchIndex, secondDocumentId, secondDocumentUpdated) + .refreshTrue + ) + query = disjunctionMax( + Chunk( + term( + field = TestDocument.stringField, + value = firstDocument.stringField.toLowerCase + ), + matchPhrase( + field = TestDocument.stringField, + value = secondDocument.stringField + ) + ) + ) + res <- Executor.execute(ElasticRequest.search(firstSearchIndex, query)).documentAs[TestDocument] + } yield assert(res)(equalTo(Chunk(secondDocumentUpdated, firstDocumentUpdated))) + } + } @@ around( + Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), + Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie + ), test("search for a document using a fuzzy query") { checkOnce(genDocumentId, genTestDocument) { (firstDocumentId, firstDocument) => for { diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticPrimitive.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticPrimitive.scala index 65a73b197..5b1c22e4d 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticPrimitive.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticPrimitive.scala @@ -39,6 +39,10 @@ object ElasticPrimitive { def toJson(value: Double): Json = Num(value) } + implicit object ElasticFloat extends ElasticPrimitive[Float] { + def toJson(value: Float): Json = Num(value) + } + implicit object ElasticInt extends ElasticPrimitive[Int] { def toJson(value: Int): Json = Num(value) } diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index a5c022b65..5837bf746 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -87,6 +87,20 @@ object ElasticQuery { final def contains(field: String, value: String): WildcardQuery[Any] = Wildcard(field = field, value = s"*$value*", boost = None, caseInsensitive = None) + /** + * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. + * + * @param queries + * the type-safe [[ElasticQuery]] object to be wrapped inside of disjunction max query + * @tparam S + * document for which field query is executed + * @return + * an instance of [[zio.elasticsearch.query.DisjunctionMax]] that represents the `disjunction max` query to be + * performed. + */ + final def disjunctionMax[S](queries: Chunk[ElasticQuery[S]]): DisjunctionMaxQuery[S] = + DisjunctionMax(queries = queries, tieBreaker = None) + /** * Constructs a type-safe instance of [[zio.elasticsearch.query.ExistsQuery]], that checks existence of the field, * using the specified parameters. @@ -215,7 +229,7 @@ object ElasticQuery { * @tparam S * document for which field query is executed * @return - * an instance of [[zio.elasticsearch.query.FuzzyQuery]] that represents the fuzzy query to be performed. + * an instance of [[zio.elasticsearch.query.FuzzyQuery]] that represents the `fuzzy` query to be performed. */ final def fuzzy[S](field: Field[S, String], value: String): FuzzyQuery[S] = Fuzzy(field = field.toString, value = value, fuzziness = None, maxExpansions = None, prefixLength = None) @@ -230,7 +244,7 @@ object ElasticQuery { * @param value * text value that will be used for the query * @return - * an instance of [[zio.elasticsearch.query.FuzzyQuery]] that represents the fuzzy query to be performed. + * an instance of [[zio.elasticsearch.query.FuzzyQuery]] that represents the `fuzzy` query to be performed. */ final def fuzzy(field: String, value: String): FuzzyQuery[Any] = Fuzzy(field = field, value = value, fuzziness = None, maxExpansions = None, prefixLength = None) 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 52f8cf94a..a0b92699b 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -200,6 +200,71 @@ private[elasticsearch] final case class ConstantScore[S](query: ElasticQuery[S], ) } +sealed trait DisjunctionMaxQuery[S] extends ElasticQuery[S] { + + /** + * Adds specified queries to the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. Returned documents must match one or + * more of these queries. + * + * @param allQueries + * the queries to be added + * @tparam S1 + * the type of the sub-queries, for which an implicit [[zio.schema.Schema]] is required + * @return + * an instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with queries added. + */ + def queries[S1 <: S: Schema](allQueries: ElasticQuery[S1]*): DisjunctionMaxQuery[S1] + + /** + * Adds specified queries to the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. Returned documents must match one or + * more of these queries. + * + * @param allQueries + * the queries to be added + * @return + * an instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with queries added. + */ + def queries(allQueries: ElasticQuery[Any]*): DisjunctionMaxQuery[S] + + /** + * Sets the `tieBreaker` parameter for the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. The `tieBreaker` value is + * a floating-point factor between 0 and 1.0 that is used to give more weight to documents that match multiple query + * clauses. Default is 0 (which means only the highest score counts). + * + * @param value + * a number to set `tieBreaker` parameter to + * @return + * a new instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with the `tieBreaker` value set. + */ + def tieBreaker(value: Float): DisjunctionMaxQuery[S] +} + +private[elasticsearch] final case class DisjunctionMax[S]( + queries: Chunk[ElasticQuery[S]], + tieBreaker: Option[Float] +) extends DisjunctionMaxQuery[S] { self => + + def tieBreaker(value: Float): DisjunctionMaxQuery[S] = + self.copy(tieBreaker = Some(value)) + + def queries[S1 <: S: Schema](allQueries: ElasticQuery[S1]*): DisjunctionMaxQuery[S1] = + self.copy(queries = queries ++ allQueries) + + def queries(allQueries: ElasticQuery[Any]*): DisjunctionMaxQuery[S] = + self.copy(queries = queries ++ allQueries) + + private[elasticsearch] def toJson(fieldPath: Option[String]): Json = { + val disMaxFields = + Chunk( + Some("queries" -> Arr(queries.map(_.toJson(fieldPath)))), + tieBreaker.map("tie_breaker" -> _.toJson) + ).collect { case Some(obj) => obj } + + Obj("dis_max" -> Obj(disMaxFields)) + + } +} + 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 74682864a..373198dc7 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -416,6 +416,29 @@ object ElasticQuerySpec extends ZIOSpecDefault { ) ) }, + test("disjunctionMax") { + val query = disjunctionMax(Chunk(exists("existsField"), ids("1", "2", "3"))) + val queryWithTieBreaker = disjunctionMax(Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) + + assert(query)( + equalTo( + DisjunctionMax[Any]( + queries = + Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), + tieBreaker = None + ) + ) + ) && + assert(queryWithTieBreaker)( + equalTo( + DisjunctionMax[Any]( + queries = + Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), + tieBreaker = Some(0.5f) + ) + ) + ) + }, test("exists") { val query = exists("testField") val queryTs = exists(TestDocument.intField) @@ -2507,6 +2530,39 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryWithCaseInsensitive.toJson(fieldPath = None))(equalTo(expectedWithCaseInsensitive.toJson)) && assert(queryWithAllParams.toJson(fieldPath = None))(equalTo(expectedWithAllParams.toJson)) }, + test("disjunctionMax") { + val query = disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))) + val queryWithTieBreaker = + disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) + + val expected = + """ + |{ + | "dis_max": { + | "queries": [ + | { "exists": { "field": "existsField" } }, + | { "ids": { "values": ["1", "2", "3"] } } + | ] + | } + |} + |""".stripMargin + + val expectedWithTieBreaker = + """ + |{ + | "dis_max": { + | "queries": [ + | { "exists": { "field": "existsField" } }, + | { "ids": { "values": ["1", "2", "3"] } } + | ], + | "tie_breaker": 0.5 + | } + |} + |""".stripMargin + + assert(query.toJson(fieldPath = None))(equalTo(expected.toJson)) && + assert(queryWithTieBreaker.toJson(fieldPath = None))(equalTo(expectedWithTieBreaker.toJson)) + }, test("exists") { val query = exists("testField") val queryTs = exists(TestDocument.dateField) diff --git a/website/sidebars.js b/website/sidebars.js index 318505b5b..5f9d1cb9e 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -16,6 +16,7 @@ module.exports = { 'overview/elastic_query', 'overview/queries/elastic_query_bool', 'overview/queries/elastic_query_constant_score', + 'overview/queries/elastic_query_disjunction_max', 'overview/queries/elastic_query_exists', 'overview/queries/elastic_query_function_score', 'overview/queries/elastic_query_fuzzy', From f14b4b19b20fa154aa38b6554f437ac47c2f6d37 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Mon, 13 Nov 2023 10:48:35 +0100 Subject: [PATCH 2/7] Fix code remarks --- .../queries/elastic_query_disjunction_max.md | 6 ++-- .../zio/elasticsearch/HttpExecutorSpec.scala | 6 ++-- .../zio/elasticsearch/ElasticQuery.scala | 22 ++++++++++--- .../zio/elasticsearch/query/Queries.scala | 32 +------------------ 4 files changed, 24 insertions(+), 42 deletions(-) diff --git a/docs/overview/queries/elastic_query_disjunction_max.md b/docs/overview/queries/elastic_query_disjunction_max.md index 144f8e620..17fe13f9c 100644 --- a/docs/overview/queries/elastic_query_disjunction_max.md +++ b/docs/overview/queries/elastic_query_disjunction_max.md @@ -3,7 +3,7 @@ id: elastic_query_disjunction_max title: "Disjunction max Query" --- -The `Disjunction max` query returns documents that match one or more query clauses. For documents that match multiple query clauses, the relevance score is set to the highest relevance score from all matching query clauses. When the relevance scores of the returned documents are identical, tie breaker parameter gives more weight to documents that match multiple query clauses. +The `Disjunction max` query returns documents that match one or more query clauses. For documents that match multiple query clauses, the relevance score is set to the highest relevance score from all matching query clauses. When the relevance scores of the returned documents are identical, tie breaker parameter can be used for giving more weight to documents that match multiple query clauses. In order to use the `Disjunction max` query import the following: ```scala @@ -13,12 +13,12 @@ import zio.elasticsearch.ElasticQuery.disjunctionMax You can create a `Disjunction max` query using the `disjunctionMax` method this way: ```scala -val query: DisjunctionMaxQuery = disjunctionMax( queries = Chunk( term( field = "stringField", value = "test"), exists( field = "intField"))) +val query: DisjunctionMaxQuery = disjunctionMax(queries = Chunk(term(field = "stringField", value = "test"), exists( field = "intField"))) ``` If you want to change the `tieBreaker`, you can use `tieBreaker` method: ```scala -val queryWithPrefixLength: DisjunctionMaxQuery = disjunctionMax( queries = Chunk( queries = Chunk( exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f)) +val queryWithPrefixLength: DisjunctionMaxQuery = disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) ``` You can find more information about `Disjunction max` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-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 80032cd7e..1edf1b395 100644 --- a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala +++ b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala @@ -1161,16 +1161,16 @@ object HttpExecutorSpec extends IntegrationSpec { Executor.execute(ElasticRequest.createIndex(firstSearchIndex)), Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie ), - test("search for a document using a disjunction max query ttt") { + test("search for a document using a disjunction max query") { checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) { (firstDocumentId, firstDocument, secondDocumentId, secondDocument) => for { _ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll)) firstDocumentUpdated = - firstDocument.copy(stringField = s"this is a ${firstDocument.stringField} test.") + firstDocument.copy(stringField = s"This is a ${firstDocument.stringField} test.") secondDocumentUpdated = secondDocument.copy(stringField = - s"this is not a ${firstDocument.stringField} test. It is a ${secondDocument.stringField} test, but not ${firstDocument.stringField}" + s"This is a ${secondDocument.stringField} test. It should be in the list before ${firstDocument.stringField}, because it has higher relevance score than ${firstDocument.stringField}" ) _ <- Executor.execute( ElasticRequest diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index 5837bf746..d6cbb0635 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -88,18 +88,30 @@ object ElasticQuery { Wildcard(field = field, value = s"*$value*", boost = None, caseInsensitive = None) /** - * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. + * Constructs a type-safe instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. * * @param queries - * the type-safe [[ElasticQuery]] object to be wrapped inside of disjunction max query + * the type-safe [[ElasticQuery]] objects to be wrapped inside of disjunction max query * @tparam S - * document for which field query is executed + * document for which field query is executed. An implicit `Schema` instance must be in scope + * @return + * an instance of [[zio.elasticsearch.query.DisjunctionMax]] that represents the `disjunction max` query to be + * performed. + */ + final def disjunctionMax[S: Schema](queries: Chunk[ElasticQuery[S]]): DisjunctionMaxQuery[S] = + DisjunctionMax[S](queries = queries, tieBreaker = None) + + /** + * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. + * + * @param queries + * the [[ElasticQuery]] objects to be wrapped inside of disjunction max query * @return * an instance of [[zio.elasticsearch.query.DisjunctionMax]] that represents the `disjunction max` query to be * performed. */ - final def disjunctionMax[S](queries: Chunk[ElasticQuery[S]]): DisjunctionMaxQuery[S] = - DisjunctionMax(queries = queries, tieBreaker = None) + final def disjunctionMax(queries: Chunk[ElasticQuery[Any]]): DisjunctionMaxQuery[Any] = + DisjunctionMax[Any](queries = queries, tieBreaker = None) /** * Constructs a type-safe instance of [[zio.elasticsearch.query.ExistsQuery]], that checks existence of the field, 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 a0b92699b..6c1ae64fa 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -202,30 +202,6 @@ private[elasticsearch] final case class ConstantScore[S](query: ElasticQuery[S], sealed trait DisjunctionMaxQuery[S] extends ElasticQuery[S] { - /** - * Adds specified queries to the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. Returned documents must match one or - * more of these queries. - * - * @param allQueries - * the queries to be added - * @tparam S1 - * the type of the sub-queries, for which an implicit [[zio.schema.Schema]] is required - * @return - * an instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with queries added. - */ - def queries[S1 <: S: Schema](allQueries: ElasticQuery[S1]*): DisjunctionMaxQuery[S1] - - /** - * Adds specified queries to the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. Returned documents must match one or - * more of these queries. - * - * @param allQueries - * the queries to be added - * @return - * an instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with queries added. - */ - def queries(allQueries: ElasticQuery[Any]*): DisjunctionMaxQuery[S] - /** * Sets the `tieBreaker` parameter for the [[zio.elasticsearch.query.DisjunctionMaxQuery]]. The `tieBreaker` value is * a floating-point factor between 0 and 1.0 that is used to give more weight to documents that match multiple query @@ -234,7 +210,7 @@ sealed trait DisjunctionMaxQuery[S] extends ElasticQuery[S] { * @param value * a number to set `tieBreaker` parameter to * @return - * a new instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] with the `tieBreaker` value set. + * an instance of the [[zio.elasticsearch.query.DisjunctionMaxQuery]] enriched with the `tieBreaker` parameter. */ def tieBreaker(value: Float): DisjunctionMaxQuery[S] } @@ -247,12 +223,6 @@ private[elasticsearch] final case class DisjunctionMax[S]( def tieBreaker(value: Float): DisjunctionMaxQuery[S] = self.copy(tieBreaker = Some(value)) - def queries[S1 <: S: Schema](allQueries: ElasticQuery[S1]*): DisjunctionMaxQuery[S1] = - self.copy(queries = queries ++ allQueries) - - def queries(allQueries: ElasticQuery[Any]*): DisjunctionMaxQuery[S] = - self.copy(queries = queries ++ allQueries) - private[elasticsearch] def toJson(fieldPath: Option[String]): Json = { val disMaxFields = Chunk( From 3bb20624aa29cd29b9dea8af11888c987d94a0e7 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Tue, 14 Nov 2023 17:02:22 +0100 Subject: [PATCH 3/7] Fix code remarks --- .../queries/elastic_query_disjunction_max.md | 5 +-- .../zio/elasticsearch/HttpExecutorSpec.scala | 16 ++++---- .../zio/elasticsearch/ElasticQuery.scala | 16 +++++--- .../zio/elasticsearch/query/Queries.scala | 6 +-- .../zio/elasticsearch/ElasticQuerySpec.scala | 40 +++++++++++++++---- website/sidebars.js | 1 + 6 files changed, 55 insertions(+), 29 deletions(-) diff --git a/docs/overview/queries/elastic_query_disjunction_max.md b/docs/overview/queries/elastic_query_disjunction_max.md index 17fe13f9c..c74b81748 100644 --- a/docs/overview/queries/elastic_query_disjunction_max.md +++ b/docs/overview/queries/elastic_query_disjunction_max.md @@ -13,13 +13,12 @@ import zio.elasticsearch.ElasticQuery.disjunctionMax You can create a `Disjunction max` query using the `disjunctionMax` method this way: ```scala -val query: DisjunctionMaxQuery = disjunctionMax(queries = Chunk(term(field = "stringField", value = "test"), exists( field = "intField"))) +val query: DisjunctionMaxQuery = disjunctionMax(term(field = "stringField", value = "test"), exists(field = "intField")) ``` If you want to change the `tieBreaker`, you can use `tieBreaker` method: ```scala -val queryWithPrefixLength: DisjunctionMaxQuery = disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) +val queryWithTieBreaker: DisjunctionMaxQuery = disjunctionMax(exists(field = "existsField"), ids(values = "1", "2", "3")).tieBreaker(0.5f) ``` You can find more information about `Disjunction max` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-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 1edf1b395..2be5c7c75 100644 --- a/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala +++ b/modules/integration/src/test/scala/zio/elasticsearch/HttpExecutorSpec.scala @@ -1183,15 +1183,13 @@ object HttpExecutorSpec extends IntegrationSpec { .refreshTrue ) query = disjunctionMax( - Chunk( - term( - field = TestDocument.stringField, - value = firstDocument.stringField.toLowerCase - ), - matchPhrase( - field = TestDocument.stringField, - value = secondDocument.stringField - ) + term( + field = TestDocument.stringField, + value = firstDocument.stringField.toLowerCase + ), + matchPhrase( + field = TestDocument.stringField, + value = secondDocument.stringField ) ) res <- Executor.execute(ElasticRequest.search(firstSearchIndex, query)).documentAs[TestDocument] diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index d6cbb0635..89e495372 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -90,28 +90,32 @@ object ElasticQuery { /** * Constructs a type-safe instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. * + * @param query + * a mandatory query to be wrapped inside of disjunction max query * @param queries - * the type-safe [[ElasticQuery]] objects to be wrapped inside of disjunction max query + * the rest of the queries to be wrapped inside of disjunction max query * @tparam S * document for which field query is executed. An implicit `Schema` instance must be in scope * @return * an instance of [[zio.elasticsearch.query.DisjunctionMax]] that represents the `disjunction max` query to be * performed. */ - final def disjunctionMax[S: Schema](queries: Chunk[ElasticQuery[S]]): DisjunctionMaxQuery[S] = - DisjunctionMax[S](queries = queries, tieBreaker = None) + final def disjunctionMax[S: Schema](query: ElasticQuery[S], queries: ElasticQuery[S]*): DisjunctionMaxQuery[S] = + DisjunctionMax[S](query = query, queries = Chunk.fromIterable(queries), tieBreaker = None) /** * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. * + * @param query + * a mandatory query to be wrapped inside of disjunction max query * @param queries - * the [[ElasticQuery]] objects to be wrapped inside of disjunction max query + * the rest of the queries to be wrapped inside of disjunction max query * @return * an instance of [[zio.elasticsearch.query.DisjunctionMax]] that represents the `disjunction max` query to be * performed. */ - final def disjunctionMax(queries: Chunk[ElasticQuery[Any]]): DisjunctionMaxQuery[Any] = - DisjunctionMax[Any](queries = queries, tieBreaker = None) + final def disjunctionMax(query: ElasticQuery[Any], queries: ElasticQuery[Any]*): DisjunctionMaxQuery[Any] = + DisjunctionMax[Any](query = query, queries = Chunk.fromIterable(queries), tieBreaker = None) /** * Constructs a type-safe instance of [[zio.elasticsearch.query.ExistsQuery]], that checks existence of the field, 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 6c1ae64fa..9fb0334d0 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -216,6 +216,7 @@ sealed trait DisjunctionMaxQuery[S] extends ElasticQuery[S] { } private[elasticsearch] final case class DisjunctionMax[S]( + query: ElasticQuery[S], queries: Chunk[ElasticQuery[S]], tieBreaker: Option[Float] ) extends DisjunctionMaxQuery[S] { self => @@ -224,14 +225,13 @@ private[elasticsearch] final case class DisjunctionMax[S]( self.copy(tieBreaker = Some(value)) private[elasticsearch] def toJson(fieldPath: Option[String]): Json = { + val allQueries = query +: queries val disMaxFields = Chunk( - Some("queries" -> Arr(queries.map(_.toJson(fieldPath)))), + Some("queries" -> Arr(allQueries.map(_.toJson(fieldPath)))), tieBreaker.map("tie_breaker" -> _.toJson) ).collect { case Some(obj) => obj } - Obj("dis_max" -> Obj(disMaxFields)) - } } diff --git a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala index 373198dc7..c04888633 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -417,14 +417,24 @@ object ElasticQuerySpec extends ZIOSpecDefault { ) }, test("disjunctionMax") { - val query = disjunctionMax(Chunk(exists("existsField"), ids("1", "2", "3"))) - val queryWithTieBreaker = disjunctionMax(Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) + val query = disjunctionMax(exists("existsField"), ids("1", "2", "3")) + val queryTs = disjunctionMax(exists(TestDocument.stringField), ids("1", "2", "3")) + val queryWithTieBreaker = disjunctionMax(exists("existsField"), ids("1", "2", "3")).tieBreaker(0.5f) assert(query)( equalTo( DisjunctionMax[Any]( - queries = - Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), + query = Exists[Any](field = "existsField", boost = None), + queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), + tieBreaker = None + ) + ) + ) && + assert(queryTs)( + equalTo( + DisjunctionMax[TestDocument]( + query = Exists[Any](field = "stringField", boost = None), + queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), tieBreaker = None ) ) @@ -432,8 +442,8 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryWithTieBreaker)( equalTo( DisjunctionMax[Any]( - queries = - Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), + query = Exists[Any](field = "existsField", boost = None), + queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), tieBreaker = Some(0.5f) ) ) @@ -2531,9 +2541,10 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryWithAllParams.toJson(fieldPath = None))(equalTo(expectedWithAllParams.toJson)) }, test("disjunctionMax") { - val query = disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))) + val query = disjunctionMax(exists("existsField"), ids("1", "2", "3")) + val queryTs = disjunctionMax(exists(TestDocument.stringField), ids("1", "2", "3")) val queryWithTieBreaker = - disjunctionMax(queries = Chunk(exists("existsField"), ids("1", "2", "3"))).tieBreaker(0.5f) + disjunctionMax(exists("existsField"), ids("1", "2", "3")).tieBreaker(0.5f) val expected = """ @@ -2547,6 +2558,18 @@ object ElasticQuerySpec extends ZIOSpecDefault { |} |""".stripMargin + val expectedTs = + """ + |{ + | "dis_max": { + | "queries": [ + | { "exists": { "field": "stringField" } }, + | { "ids": { "values": ["1", "2", "3"] } } + | ] + | } + |} + |""".stripMargin + val expectedWithTieBreaker = """ |{ @@ -2561,6 +2584,7 @@ object ElasticQuerySpec extends ZIOSpecDefault { |""".stripMargin assert(query.toJson(fieldPath = None))(equalTo(expected.toJson)) && + assert(queryTs.toJson(fieldPath = None))(equalTo(expectedTs.toJson)) && assert(queryWithTieBreaker.toJson(fieldPath = None))(equalTo(expectedWithTieBreaker.toJson)) }, test("exists") { diff --git a/website/sidebars.js b/website/sidebars.js index 5f9d1cb9e..53c8cf2d8 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -58,6 +58,7 @@ module.exports = { 'overview/aggregations/elastic_aggregation_sum', 'overview/aggregations/elastic_aggregation_terms', 'overview/aggregations/elastic_aggregation_value_count', + 'overview/aggregations/elastic_aggregation_weighted_avg', ], }, { From 0061a6e9f9128d1ff39128b0537e9f515e2dd0b8 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Wed, 15 Nov 2023 15:42:26 +0100 Subject: [PATCH 4/7] Implement changes in Disjunction max query --- docs/overview/queries/elastic_query_disjunction_max.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/overview/queries/elastic_query_disjunction_max.md b/docs/overview/queries/elastic_query_disjunction_max.md index c74b81748..f23ea2620 100644 --- a/docs/overview/queries/elastic_query_disjunction_max.md +++ b/docs/overview/queries/elastic_query_disjunction_max.md @@ -13,12 +13,12 @@ import zio.elasticsearch.ElasticQuery.disjunctionMax You can create a `Disjunction max` query using the `disjunctionMax` method this way: ```scala -val query: DisjunctionMaxQuery = disjunctionMax(term(field = "stringField", value = "test"), exists(field = "intField")) +val query: DisjunctionMaxQuery = disjunctionMax(query = term(field = "stringField", value = "test"), queries = exists(field = "intField"), term(field = "termField", value = "test")) ``` If you want to change the `tieBreaker`, you can use `tieBreaker` method: ```scala -val queryWithTieBreaker: DisjunctionMaxQuery = disjunctionMax(exists(field = "existsField"), ids(values = "1", "2", "3")).tieBreaker(0.5f) +val queryWithTieBreaker: DisjunctionMaxQuery = disjunctionMax(query = exists(field = "existsField"), queries = ids(values = "1", "2", "3"), term(field = "termField", value = "test")).tieBreaker(0.5f) ``` You can find more information about `Disjunction max` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-dis-max-query.html). From 5911c7078bf068870ab748af3695c0a330cc8517 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Wed, 15 Nov 2023 15:49:11 +0100 Subject: [PATCH 5/7] Implement changes in Disjunction max query docs --- docs/overview/queries/elastic_query_disjunction_max.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/overview/queries/elastic_query_disjunction_max.md b/docs/overview/queries/elastic_query_disjunction_max.md index f23ea2620..938a07e68 100644 --- a/docs/overview/queries/elastic_query_disjunction_max.md +++ b/docs/overview/queries/elastic_query_disjunction_max.md @@ -13,7 +13,12 @@ import zio.elasticsearch.ElasticQuery.disjunctionMax You can create a `Disjunction max` query using the `disjunctionMax` method this way: ```scala -val query: DisjunctionMaxQuery = disjunctionMax(query = term(field = "stringField", value = "test"), queries = exists(field = "intField"), term(field = "termField", value = "test")) +val query: DisjunctionMaxQuery = disjunctionMax(query = term(field = "stringField", value = "test"), queries = exists(field = "intField"), exists(field = "existsField")) +``` + +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `Disjunction max` query using the `disjunctionMax` method this way: +```scala +val query: DisjunctionMaxQuery = disjunctionMax(query = term(field = Document.stringField, value = "test"), queries = exists(field = Document.intField), term(field = Document.termField, value = "test")) ``` If you want to change the `tieBreaker`, you can use `tieBreaker` method: From b04fca9e4049039a14bde83414693ef1ec59f372 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Fri, 17 Nov 2023 12:20:57 +0100 Subject: [PATCH 6/7] Fix code remarks --- .../main/scala/zio/elasticsearch/ElasticQuery.scala | 8 ++------ .../main/scala/zio/elasticsearch/query/Queries.scala | 4 +--- .../scala/zio/elasticsearch/ElasticQuerySpec.scala | 12 ++++++------ 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index 89e495372..af317bb4d 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -90,8 +90,6 @@ object ElasticQuery { /** * Constructs a type-safe instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. * - * @param query - * a mandatory query to be wrapped inside of disjunction max query * @param queries * the rest of the queries to be wrapped inside of disjunction max query * @tparam S @@ -101,13 +99,11 @@ object ElasticQuery { * performed. */ final def disjunctionMax[S: Schema](query: ElasticQuery[S], queries: ElasticQuery[S]*): DisjunctionMaxQuery[S] = - DisjunctionMax[S](query = query, queries = Chunk.fromIterable(queries), tieBreaker = None) + DisjunctionMax[S](queries = Chunk.fromIterable(queries).prepended(query), tieBreaker = None) /** * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. * - * @param query - * a mandatory query to be wrapped inside of disjunction max query * @param queries * the rest of the queries to be wrapped inside of disjunction max query * @return @@ -115,7 +111,7 @@ object ElasticQuery { * performed. */ final def disjunctionMax(query: ElasticQuery[Any], queries: ElasticQuery[Any]*): DisjunctionMaxQuery[Any] = - DisjunctionMax[Any](query = query, queries = Chunk.fromIterable(queries), tieBreaker = None) + DisjunctionMax[Any](queries = Chunk.fromIterable(queries).prepended(query), tieBreaker = None) /** * Constructs a type-safe instance of [[zio.elasticsearch.query.ExistsQuery]], that checks existence of the field, 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 9fb0334d0..6b30adeb2 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -216,7 +216,6 @@ sealed trait DisjunctionMaxQuery[S] extends ElasticQuery[S] { } private[elasticsearch] final case class DisjunctionMax[S]( - query: ElasticQuery[S], queries: Chunk[ElasticQuery[S]], tieBreaker: Option[Float] ) extends DisjunctionMaxQuery[S] { self => @@ -225,10 +224,9 @@ private[elasticsearch] final case class DisjunctionMax[S]( self.copy(tieBreaker = Some(value)) private[elasticsearch] def toJson(fieldPath: Option[String]): Json = { - val allQueries = query +: queries val disMaxFields = Chunk( - Some("queries" -> Arr(allQueries.map(_.toJson(fieldPath)))), + Some("queries" -> Arr(queries.map(_.toJson(fieldPath)))), tieBreaker.map("tie_breaker" -> _.toJson) ).collect { case Some(obj) => obj } Obj("dis_max" -> Obj(disMaxFields)) diff --git a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala index c04888633..62188c22a 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -424,8 +424,8 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(query)( equalTo( DisjunctionMax[Any]( - query = Exists[Any](field = "existsField", boost = None), - queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), + queries = + Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), tieBreaker = None ) ) @@ -433,8 +433,8 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryTs)( equalTo( DisjunctionMax[TestDocument]( - query = Exists[Any](field = "stringField", boost = None), - queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), + queries = + Chunk(Exists[Any](field = "stringField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), tieBreaker = None ) ) @@ -442,8 +442,8 @@ object ElasticQuerySpec extends ZIOSpecDefault { assert(queryWithTieBreaker)( equalTo( DisjunctionMax[Any]( - query = Exists[Any](field = "existsField", boost = None), - queries = Chunk(Ids[Any](values = Chunk("1", "2", "3"))), + queries = + Chunk(Exists[Any](field = "existsField", boost = None), Ids[Any](values = Chunk("1", "2", "3"))), tieBreaker = Some(0.5f) ) ) From 201a49167c2abae7bb09ee9fff03bcbeb13392e8 Mon Sep 17 00:00:00 2001 From: vanjaftn Date: Fri, 17 Nov 2023 13:38:14 +0100 Subject: [PATCH 7/7] Resolve conflict --- .../src/main/scala/zio/elasticsearch/ElasticQuery.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index af317bb4d..08003f58e 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -99,7 +99,7 @@ object ElasticQuery { * performed. */ final def disjunctionMax[S: Schema](query: ElasticQuery[S], queries: ElasticQuery[S]*): DisjunctionMaxQuery[S] = - DisjunctionMax[S](queries = Chunk.fromIterable(queries).prepended(query), tieBreaker = None) + DisjunctionMax[S](queries = query +: Chunk.fromIterable(queries), tieBreaker = None) /** * Constructs an instance of [[zio.elasticsearch.query.DisjunctionMax]] using the specified parameters. @@ -111,7 +111,7 @@ object ElasticQuery { * performed. */ final def disjunctionMax(query: ElasticQuery[Any], queries: ElasticQuery[Any]*): DisjunctionMaxQuery[Any] = - DisjunctionMax[Any](queries = Chunk.fromIterable(queries).prepended(query), tieBreaker = None) + DisjunctionMax[Any](queries = query +: Chunk.fromIterable(queries), tieBreaker = None) /** * Constructs a type-safe instance of [[zio.elasticsearch.query.ExistsQuery]], that checks existence of the field,