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 LocalDate elastic primitive and date format for range queries #214

Merged
merged 3 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,33 @@ object HttpExecutorSpec extends IntegrationSpec {
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)),
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie
),
test("search for first 2 documents using range query with date format") {
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument, genDocumentId, genTestDocument) {
(firstDocumentId, firstDocument, secondDocumentId, secondDocument, thirdDocumentId, thirdDocument) =>
val firstDocumentUpdated = firstDocument.copy(dateField = LocalDate.now.minusDays(2))
val secondDocumentUpdated = secondDocument.copy(dateField = LocalDate.now)
val thirdDocumentUpdated = thirdDocument.copy(dateField = LocalDate.now.plusDays(2))
for {
_ <- Executor.execute(ElasticRequest.deleteByQuery(firstSearchIndex, matchAll))
_ <- Executor.execute(
ElasticRequest.upsert[TestDocument](firstSearchIndex, firstDocumentId, firstDocumentUpdated)
)
_ <- Executor.execute(
ElasticRequest.upsert[TestDocument](firstSearchIndex, secondDocumentId, secondDocumentUpdated)
)
_ <- Executor.execute(
ElasticRequest
.upsert[TestDocument](firstSearchIndex, thirdDocumentId, thirdDocumentUpdated)
.refreshTrue
)
query = range(TestDocument.dateField).gte(LocalDate.now).format("uuuu-MM-dd").boost(1.0)
res <- Executor.execute(ElasticRequest.search(firstSearchIndex, query)).documentAs[TestDocument]
} yield assert(res)(equalTo(List(secondDocumentUpdated, thirdDocumentUpdated)))
}
} @@ around(
Executor.execute(ElasticRequest.createIndex(firstSearchIndex)),
Executor.execute(ElasticRequest.deleteIndex(firstSearchIndex)).orDie
),
test("search for documents with source filtering") {
checkOnce(genDocumentId, genTestDocument, genDocumentId, genTestDocument, genDocumentId, genTestDocument) {
(firstDocumentId, firstDocument, secondDocumentId, secondDocument, thirdDocumentId, thirdDocument) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package zio.elasticsearch
import zio.json.ast.Json
import zio.json.ast.Json.{Num, Str}

import java.time.LocalDate
import java.util.UUID

object ElasticPrimitive {
Expand Down Expand Up @@ -54,6 +55,10 @@ object ElasticPrimitive {
def toJson(value: UUID): Json = Str(value.toString)
}

implicit object ElasticLocalDate extends ElasticPrimitive[LocalDate] {
def toJson(value: LocalDate): Json = Str(value.toString)
}
Comment on lines +58 to +60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this up because of sorting.


final implicit class ElasticPrimitiveOps[A](private val value: A) extends AnyVal {
def toJson(implicit EP: ElasticPrimitive[A]): Json = EP.toJson(value)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

package zio.elasticsearch.query

import zio.Chunk
import zio.elasticsearch.ElasticPrimitive._
import zio.elasticsearch.query.options._
import zio.elasticsearch.query.sort.options.HasFormat
import zio.json.ast.Json
import zio.json.ast.Json.{Arr, Num, Obj, Str}
import zio.schema.Schema
Expand Down Expand Up @@ -353,7 +355,8 @@ private[elasticsearch] case object Unbounded extends LowerBound with UpperBound

sealed trait RangeQuery[S, A, LB <: LowerBound, UB <: UpperBound]
extends ElasticQuery[S]
with HasBoost[RangeQuery[S, A, LB, UB]] {
with HasBoost[RangeQuery[S, A, LB, UB]]
with HasFormat[RangeQuery[S, A, LB, UB]] {
def gt[B <: A: ElasticPrimitive](value: B)(implicit
@unused ev: LB =:= Unbounded.type
): RangeQuery[S, B, GreaterThan[B], UB]
Expand All @@ -375,11 +378,15 @@ private[elasticsearch] final case class Range[S, A, LB <: LowerBound, UB <: Uppe
field: String,
lower: LB,
upper: UB,
boost: Option[Double]
boost: Option[Double],
format: Option[String]
) extends RangeQuery[S, A, LB, UB] { self =>
def boost(value: Double): RangeQuery[S, A, LB, UB] =
self.copy(boost = Some(value))

def format(value: String): RangeQuery[S, A, LB, UB] =
self.copy(format = Some(value))

def gt[B <: A: ElasticPrimitive](value: B)(implicit
@unused ev: LB =:= Unbounded.type
): RangeQuery[S, B, GreaterThan[B], UB] =
Expand All @@ -400,12 +407,19 @@ private[elasticsearch] final case class Range[S, A, LB <: LowerBound, UB <: Uppe
): RangeQuery[S, B, LB, LessThanOrEqualTo[B]] =
self.copy(upper = LessThanOrEqualTo(value))

def paramsToJson(fieldPath: Option[String]): Json = {
val rangeFields = Some(
fieldPath.foldRight(field)(_ + "." + _) -> Obj(List(lower.toJson, upper.toJson).flatten: _*)
) ++ boost.map("boost" -> Num(_))
Obj("range" -> Obj(rangeFields.toList: _*))
}
def paramsToJson(fieldPath: Option[String]): Json =
Obj(
"range" -> Obj(
fieldPath.foldRight(field)(_ + "." + _) -> Obj(
Chunk(
lower.toJson,
upper.toJson,
boost.map("boost" -> Num(_)),
format.map("format" -> Str(_))
).flatten: _*
)
)
)
}

private[elasticsearch] object Range {
Expand All @@ -414,7 +428,8 @@ private[elasticsearch] object Range {
field = field,
lower = Unbounded,
upper = Unbounded,
boost = None
boost = None,
format = None
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import zio.prelude.Validation
import zio.test.Assertion.equalTo
import zio.test.{Spec, TestEnvironment, ZIOSpecDefault, assert}

import java.time.LocalDate

object ElasticQuerySpec extends ZIOSpecDefault {
def spec: Spec[TestEnvironment, Any] =
suite("ElasticQuery")(
Expand Down Expand Up @@ -259,7 +261,13 @@ object ElasticQuerySpec extends ZIOSpecDefault {
must = List(Terms(field = "stringField", values = List("a", "b", "c"), boost = None)),
mustNot = List(Match(field = "intField", value = 50, boost = None)),
should = List(
Range(field = "intField", lower = GreaterThan(1), upper = LessThanOrEqualTo(100), boost = None),
Range(
field = "intField",
lower = GreaterThan(1),
upper = LessThanOrEqualTo(100),
boost = None,
format = None
),
Match(field = "stringField", value = "test", boost = None)
),
boost = None,
Expand Down Expand Up @@ -302,7 +310,13 @@ object ElasticQuerySpec extends ZIOSpecDefault {
must = List(Terms(field = "stringField", values = List("a", "b", "c"), boost = None)),
mustNot = List(Match(field = "intField", value = 50, boost = None)),
should = List(
Range(field = "intField", lower = GreaterThan(1), upper = LessThanOrEqualTo(100), boost = None),
Range(
field = "intField",
lower = GreaterThan(1),
upper = LessThanOrEqualTo(100),
boost = None,
format = None
),
Match(field = "stringField", value = "test", boost = None)
),
boost = Some(3.14),
Expand Down Expand Up @@ -588,14 +602,16 @@ object ElasticQuerySpec extends ZIOSpecDefault {
val queryInclusiveUpperBound = range(TestDocument.intField).lte(21)
val queryMixedBounds = queryLowerBound.lte(21.0)
val queryWithBoostParam = queryMixedBounds.boost(2.8)
val queryWithFormatParam = range(TestDocument.dateField).gt(LocalDate.of(2023, 5, 11)).format("uuuu-MM-dd")

assert(query)(
equalTo(
Range[Any, Any, Unbounded.type, Unbounded.type](
field = "testField",
lower = Unbounded,
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -605,7 +621,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "stringField",
lower = Unbounded,
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -615,7 +632,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "intField",
lower = Unbounded,
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -625,7 +643,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "stringField.test",
lower = Unbounded,
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -635,7 +654,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "doubleField",
lower = GreaterThan(3.14),
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -645,7 +665,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "doubleField",
lower = Unbounded,
upper = LessThan(10.21),
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -655,7 +676,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "intField",
lower = GreaterThanOrEqualTo(10),
upper = Unbounded,
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -665,7 +687,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "intField",
lower = Unbounded,
upper = LessThanOrEqualTo(21),
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -675,7 +698,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "doubleField",
lower = GreaterThan(3.14),
upper = LessThanOrEqualTo(21.0),
boost = None
boost = None,
format = None
)
)
) &&
Expand All @@ -685,7 +709,19 @@ object ElasticQuerySpec extends ZIOSpecDefault {
field = "doubleField",
lower = GreaterThan(3.14),
upper = LessThanOrEqualTo(21),
boost = Some(2.8)
boost = Some(2.8),
format = None
)
)
) &&
assert(queryWithFormatParam)(
equalTo(
Range[TestDocument, LocalDate, GreaterThan[LocalDate], Unbounded.type](
field = "dateField",
lower = GreaterThan(LocalDate.of(2023, 5, 11)),
upper = Unbounded,
boost = None,
format = Some("uuuu-MM-dd")
)
)
)
Expand Down Expand Up @@ -1978,6 +2014,7 @@ object ElasticQuerySpec extends ZIOSpecDefault {
val queryInclusiveUpperBound = range(TestDocument.intField).lte(45)
val queryMixedBounds = range(TestDocument.intField).gt(10).lte(99)
val queryMixedBoundsWithBoost = range(TestDocument.intField).gt(10).lte(99).boost(3.14)
val queryWithFormat = range(TestDocument.dateField).gt(LocalDate.of(2020, 1, 10)).format("uuuu-MM-dd")

val expectedEmpty =
"""
Expand All @@ -1997,8 +2034,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
| "query": {
| "range": {
| "intField": {
| },
| "boost": 3.14
| "boost": 3.14
| }
| }
| }
|}
Expand Down Expand Up @@ -2077,9 +2114,23 @@ object ElasticQuerySpec extends ZIOSpecDefault {
| "range": {
| "intField": {
| "gt": 10,
| "lte": 99
| },
| "boost": 3.14
| "lte": 99,
| "boost": 3.14
| }
| }
| }
|}
|""".stripMargin

val expectedWithFormat =
"""
|{
| "query": {
| "range": {
| "dateField": {
| "gt": "2020-01-10",
| "format": "uuuu-MM-dd"
| }
| }
| }
|}
Expand All @@ -2092,7 +2143,8 @@ object ElasticQuerySpec extends ZIOSpecDefault {
assert(queryInclusiveLowerBound.toJson)(equalTo(expectedInclusiveLowerBound.toJson)) &&
assert(queryInclusiveUpperBound.toJson)(equalTo(expectedInclusiveUpperBound.toJson)) &&
assert(queryMixedBounds.toJson)(equalTo(expectedMixedBounds.toJson)) &&
assert(queryMixedBoundsWithBoost.toJson)(equalTo(expectedMixedBoundsWithBoost.toJson))
assert(queryMixedBoundsWithBoost.toJson)(equalTo(expectedMixedBoundsWithBoost.toJson)) &&
assert(queryWithFormat.toJson)(equalTo(expectedWithFormat.toJson))
},
test("startsWith") {
val query = startsWith(TestDocument.stringField, "test")
Expand Down