Skip to content

Commit

Permalink
(dsl): Support prefix query (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
petarcurcin authored Jun 24, 2023
1 parent 1200f3a commit 0ac0d9a
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 3 deletions.
31 changes: 31 additions & 0 deletions docs/overview/queries/elastic_query_prefix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
id: elastic_query_prefix
title: "Prefix Query"
---

The `Prefix` query returns documents that contain a specific prefix in a provided field.

In order to use the `Prefix` query import the following:
```scala
import zio.elasticsearch.query.PrefixQuery
import zio.elasticsearch.ElasticQuery._
```

You can create a `Prefix` query using the `prefix` method this way:
```scala
val query: PrefixQuery = prefix(field = Document.name, value = "test")
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `Prefix` query using the `prefix` method this way:
```scala
val query: PrefixQuery = prefix(field = Document.name, value = "test")
```

If you want to change the `case_insensitive`, you can use `caseInsensitive`, `caseInsensitiveFalse` or `caseInsensitiveTrue` method:
```scala
val queryWithCaseInsensitive: PrefixQuery = prefix(field = Document.name, value = "test").caseInsensitive(true)
val queryWithCaseInsensitiveFalse: PrefixQuery = prefix(field = Document.name, value = "test").caseInsensitiveFalse
val queryWithCaseInsensitiveTrue: PrefixQuery = prefix(field = Document.name, value = "test").caseInsensitiveTrue
```

You can find more information about `Prefix` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-prefix-query.html).
2 changes: 1 addition & 1 deletion docs/overview/queries/elastic_query_term.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: elastic_query_term
title: "Term Query"
---

The `Term` query returns documents that contain an exact term in a provided field.
The `Term` query returns documents that contain an exact term in the provided field.

In order to use the `Term` query import the following:
```scala
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,32 @@ object HttpExecutorSpec extends IntegrationSpec {
Executor.execute(ElasticRequest.createIndex(secondSearchIndex)),
Executor.execute(ElasticRequest.deleteIndex(secondSearchIndex)).orDie
),
test("search for a document which contains a specific prefix using a prefix query") {
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) {
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) =>
for {
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll))
_ <- Executor.execute(
ElasticRequest.upsert[TestDocument](firstSearchIndex, firstDocumentId, firstDocument)
)
_ <- Executor.execute(
ElasticRequest
.upsert[TestDocument](firstSearchIndex, secondDocumentId, secondDocument)
.refreshTrue
)
query = ElasticQuery.prefix(
field = TestDocument.stringField.keyword,
value = firstDocument.stringField.take(3)
)
res <- Executor
.execute(ElasticRequest.search(firstSearchIndex, query))
.documentAs[TestDocument]
} yield assert(res)(Assertion.contains(firstDocument))
}
} @@ around(
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)),
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie
),
test("search for a document which contains a term using a wildcard query") {
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument) {
(firstDocumentId, firstDocument, secondDocumentId, secondDocument) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,38 @@ object ElasticQuery {
final def nested(path: String, query: ElasticQuery[_]): NestedQuery[Any] =
Nested(path = path, query = query, scoreMode = None, ignoreUnmapped = None, innerHitsField = None)

/**
* Constructs a type-safe instance of [[zio.elasticsearch.query.PrefixQuery]] using the specified parameters.
* [[zio.elasticsearch.query.PrefixQuery]] is used for matching documents that contain a specific prefix in a provided
* field.
*
* @param field
* the type-safe field for which query is specified for
* @param value
* text value that will be used for the query
* @tparam S
* document for which field query is executed
* @return
* an instance of [[zio.elasticsearch.query.PrefixQuery]] that represents the prefix query to be performed.
*/
final def prefix[S](field: Field[S, String], value: String): PrefixQuery[S] =
Prefix(field = field.toString, value = value, caseInsensitive = None)

/**
* Constructs an instance of [[zio.elasticsearch.query.PrefixQuery]] using the specified parameters.
* [[zio.elasticsearch.query.PrefixQuery]] is used for matching documents that contain a specific prefix in a provided
* field.
*
* @param field
* the field for which query is specified for
* @param value
* text value that will be used for the query
* @return
* an instance of [[zio.elasticsearch.query.PrefixQuery]] that represents the prefix query to be performed.
*/
final def prefix(field: String, value: String): Prefix[Any] =
Prefix(field = field, value = value, caseInsensitive = None)

/**
* Constructs a type-safe unbounded instance of [[zio.elasticsearch.query.RangeQuery]] using the specified parameters.
*
Expand Down Expand Up @@ -662,7 +694,7 @@ object ElasticQuery {
* @param field
* the type-safe field for which query is specified for
* @param value
* the value that will be used for the query, represented by an instance of type `A`
* text value that will be used for the query
* @tparam S
* document for which field query is executed
* @return
Expand All @@ -679,7 +711,7 @@ object ElasticQuery {
* @param field
* the field for which query is specified for
* @param value
* the value that will be used for the query, represented by an instance of type `A`
* text value that will be used for the query
* @return
* an instance of [[zio.elasticsearch.query.TermQuery]] that represents the term query to be performed.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,25 @@ private[elasticsearch] case object Unbounded extends LowerBound with UpperBound
private[elasticsearch] def toJson: Option[(String, Json)] = None
}

sealed trait PrefixQuery[S] extends ElasticQuery[S] with HasCaseInsensitive[PrefixQuery[S]]

private[elasticsearch] final case class Prefix[S](
field: String,
value: String,
caseInsensitive: Option[Boolean]
) extends PrefixQuery[S] { self =>

def caseInsensitive(value: Boolean): PrefixQuery[S] =
self.copy(caseInsensitive = Some(value))

private[elasticsearch] def toJson(fieldPath: Option[String]): Json = {
val prefixFields = Some("value" -> value.toJson) ++ caseInsensitive.map(
"case_insensitive" -> _.toJson
)
Obj("prefix" -> Obj(fieldPath.foldRight(field)(_ + "." + _) -> Obj(Chunk.fromIterable(prefixFields))))
}
}

sealed trait RangeQuery[S, A, LB <: LowerBound, UB <: UpperBound]
extends ElasticQuery[S]
with HasBoost[RangeQuery[S, A, LB, UB]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,29 @@ object ElasticQuerySpec extends ZIOSpecDefault {
)
)
},
test("prefix") {
val query = prefix("stringField", "test")
val queryTs = prefix(TestDocument.stringField, "test")
val queryWithSuffix = prefix(TestDocument.stringField.keyword, "test")
val queryWithCaseInsensitive = prefix(TestDocument.stringField, "test").caseInsensitiveTrue

assert(query)(
equalTo(Prefix[Any](field = "stringField", value = "test", caseInsensitive = None))
) &&
assert(queryTs)(
equalTo(Prefix[TestDocument](field = "stringField", value = "test", caseInsensitive = None))
) &&
assert(queryWithSuffix)(
equalTo(
Prefix[TestDocument](field = "stringField.keyword", value = "test", caseInsensitive = None)
)
) &&
assert(queryWithCaseInsensitive)(
equalTo(
Prefix[TestDocument](field = "stringField", value = "test", caseInsensitive = Some(true))
)
)
},
test("range") {
val query = range("testField")
val queryString = range(TestDocument.stringField)
Expand Down Expand Up @@ -2531,6 +2554,36 @@ object ElasticQuerySpec extends ZIOSpecDefault {
assert(queryWithScoreMode.toJson(fieldPath = None))(equalTo(expectedWithScoreMode.toJson)) &&
assert(queryWithAllParams.toJson(fieldPath = None))(equalTo(expectedWithAllParams.toJson))
},
test("prefix") {
val query = prefix(TestDocument.stringField, "test")
val queryWithCaseInsensitive = prefix(TestDocument.stringField, "test").caseInsensitiveTrue

val expected =
"""
|{
| "prefix": {
| "stringField": {
| "value": "test"
| }
| }
|}
|""".stripMargin

val expectedWithCaseInsensitive =
"""
|{
| "prefix": {
| "stringField": {
| "value": "test",
| "case_insensitive": true
| }
| }
|}
|""".stripMargin

assert(query.toJson(fieldPath = None))(equalTo(expected.toJson)) &&
assert(queryWithCaseInsensitive.toJson(fieldPath = None))(equalTo(expectedWithCaseInsensitive.toJson))
},
test("range") {
val queryEmpty = range(TestDocument.intField)
val queryEmptyWithBoost = range(TestDocument.intField).boost(3.14)
Expand Down
1 change: 1 addition & 0 deletions website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports = {
'overview/queries/elastic_query_match_all',
'overview/queries/elastic_query_match_phrase',
'overview/queries/elastic_query_nested',
'overview/queries/elastic_query_prefix',
'overview/queries/elastic_query_range',
'overview/queries/elastic_query_term',
'overview/queries/elastic_query_terms',
Expand Down

0 comments on commit 0ac0d9a

Please sign in to comment.