Skip to content

Commit

Permalink
(dsl): Add public data-type GeoPoint and refactor GeoDistanceQuery (#277
Browse files Browse the repository at this point in the history
)
  • Loading branch information
arnoldlacko authored Jul 3, 2023
1 parent cd495ed commit b567c7d
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 223 deletions.
44 changes: 21 additions & 23 deletions docs/overview/queries/elastic_query_geo_distance.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,48 @@ import zio.elasticsearch.query.GeoDistanceQuery
import zio.elasticsearch.ElasticQuery._
```

You can create a `GeoDistance` query using the `geoDistance` method with latitude and longitude in the following manner:
You can create a `GeoDistance` query using the `geoDistance` method with a `GeoPoint` in the following manner:
```scala
val query: GeoDistanceQuery = geoDistance(field = "location", latitude = 20.0, longitude = 20.0)
val query: GeoDistanceQuery =
geoDistance(field = "location", point = GeoPoint(20.0, 20.0), distance = Distance(200, Kilometers))
```

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `GeoDistance` query using the `geoDistance` method with latitude and longitude in the following manner:
```scala
val query: GeoDistanceQuery = geoDistance(field = Document.location, latitude = 20.0, longitude = 20.0)
val query: GeoDistanceQuery =
geoDistance(field = Document.location, point = GeoPoint(20.0, 20.0), distance = Distance(200, Kilometers))
```

You can create a `GeoDistance` query using the `geoDistance` method with coordinates in the following manner:
If you want to specify the `distance_type`, you can use the `distanceType` method:
```scala
val query: GeoDistanceQuery = geoDistance(field = "location", coordinates = "40,31")
```
import zio.elasticsearch.query.DistanceType

You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `GeoDistance` query using the `geoDistance` method with coordinates in the following manner:
```scala
val query: GeoDistanceQuery = geoDistance(field = Document.location, coordinates = "40,31")
val queryWithDistanceType: GeoDistanceQuery =
geoDistance(field = "location", point = GeoPoint(20.0, 20.0), distance = Distance(200, Kilometers))
.distanceType(value = DistanceType.Plane)
```

If you want to change the `distance`, you can use `distance` method:
If you want to specify a query name, you can use the `name` method:
```scala
import zio.elasticsearch.query.DistanceUnit

val queryWithDistance: GeoDistanceQuery = geoDistance(field = "location", coordinates = "40,31").distance(value = 20.0, unit = DistanceUnit.Kilometers)
val queryWithName: GeoDistanceQuery =
geoDistance(field = "location", point = GeoPoint(20.0, 20.0), distance = Distance(200, Kilometers)).name("name")
```

If you want to change the `distance_type`, you can use `distanceType` method:
If you want to specify the `validation_method`, you can use the `validationMethod` method:
```scala
import zio.elasticsearch.query.DistanceType

val queryWithDistanceType: GeoDistanceQuery = geoDistance(field = "location", coordinates = "40,31").distanceType(value = DistanceType.Plane)
```
import zio.elasticsearch.query.ValidationMethod

If you want to change the `_name`, you can use `name` method:
```scala
val queryWithName: GeoDistanceQuery = geoDistance(field = "location", coordinates = "40,31").name("name")
val queryWithValidationMethod: GeoDistanceQuery =
geoDistance(field = "location", point = GeoPoint(20.0, 20.0), distance = Distance(200, Kilometers))
.validationMethod(value = ValidationMethod.IgnoreMalformed)
```

If you want to change the `validation_method`, you can use `validationMethod` method:
You can also specify the point as a geo-hash:
```scala
import zio.elasticsearch.query.ValidationMethod

