Skip to content

Commit

Permalink
Add integration tests and fix code remarks
Browse files Browse the repository at this point in the history
  • Loading branch information
markaya committed May 10, 2023
1 parent 939f0a1 commit c919276
Show file tree
Hide file tree
Showing 10 changed files with 301 additions and 206 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import zio.elasticsearch.ElasticSort.sortBy
import zio.elasticsearch.domain.{PartialTestDocument, TestDocument, TestSubDocument}
import zio.elasticsearch.executor.Executor
import zio.elasticsearch.executor.response.{CardinalityAggregationResponse, MaxAggregationResponse}
import zio.elasticsearch.query.DistanceUnit.Kilometers
import zio.elasticsearch.query.sort.SortMode.Max
import zio.elasticsearch.query.sort.SortOrder._
import zio.elasticsearch.query.sort.SourceType.NumberType
Expand All @@ -33,9 +34,9 @@ import zio.elasticsearch.script.Script
import zio.json.ast.Json.{Arr, Str}
import zio.schema.codec.JsonCodec
import zio.stream.{Sink, ZSink}
import zio.test._
import zio.test.TestAspect._
import zio.test.Assertion._
import zio.test.TestAspect._
import zio.test._

import java.time.LocalDate
import scala.util.Random
Expand Down Expand Up @@ -1577,6 +1578,44 @@ object HttpExecutorSpec extends IntegrationSpec {
) && assert(doc)(isSome(equalTo(newDocument.copy(intField = newDocument.intField + 1))))
}
}
),
suite("geo-distance query")(
test("using geo-distance query") {
checkOnce(genTestDocument) { document =>
val indexDefinition =
"""
|{
| "mappings": {
| "properties": {
| "locationField": {
| "type": "geo_point"
| }
| }
| }
|}
|""".stripMargin

for {
_ <- Executor.execute(ElasticRequest.createIndex(geoDistanceIndex, indexDefinition))
_ <- Executor.execute(ElasticRequest.deleteByQuery(geoDistanceIndex, matchAll))
_ <- Executor.execute(
ElasticRequest.create[TestDocument](geoDistanceIndex, document).refreshTrue
)
r <- Executor
.execute(
ElasticRequest.search(
geoDistanceIndex,
ElasticQuery
.geoDistance("locationField", document.locationField.lat, document.locationField.lon)
.distance(300, Kilometers)
)
)
.documentAs[TestDocument]
} yield assert(r)(
equalTo(List(document))
)
}
} @@ after(Executor.execute(ElasticRequest.deleteIndex(geoDistanceIndex)).orDie)
)
) @@ nondeterministic @@ sequential @@ prepareElasticsearchIndexForTests @@ afterAll(
Executor.execute(ElasticRequest.deleteIndex(index)).orDie
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ trait IntegrationSpec extends ZIOSpecDefault {

val updateByQueryIndex: IndexName = IndexName("update-by-query-index")

val geoDistanceIndex = IndexName("geo-distance-index")

val prepareElasticsearchIndexForTests: TestAspect[Nothing, Any, Throwable, Any] = beforeAll((for {
_ <- Executor.execute(ElasticRequest.createIndex(index))
_ <- Executor.execute(ElasticRequest.deleteByQuery(index, matchAll).refreshTrue)
Expand All @@ -60,20 +62,28 @@ trait IntegrationSpec extends ZIOSpecDefault {
def genDocumentId: Gen[Any, DocumentId] =
Gen.stringBounded(10, 40)(Gen.alphaNumericChar).map(DocumentId(_))

def genLocation: Gen[Any, Location] =
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)

def genTestDocument: Gen[Any, TestDocument] = for {
stringField <- Gen.stringBounded(5, 10)(Gen.alphaChar)
dateField <- Gen.localDate(LocalDate.parse("2010-12-02"), LocalDate.parse("2022-12-05"))
subDocumentList <- Gen.listOfBounded(1, 3)(genTestSubDocument)
intField <- Gen.int(1, 2000)
doubleField <- Gen.double(100, 2000)
booleanField <- Gen.boolean
locationField <- genLocation
} yield TestDocument(
stringField = stringField,
dateField = dateField,
subDocumentList = subDocumentList,
intField = intField,
doubleField = doubleField,
booleanField = booleanField
booleanField = booleanField,
locationField = locationField
)

def genTestSubDocument: Gen[Any, TestSubDocument] = for {
Expand Down
20 changes: 10 additions & 10 deletions modules/library/src/main/scala/zio/elasticsearch/ElasticQuery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ object ElasticQuery {
Bool[Any](filter = queries.toList, must = Nil, mustNot = Nil, should = Nil, boost = None, minimumShouldMatch = None)

/**
* Constructs an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
* 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
Expand Down Expand Up @@ -147,34 +147,34 @@ object ElasticQuery {
GeoDistance(field = field, point = Left((longitude, latitude)))

/**
* Constructs an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
* 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 longitudeAndLatitude
* longitude and latitude of desired point written as string(e.g. "40,31") or geo hash (e.g. "drm3btev3e86")
* @param coordinates
* longitude and latitude of desired point written as string (e.g. "40,31") or geo hash (e.g. "drm3btev3e86")
* @tparam S
* document for which field query is executed
* @tparam A
* the type of value to be matched. A JSON decoder must be in scope for this type
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
*/
final def geoDistance[S, A: ElasticPrimitive](field: Field[S, A], longitudeAndLatitude: String): GeoDistanceQuery[S] =
GeoDistance(field = field.toString, point = Right(longitudeAndLatitude))
final def geoDistance[S, A: ElasticPrimitive](field: Field[S, A], coordinates: String): GeoDistanceQuery[S] =
GeoDistance(field = field.toString, point = Right(coordinates))

/**
* Constructs an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] using the specified parameters.
*
* @param field
* the field for which query is specified for
* @param longitudeAndLatitude
* longitude and latitude of desired point written as string(e.g. "40,31") or geo hash (e.g. "drm3btev3e86")
* @param coordinates
* longitude and latitude of desired point written as string (e.g. "40,31") or geo hash (e.g. "drm3btev3e86")
* @return
* an instance of [[zio.elasticsearch.query.GeoDistanceQuery]] that represents `geo_distance` query to be performed.
*/
final def geoDistance(field: String, longitudeAndLatitude: String): GeoDistanceQuery[Any] =
GeoDistance(field = field, point = Right(longitudeAndLatitude))
final def geoDistance(field: String, coordinates: String): GeoDistanceQuery[Any] =
GeoDistance(field = field, point = Right(coordinates))

/**
* Constructs an instance of [[zio.elasticsearch.query.HasChildQuery]] using the specified parameters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ final case class Distance(distanceValue: Double, distanceUnit: DistanceUnit) {
}

sealed trait DistanceUnit {
def value: String
override def toString: String = value
def symbol: String
override def toString: String = symbol
}

object DistanceUnit {
case object Centimeter extends DistanceUnit { def value: String = "cm" }
case object Feet extends DistanceUnit { def value: String = "ft" }
case object Inch extends DistanceUnit { def value: String = "in" }
case object Kilometers extends DistanceUnit { def value: String = "km" }
case object Mile extends DistanceUnit { def value: String = "mi" }
case object Meter extends DistanceUnit { def value: String = "m" }
case object Milimeter extends DistanceUnit { def value: String = "mm" }
case object NauticalMile extends DistanceUnit { def value: String = "nmi" }
case object Yard extends DistanceUnit { def value: String = "yd" }
case object Centimeter extends DistanceUnit { def symbol: String = "cm" }
case object Feet extends DistanceUnit { def symbol: String = "ft" }
case object Inch extends DistanceUnit { def symbol: String = "in" }
case object Kilometers extends DistanceUnit { def symbol: String = "km" }
case object Mile extends DistanceUnit { def symbol: String = "mi" }
case object Meter extends DistanceUnit { def symbol: String = "m" }
case object Milimeter extends DistanceUnit { def symbol: String = "mm" }
case object NauticalMile extends DistanceUnit { def symbol: String = "nmi" }
case object Yard extends DistanceUnit { def symbol: String = "yd" }
}

sealed trait DistanceType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,14 @@ private[elasticsearch] final case class Exists[S](field: String) extends ExistsQ
sealed trait GeoDistanceQuery[S] extends ElasticQuery[S] {

/**
* sets the `distance` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]]
* Sets the `distance` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]].
*
* `Distance` represents the radius of the circle centred on the specified location. Points which fall into this
* circle are considered to be matches. The distance can be specified in various units. See
* [[zio.elasticsearch.query.DistanceUnit]].
*
* @param value
* the [[Double]] value for distance parameter
* the [[Double]] value for a non-negative real number used for distance
* @param unit
* the [[zio.elasticsearch.query.DistanceUnit]] in which we want to represent the distance
* @return
Expand All @@ -129,20 +129,21 @@ sealed trait GeoDistanceQuery[S] extends ElasticQuery[S] {
def distance(value: Double, unit: DistanceUnit): GeoDistanceQuery[S]

/**
* sets the `distanceType` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]]
* Sets the `distanceType` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]].
*
* Defines how to compute the distance. Can either be [[zio.elasticsearch.query.DistanceType.Arc]] (default), or
* [[zio.elasticsearch.query.DistanceType.Plane]] (faster, but inaccurate on long distances and close to the poles).
* Defines how to compute the distance.
*
* @param value
* the [[zio.elasticsearch.query.DistanceType]] value to represent distance type
* defines how to compute the distance
* - [[zio.elasticsearch.query.DistanceType.Arc]]: Default algorithm
* - [[zio.elasticsearch.query.DistanceType.Plane]]: Faster, but inaccurate on long distances and close to the poles
* @return
* a new instance of the [[zio.elasticsearch.query.GeoDistanceQuery]] with the score value set.
*/
def distanceType(value: DistanceType): GeoDistanceQuery[S]

/**
* sets the `queryName` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]]
* Sets the `queryName` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]].
*
* Represents the optional name field to identify the query
*
Expand All @@ -154,14 +155,16 @@ sealed trait GeoDistanceQuery[S] extends ElasticQuery[S] {
def name(value: String): GeoDistanceQuery[S]

/**
* sets the `validationMethod` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]]
* Sets the `validationMethod` parameter for the [[zio.elasticsearch.query.GeoDistanceQuery]].
*
* Set to [[zio.elasticsearch.query.ValidationMethod.IgnoreMalformed]] to accept geo points with invalid latitude or
* longitude, set to [[zio.elasticsearch.query.ValidationMethod.Coerce]] to additionally try and infer correct
* coordinates (default is [[zio.elasticsearch.query.ValidationMethod.Strict]]).
* Defines handling of incorrect coordinates.
*
* @param value
* the [[zio.elasticsearch.query.ValidationMethod]] value to represent the validationMethod field
* defines how to handle invalid latitude nad longitude:
* - [[zio.elasticsearch.query.ValidationMethod.Strict]]: Default method
* - [[zio.elasticsearch.query.ValidationMethod.IgnoreMalformed]]: Accepts geo points with invalid latitude or
* longitude
* - [[zio.elasticsearch.query.ValidationMethod.Coerce]]: Additionally try and infer correct coordinates
* @return
* a new instance of the [[zio.elasticsearch.query.GeoDistanceQuery]] with the score value set.
*/
Expand All @@ -182,18 +185,16 @@ private[elasticsearch] final case class GeoDistance[S](

def distanceType(value: DistanceType): GeoDistanceQuery[S] = self.copy(distanceType = Some(value))

private def getPointJson: (String, Json) = point match {
case Left((lat, lon)) => field -> Obj("lat" -> Num(lat), "lon" -> Num(lon))
case Right(stringValue) => field -> Str(stringValue)
}

def name(value: String): GeoDistanceQuery[S] = self.copy(queryName = Some(value))

def paramsToJson(fieldPath: Option[String]): Json =
Obj(
"geo_distance" -> Obj(
Chunk(
Some(getPointJson),
point match {
case Left((lat, lon)) => Some(field -> Obj("lat" -> Num(lat), "lon" -> Num(lon)))
case Right(stringValue) => Some(field -> Str(stringValue))
},
distance.map(d => "distance" -> Str(d.toString)),
distanceType.map(dt => "distance_type" -> Str(dt.toString)),
queryName.map(qn => "_name" -> Str(qn)),
Expand Down
Loading

0 comments on commit c919276

Please sign in to comment.