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): Add public data-type GeoPoint and refactor GeoDistanceQuery #277

Merged
merged 2 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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