val queryWithValidationMethod: GeoDistanceQuery = geoDistance(field = "location", coordinates = "40,31").validationMethod(value = ValidationMethod.IgnoreMalformed)
val queryWithValidationMethod: GeoDistanceQuery =
geoDistance(field = "location", point = GeoHash("drm3btev3e86"), distance = Distance(200, Kilometers))
```

You can find more information about `GeoDistance` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-geo-distance-query.html#query-dsl-geo-distance-query).
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ import zio.elasticsearch.ElasticHighlight.highlight
import zio.elasticsearch.ElasticQuery.{script => _, _}
import zio.elasticsearch.ElasticSort.sortBy
import zio.elasticsearch.aggregation.AggregationOrder
import zio.elasticsearch.data.GeoPoint
import zio.elasticsearch.domain.{PartialTestDocument, TestDocument, TestSubDocument}
import zio.elasticsearch.executor.Executor
import zio.elasticsearch.query.DistanceUnit.Kilometers
import zio.elasticsearch.query.FunctionScoreFunction.randomScoreFunction
import zio.elasticsearch.query.sort.SortMode.Max
import zio.elasticsearch.query.sort.SortOrder._
import zio.elasticsearch.query.sort.SourceType.NumberType
import zio.elasticsearch.query.{FunctionScoreBoostMode, FunctionScoreFunction}
import zio.elasticsearch.query.{Distance, FunctionScoreBoostMode, FunctionScoreFunction}
import zio.elasticsearch.request.{CreationOutcome, DeletionOutcome}
import zio.elasticsearch.result._
import zio.elasticsearch.script.{Painless, Script}
Expand Down Expand Up @@ -1760,7 +1761,7 @@ object HttpExecutorSpec extends IntegrationSpec {
|{
| "mappings": {
| "properties": {
| "locationField": {
| "geoPointField": {
| "type": "geo_point"
| }
| }
Expand All @@ -1774,30 +1775,20 @@ object HttpExecutorSpec extends IntegrationSpec {
_ <- Executor.execute(
ElasticRequest.create[TestDocument](geoDistanceIndex, document).refreshTrue
)
r1 <- Executor
.execute(
ElasticRequest.search(
geoDistanceIndex,
ElasticQuery
.geoDistance("locationField", document.locationField.lat, document.locationField.lon)
.distance(300, Kilometers)
)
)
.documentAs[TestDocument]
r2 <-
Executor
.execute(
ElasticRequest.search(
geoDistanceIndex,
ElasticQuery
.geoDistance("locationField", s"${document.locationField.lat}, ${document.locationField.lon}")
.distance(300, Kilometers)
)
)
.documentAs[TestDocument]
} yield assert(r1 ++ r2)(
equalTo(Chunk(document, document))
)
result <- Executor
.execute(
ElasticRequest.search(
geoDistanceIndex,
ElasticQuery
.geoDistance(
"geoPointField",
GeoPoint(document.geoPointField.lat, document.geoPointField.lon),
Distance(300, Kilometers)
)
)
)
.documentAs[TestDocument]
} yield assert(result)(equalTo(Chunk(document)))
}
} @@ after(Executor.execute(ElasticRequest.deleteIndex(geoDistanceIndex)).orDie)
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package zio.elasticsearch
import sttp.client3.httpclient.zio.HttpClientZioBackend
import zio._
import zio.elasticsearch.ElasticQuery.matchAll
import zio.elasticsearch.data.GeoPoint
import zio.elasticsearch.domain._
import zio.elasticsearch.executor.Executor
import zio.test.Assertion.{containsString, hasMessage}
Expand Down Expand Up @@ -62,11 +63,11 @@ trait IntegrationSpec extends ZIOSpecDefault {
def genDocumentId: Gen[Any, DocumentId] =
Gen.stringBounded(10, 40)(Gen.alphaNumericChar).map(DocumentId(_))

def genLocation: Gen[Any, Location] =
def genGeoPoint: Gen[Any, GeoPoint] =
for {
latField <- Gen.bigDecimal(10, 90).map(_.setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble)
lonField <- Gen.bigDecimal(10, 90).map(_.setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble)
} yield Location(lat = latField, lon = lonField)
latitude <- Gen.bigDecimal(10, 90).map(_.setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble)
longitude <- Gen.bigDecimal(10, 90).map(_.setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble)
} yield GeoPoint(latitude, longitude)

def genTestDocument: Gen[Any, TestDocument] = for {
stringField <- Gen.stringBounded(5, 10)(Gen.alphaChar)
Expand All @@ -75,15 +76,15 @@ trait IntegrationSpec extends ZIOSpecDefault {
intField <- Gen.int(1, 2000)
doubleField <- Gen.double(100, 2000)
booleanField <- Gen.boolean
locationField <- genLocation
geoPointField <- genGeoPoint
} yield TestDocument(
stringField = stringField,
dateField = dateField,
subDocumentList = subDocumentList,
intField = intField,
doubleField = doubleField,
booleanField = booleanField,
locationField = locationField
geoPointField = geoPointField
)

def genTestSubDocument: Gen[Any, TestSubDocument] = for {
Expand Down
82 changes: 43 additions & 39 deletions modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package zio.elasticsearch

import zio.Chunk
import zio.elasticsearch.ElasticPrimitive.ElasticPrimitive
import zio.elasticsearch.data.GeoPoint
import zio.elasticsearch.query._
import zio.elasticsearch.script.Script
import zio.schema.Schema
Expand Down Expand Up @@ -176,25 +177,22 @@ object ElasticQuery {
* Constructs a type-safe instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
* @param field
* the type-safe field for which query is specified for
* @param longitude
* longitude of the desired point
* @param latitude
* latitude of the desired point
* the type-safe GeoPoint field for which the query is specified
* @param point
* the geo-point from which the distance should be measured
* @param distance
* the distance within which values should be matched
* @tparam S
* document for which field query is executed
* the type of document on which the query is defined
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents the `geo_distance` query to be
* performed.
*/
final def geoDistance[S](
field: Field[S, _],
latitude: Double,
longitude: Double
): GeoDistanceQuery[S] =
final def geoDistance[S](field: Field[S, GeoPoint], point: GeoPoint, distance: Distance): GeoDistanceQuery[S] =
GeoDistance(
field = field.toString,
point = s"$latitude,$longitude",
distance = None,
point = s"${point.lat},${point.lon}",
distance = distance,
distanceType = None,
queryName = None,
validationMethod = None
Expand All @@ -204,19 +202,20 @@ object ElasticQuery {
* Constructs an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
* @param field
* the field for which query is specified for
* @param longitude
* longitude of the desired point
* @param latitude
* latitude of the desired point
* the field for which the query is specified
* @param point
* the geo-point from which the distance should be measured
* @param distance
* the distance within which values should be matched
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents the `geo_distance` query to be
* performed.
*/
final def geoDistance(field: String, latitude: Double, longitude: Double): GeoDistanceQuery[Any] =
final def geoDistance(field: String, point: GeoPoint, distance: Distance): GeoDistanceQuery[Any] =
GeoDistance(
field = field,
point = s"$latitude,$longitude",
distance = None,
point = s"${point.lat},${point.lon}",
distance = distance,
distanceType = None,
queryName = None,
validationMethod = None
Expand All @@ -226,20 +225,22 @@ object ElasticQuery {
* Constructs a type-safe instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
* @param field
* the type-safe field for which query is specified for
* @param coordinates
* longitude and latitude the of the desired point written as string (e.g. "40,31") or geo hash (e.g.
* "drm3btev3e86")
* the type-safe field for which the query is specified
* @param point
* the geo-point from which the distance should be measured, defined as geo-hash
* @param distance
* the distance within which values should be matched
* @tparam S
* document for which field query is executed
* the type of document on which the query is defined
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents the `geo_distance` query to be
* performed.
*/
final def geoDistance[S](field: Field[S, _], coordinates: String): GeoDistanceQuery[S] =
final def geoDistance[S](field: Field[S, GeoPoint], point: GeoHash, distance: Distance): GeoDistanceQuery[S] =
GeoDistance(
field = field.toString,
point = coordinates,
distance = None,
point = GeoHash.unwrap(point),
distance = distance,
distanceType = None,
queryName = None,
validationMethod = None
Expand All @@ -249,17 +250,20 @@ object ElasticQuery {
* Constructs an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
* @param field
* the field for which query is specified for
* @param coordinates
* longitude and latitude of the desired point written as string (e.g. "40,31") or geo hash (e.g. "drm3btev3e86")
* the field for which the query is specified
* @param point
* the geo-point from which the distance should be measured, defined as geo-hash
* @param distance
* the distance within which values should be matched
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents the `geo_distance` query to be
* performed.
*/
final def geoDistance(field: String, coordinates: String): GeoDistanceQuery[Any] =
final def geoDistance(field: String, point: GeoHash, distance: Distance): GeoDistanceQuery[Any] =
GeoDistance(
field = field,
point = coordinates,
distance = None,
point = GeoHash.unwrap(point),
distance = distance,
distanceType = None,
queryName = None,
validationMethod = None
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2022 LambdaWorks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package zio.elasticsearch.data

import zio.schema.{DeriveSchema, Schema}

final case class GeoPoint(lat: Double, lon: Double)

object GeoPoint {
implicit val schema: Schema.CaseClass2[Double, Double, GeoPoint] = DeriveSchema.gen[GeoPoint]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2022 LambdaWorks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package zio.elasticsearch.query

import zio.prelude.Newtype

object GeoHash extends Newtype[String]
Loading

0 comments on commit b567c7d

Please sign in to comment.