diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala index 224889176..6c6194715 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala @@ -899,6 +899,48 @@ object ElasticQuery { final def prefix(field: String, value: String): Prefix[Any] = Prefix(field = field, value = value, caseInsensitive = None) + /** + * Constructs an instance of [[zio.elasticsearch.query.QueryStringQuery]] using the specified parameters. + * [[zio.elasticsearch.query.QueryStringQuery]] supports query strings with simple syntax for searching multiple + * fields. + * + * @param query + * the query string to search for + * @return + * an instance of [[zio.elasticsearch.query.QueryStringQuery]] that represents the query to be performed. + */ + final def queryStringQuery(query: String): QueryString[Any] = + QueryString( + query = query, + fields = Chunk.empty, + defaultField = None, + boost = None, + minimumShouldMatch = None + ) + + /** + * Constructs a type-safe instance of [[zio.elasticsearch.query.QueryStringQuery]] using the specified parameters. + * [[zio.elasticsearch.query.QueryStringQuery]] supports query strings with simple syntax for searching multiple + * fields. + * + * @param fields + * the type-safe fields to be searched + * @param query + * the query string to search for + * @tparam S + * the document type on which the query is executed + * @return + * an instance of [[zio.elasticsearch.query.QueryStringQuery]] that represents the query to be performed. + */ + final def queryStringQuery[S: Schema](query: String, fields: Field[S, _]*): QueryStringQuery[S] = + QueryString[S]( + query = query, + fields = Chunk.fromIterable(fields.map(_.toString)), + defaultField = None, + boost = None, + minimumShouldMatch = None + ) + /** * Constructs a type-safe unbounded instance of [[zio.elasticsearch.query.RangeQuery]] 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 189f4d86c..c76688461 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/query/Queries.scala @@ -22,7 +22,7 @@ import zio.elasticsearch.Field import zio.elasticsearch.query.options._ import zio.elasticsearch.query.sort.options.HasFormat import zio.json.ast.Json -import zio.json.ast.Json.{Arr, Obj} +import zio.json.ast.Json.{Arr, Obj, Str} import zio.schema.Schema sealed trait ElasticQuery[-S] { self => @@ -1016,6 +1016,49 @@ private[elasticsearch] final case class Prefix[S]( } } +sealed trait QueryStringQuery[S] + extends ElasticQuery[S] + with HasBoost[QueryStringQuery[S]] + with HasMinimumShouldMatch[QueryStringQuery[S]] + +private[elasticsearch] final case class QueryString[S]( + defaultField: Option[String], + fields: Chunk[String], + query: String, + boost: Option[Double], + minimumShouldMatch: Option[Int] +) extends QueryStringQuery[S] { self => + + def boost(value: Double): QueryStringQuery[S] = + self.copy(boost = Some(value)) + + def fields(field: String, fields: String*): QueryStringQuery[S] = + self.copy(fields = Chunk.fromIterable(field +: fields)) + + def fields(fields: Chunk[String]): QueryStringQuery[S] = + self.copy(fields = fields) + + def minimumShouldMatch(value: Int): QueryStringQuery[S] = + self.copy(minimumShouldMatch = Some(value)) + + private[elasticsearch] def toJson(fieldPath: Option[String]): Json = { + val fieldsJson = + if (fields.nonEmpty) Some("fields" -> Arr(fields.map(Str(_)))) + else None + + val params = Chunk( + Some("query" -> Str(query)), + defaultField.map("default_field" -> Str(_)), + fieldsJson, + boost.map("boost" -> Json.Num(_)), + minimumShouldMatch.map("minimum_should_match" -> Json.Num(_)) + ).flatten + + Obj("query" -> Obj("query_string" -> Obj(params))) + } + +} + sealed trait RangeQuery[S, A, LB <: LowerBound, UB <: UpperBound] extends ElasticQuery[S] with HasBoost[RangeQuery[S, A, LB, UB]] diff --git a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala index c60bb91f5..07f57b34d 100644 --- a/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala +++ b/modules/library/src/test/scala/zio/elasticsearch/ElasticQuerySpec.scala @@ -1514,6 +1514,70 @@ object ElasticQuerySpec extends ZIOSpecDefault { ) ) }, + test("queryStringQuery") { + val queryNoFields = queryStringQuery("(new york city) OR (big apple)") + val queryWithFields = queryStringQuery("(new york city) OR (big apple)") + .fields(Chunk("title", "description")) + val queryWithTypedFields = queryStringQuery("(new york city) OR (big apple)") + .fields(Chunk(TestDocument.stringField.toString)) + val queryWithMinShouldMatch = queryNoFields.minimumShouldMatch(1) + val queryAllParams = queryWithFields.minimumShouldMatch(1).boost(2.0) + assert(queryNoFields)( + equalTo( + QueryString[Any]( + query = "(new york city) OR (big apple)", + fields = Chunk.empty, + defaultField = None, + boost = None, + minimumShouldMatch = None + ) + ) + ) && + assert(queryWithFields)( + equalTo( + QueryString[Any]( + query = "(new york city) OR (big apple)", + fields = Chunk("title", "description"), + defaultField = None, + boost = None, + minimumShouldMatch = None + ) + ) + ) && + assert(queryWithTypedFields)( + equalTo( + QueryString[Any]( + query = "(new york city) OR (big apple)", + fields = Chunk("string"), + defaultField = None, + boost = None, + minimumShouldMatch = None + ) + ) + ) && + assert(queryWithMinShouldMatch)( + equalTo( + QueryString[Any]( + query = "(new york city) OR (big apple)", + fields = Chunk.empty, + defaultField = None, + boost = None, + minimumShouldMatch = Some(1) + ) + ) + ) && + assert(queryAllParams)( + equalTo( + QueryString[Any]( + query = "(new york city) OR (big apple)", + fields = Chunk("title", "description"), + defaultField = None, + boost = Some(2.0), + minimumShouldMatch = Some(1) + ) + ) + ) + }, test("range") { val query = range("testField") val queryString = range(TestDocument.stringField)