DATAMONGO-2138 - Polishing.
Rename NestedProperty to KPropertyPath to reflect the underlying concept in alignment with our own PropertyPath type. Rename nestedFieldName(…) method to asString(…) to align with Kotlin method terminology. Reformat. Slightly reword documentation. Add Type-safe Queries for Kotlin to What's New section. Original pull request: #622.
This commit is contained in:
@@ -23,7 +23,7 @@ import kotlin.reflect.KProperty
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0
|
||||
*/
|
||||
fun Criteria.isEqualTo(o: Any?) : Criteria = `is`(o)
|
||||
fun Criteria.isEqualTo(o: Any?): Criteria = `is`(o)
|
||||
|
||||
/**
|
||||
* Extension for [Criteria.in] providing an `inValues` alias since `in` is a reserved keyword in Kotlin.
|
||||
@@ -31,7 +31,7 @@ fun Criteria.isEqualTo(o: Any?) : Criteria = `is`(o)
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0
|
||||
*/
|
||||
fun <T: Any?> Criteria.inValues(c: Collection<T>) : Criteria = `in`(c)
|
||||
fun <T : Any?> Criteria.inValues(c: Collection<T>): Criteria = `in`(c)
|
||||
|
||||
/**
|
||||
* Extension for [Criteria.in] providing an `inValues` alias since `in` is a reserved keyword in Kotlin.
|
||||
@@ -39,19 +39,20 @@ fun <T: Any?> Criteria.inValues(c: Collection<T>) : Criteria = `in`(c)
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0
|
||||
*/
|
||||
fun Criteria.inValues(vararg o: Any?) : Criteria = `in`(*o)
|
||||
fun Criteria.inValues(vararg o: Any?): Criteria = `in`(*o)
|
||||
|
||||
/**
|
||||
* Creates a Criteria using a KProperty as key.
|
||||
* Supports nested field names with [NestedProperty].
|
||||
* Supports nested field names with [KPropertyPath].
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
*/
|
||||
fun where(key: KProperty<*>): Criteria = Criteria.where(nestedFieldName(key))
|
||||
fun where(key: KProperty<*>): Criteria = Criteria.where(asString(key))
|
||||
|
||||
/**
|
||||
* Add new key to the criteria chain using a KProperty.
|
||||
* Supports nested field names with [NestedProperty].
|
||||
* Supports nested field names with [KPropertyPath].
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
*/
|
||||
infix fun Criteria.and(key: KProperty<*>): Criteria = and(nestedFieldName(key))
|
||||
infix fun Criteria.and(key: KProperty<*>): Criteria = and(asString(key))
|
||||
|
||||
@@ -19,29 +19,30 @@ import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
/**
|
||||
* Refer to a field in an embedded/nested document.
|
||||
* Abstraction of a property path consisting of [KProperty].
|
||||
* @author Tjeu Kayim
|
||||
* @author Mark Paluch
|
||||
* @since 2.2
|
||||
*/
|
||||
class NestedProperty<T, U>(
|
||||
internal val parent: KProperty<U>,
|
||||
internal val child: KProperty1<U, T>
|
||||
class KPropertyPath<T, U>(
|
||||
internal val parent: KProperty<U>,
|
||||
internal val child: KProperty1<U, T>
|
||||
) : KProperty<T> by child
|
||||
|
||||
/**
|
||||
* Recursively construct field name for a nested property.
|
||||
* @author Tjeu Kayim
|
||||
*/
|
||||
internal fun nestedFieldName(property: KProperty<*>): String {
|
||||
internal fun asString(property: KProperty<*>): String {
|
||||
return when (property) {
|
||||
is NestedProperty<*, *> ->
|
||||
"${nestedFieldName(property.parent)}.${property.child.name}"
|
||||
is KPropertyPath<*, *> ->
|
||||
"${asString(property.parent)}.${property.child.name}"
|
||||
else -> property.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds [NestedProperty] from Property References.
|
||||
* Builds [KPropertyPath] from Property References.
|
||||
* Refer to a field in an embedded/nested document.
|
||||
*
|
||||
* For example, referring to the field "book.author":
|
||||
@@ -52,4 +53,4 @@ internal fun nestedFieldName(property: KProperty<*>): String {
|
||||
* @since 2.2
|
||||
*/
|
||||
operator fun <T, U> KProperty<T>.div(other: KProperty1<T, U>) =
|
||||
NestedProperty(this, other)
|
||||
KPropertyPath(this, other)
|
||||
@@ -31,7 +31,7 @@ import kotlin.reflect.KProperty
|
||||
* @see Criteria.isEqualTo
|
||||
*/
|
||||
infix fun <T> KProperty<T>.isEqualTo(value: T) =
|
||||
Criteria(nestedFieldName(this)).isEqualTo(value)
|
||||
Criteria(asString(this)).isEqualTo(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $ne operator.
|
||||
@@ -42,7 +42,7 @@ infix fun <T> KProperty<T>.isEqualTo(value: T) =
|
||||
* @see Criteria.ne
|
||||
*/
|
||||
infix fun <T> KProperty<T>.ne(value: T): Criteria =
|
||||
Criteria(nestedFieldName(this)).ne(value)
|
||||
Criteria(asString(this)).ne(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lt operator.
|
||||
@@ -53,7 +53,7 @@ infix fun <T> KProperty<T>.ne(value: T): Criteria =
|
||||
* @see Criteria.lt
|
||||
*/
|
||||
infix fun <T> KProperty<T>.lt(value: T): Criteria =
|
||||
Criteria(nestedFieldName(this)).lt(value)
|
||||
Criteria(asString(this)).lt(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $lte operator.
|
||||
@@ -64,7 +64,7 @@ infix fun <T> KProperty<T>.lt(value: T): Criteria =
|
||||
* @see Criteria.lte
|
||||
*/
|
||||
infix fun <T> KProperty<T>.lte(value: T): Criteria =
|
||||
Criteria(nestedFieldName(this)).lte(value)
|
||||
Criteria(asString(this)).lte(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gt operator.
|
||||
@@ -75,7 +75,7 @@ infix fun <T> KProperty<T>.lte(value: T): Criteria =
|
||||
* @see Criteria.gt
|
||||
*/
|
||||
infix fun <T> KProperty<T>.gt(value: T): Criteria =
|
||||
Criteria(nestedFieldName(this)).gt(value)
|
||||
Criteria(asString(this)).gt(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $gte operator.
|
||||
@@ -86,7 +86,7 @@ infix fun <T> KProperty<T>.gt(value: T): Criteria =
|
||||
* @see Criteria.gte
|
||||
*/
|
||||
infix fun <T> KProperty<T>.gte(value: T): Criteria =
|
||||
Criteria(nestedFieldName(this)).gte(value)
|
||||
Criteria(asString(this)).gte(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $in operator.
|
||||
@@ -97,7 +97,7 @@ infix fun <T> KProperty<T>.gte(value: T): Criteria =
|
||||
* @see Criteria.inValues
|
||||
*/
|
||||
fun <T> KProperty<T>.inValues(vararg o: Any): Criteria =
|
||||
Criteria(nestedFieldName(this)).`in`(*o)
|
||||
Criteria(asString(this)).`in`(*o)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $in operator.
|
||||
@@ -108,7 +108,7 @@ fun <T> KProperty<T>.inValues(vararg o: Any): Criteria =
|
||||
* @see Criteria.inValues
|
||||
*/
|
||||
infix fun <T> KProperty<T>.inValues(value: Collection<T>): Criteria =
|
||||
Criteria(nestedFieldName(this)).`in`(value)
|
||||
Criteria(asString(this)).`in`(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $nin operator.
|
||||
@@ -119,7 +119,7 @@ infix fun <T> KProperty<T>.inValues(value: Collection<T>): Criteria =
|
||||
* @see Criteria.nin
|
||||
*/
|
||||
fun <T> KProperty<T>.nin(vararg o: Any): Criteria =
|
||||
Criteria(nestedFieldName(this)).nin(*o)
|
||||
Criteria(asString(this)).nin(*o)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $nin operator.
|
||||
@@ -130,7 +130,7 @@ fun <T> KProperty<T>.nin(vararg o: Any): Criteria =
|
||||
* @see Criteria.nin
|
||||
*/
|
||||
infix fun <T> KProperty<T>.nin(value: Collection<T>): Criteria =
|
||||
Criteria(nestedFieldName(this)).nin(value)
|
||||
Criteria(asString(this)).nin(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $mod operator.
|
||||
@@ -141,7 +141,7 @@ infix fun <T> KProperty<T>.nin(value: Collection<T>): Criteria =
|
||||
* @see Criteria.mod
|
||||
*/
|
||||
fun KProperty<Number>.mod(value: Number, remainder: Number): Criteria =
|
||||
Criteria(nestedFieldName(this)).mod(value, remainder)
|
||||
Criteria(asString(this)).mod(value, remainder)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $all operator.
|
||||
@@ -152,7 +152,7 @@ fun KProperty<Number>.mod(value: Number, remainder: Number): Criteria =
|
||||
* @see Criteria.all
|
||||
*/
|
||||
fun KProperty<*>.all(vararg o: Any): Criteria =
|
||||
Criteria(nestedFieldName(this)).all(*o)
|
||||
Criteria(asString(this)).all(*o)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $all operator.
|
||||
@@ -163,7 +163,7 @@ fun KProperty<*>.all(vararg o: Any): Criteria =
|
||||
* @see Criteria.all
|
||||
*/
|
||||
infix fun KProperty<*>.all(value: Collection<*>): Criteria =
|
||||
Criteria(nestedFieldName(this)).all(value)
|
||||
Criteria(asString(this)).all(value)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $size operator.
|
||||
@@ -174,7 +174,7 @@ infix fun KProperty<*>.all(value: Collection<*>): Criteria =
|
||||
* @see Criteria.size
|
||||
*/
|
||||
infix fun KProperty<*>.size(s: Int): Criteria =
|
||||
Criteria(nestedFieldName(this)).size(s)
|
||||
Criteria(asString(this)).size(s)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $exists operator.
|
||||
@@ -185,7 +185,7 @@ infix fun KProperty<*>.size(s: Int): Criteria =
|
||||
* @see Criteria.exists
|
||||
*/
|
||||
infix fun KProperty<*>.exists(b: Boolean): Criteria =
|
||||
Criteria(nestedFieldName(this)).exists(b)
|
||||
Criteria(asString(this)).exists(b)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $type operator.
|
||||
@@ -196,7 +196,7 @@ infix fun KProperty<*>.exists(b: Boolean): Criteria =
|
||||
* @see Criteria.type
|
||||
*/
|
||||
infix fun KProperty<*>.type(t: Int): Criteria =
|
||||
Criteria(nestedFieldName(this)).type(t)
|
||||
Criteria(asString(this)).type(t)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $type operator.
|
||||
@@ -207,7 +207,7 @@ infix fun KProperty<*>.type(t: Int): Criteria =
|
||||
* @see Criteria.type
|
||||
*/
|
||||
infix fun KProperty<*>.type(t: Collection<JsonSchemaObject.Type>): Criteria =
|
||||
Criteria(nestedFieldName(this)).type(*t.toTypedArray())
|
||||
Criteria(asString(this)).type(*t.toTypedArray())
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $type operator.
|
||||
@@ -218,7 +218,7 @@ infix fun KProperty<*>.type(t: Collection<JsonSchemaObject.Type>): Criteria =
|
||||
* @see Criteria.type
|
||||
*/
|
||||
fun KProperty<*>.type(vararg t: JsonSchemaObject.Type): Criteria =
|
||||
Criteria(nestedFieldName(this)).type(*t)
|
||||
Criteria(asString(this)).type(*t)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $not meta operator which affects the clause directly following
|
||||
@@ -229,7 +229,7 @@ fun KProperty<*>.type(vararg t: JsonSchemaObject.Type): Criteria =
|
||||
* @see Criteria.not
|
||||
*/
|
||||
fun KProperty<*>.not(): Criteria =
|
||||
Criteria(nestedFieldName(this)).not()
|
||||
Criteria(asString(this)).not()
|
||||
|
||||
/**
|
||||
* Creates a criterion using a $regex operator.
|
||||
@@ -240,7 +240,7 @@ fun KProperty<*>.not(): Criteria =
|
||||
* @see Criteria.regex
|
||||
*/
|
||||
infix fun KProperty<String?>.regex(re: String): Criteria =
|
||||
Criteria(nestedFieldName(this)).regex(re, null)
|
||||
Criteria(asString(this)).regex(re, null)
|
||||
|
||||
/**
|
||||
* Creates a criterion using a $regex and $options operator.
|
||||
@@ -251,7 +251,7 @@ infix fun KProperty<String?>.regex(re: String): Criteria =
|
||||
* @see Criteria.regex
|
||||
*/
|
||||
fun KProperty<String?>.regex(re: String, options: String?): Criteria =
|
||||
Criteria(nestedFieldName(this)).regex(re, options)
|
||||
Criteria(asString(this)).regex(re, options)
|
||||
|
||||
/**
|
||||
* Syntactical sugar for [isEqualTo] making obvious that we create a regex predicate.
|
||||
@@ -260,7 +260,7 @@ fun KProperty<String?>.regex(re: String, options: String?): Criteria =
|
||||
* @see Criteria.regex
|
||||
*/
|
||||
infix fun KProperty<String?>.regex(re: Regex): Criteria =
|
||||
Criteria(nestedFieldName(this)).regex(re.toPattern())
|
||||
Criteria(asString(this)).regex(re.toPattern())
|
||||
|
||||
/**
|
||||
* Syntactical sugar for [isEqualTo] making obvious that we create a regex predicate.
|
||||
@@ -269,7 +269,7 @@ infix fun KProperty<String?>.regex(re: Regex): Criteria =
|
||||
* @see Criteria.regex
|
||||
*/
|
||||
infix fun KProperty<String?>.regex(re: Pattern): Criteria =
|
||||
Criteria(nestedFieldName(this)).regex(re)
|
||||
Criteria(asString(this)).regex(re)
|
||||
|
||||
/**
|
||||
* Syntactical sugar for [isEqualTo] making obvious that we create a regex predicate.
|
||||
@@ -278,35 +278,35 @@ infix fun KProperty<String?>.regex(re: Pattern): Criteria =
|
||||
* @see Criteria.regex
|
||||
*/
|
||||
infix fun KProperty<String?>.regex(re: BsonRegularExpression): Criteria =
|
||||
Criteria(nestedFieldName(this)).regex(re)
|
||||
Criteria(asString(this)).regex(re)
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $geoWithin $centerSphere operation. This is only available for
|
||||
* Mongo 2.4 and higher.
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $geoWithin](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/)
|
||||
* $geoWithin](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/)
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $centerSphere](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/)
|
||||
* $centerSphere](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/)
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
* @see Criteria.withinSphere
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.withinSphere(circle: Circle): Criteria =
|
||||
Criteria(nestedFieldName(this)).withinSphere(circle)
|
||||
Criteria(asString(this)).withinSphere(circle)
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $geoWithin operation.
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $geoWithin](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/)
|
||||
* $geoWithin](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/)
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
* @see Criteria.within
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.within(shape: Shape): Criteria =
|
||||
Criteria(nestedFieldName(this)).within(shape)
|
||||
Criteria(asString(this)).within(shape)
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $near operation.
|
||||
@@ -317,20 +317,20 @@ infix fun KProperty<GeoJson<*>>.within(shape: Shape): Criteria =
|
||||
* @see Criteria.near
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.near(point: Point): Criteria =
|
||||
Criteria(nestedFieldName(this)).near(point)
|
||||
Criteria(asString(this)).near(point)
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $nearSphere operation. This is only available for Mongo 1.7 and
|
||||
* higher.
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $nearSphere](https://docs.mongodb.com/manual/reference/operator/query/nearSphere/)
|
||||
* $nearSphere](https://docs.mongodb.com/manual/reference/operator/query/nearSphere/)
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
* @see Criteria.nearSphere
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.nearSphere(point: Point): Criteria =
|
||||
Criteria(nestedFieldName(this)).nearSphere(point)
|
||||
Criteria(asString(this)).nearSphere(point)
|
||||
|
||||
/**
|
||||
* Creates criterion using `$geoIntersects` operator which matches intersections of the given `geoJson`
|
||||
@@ -340,19 +340,19 @@ infix fun KProperty<GeoJson<*>>.nearSphere(point: Point): Criteria =
|
||||
* @see Criteria.intersects
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.intersects(geoJson: GeoJson<*>): Criteria =
|
||||
Criteria(nestedFieldName(this)).intersects(geoJson)
|
||||
Criteria(asString(this)).intersects(geoJson)
|
||||
|
||||
/**
|
||||
* Creates a geo-spatial criterion using a $maxDistance operation, for use with $near
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $maxDistance](https://docs.mongodb.com/manual/reference/operator/query/maxDistance/)
|
||||
* $maxDistance](https://docs.mongodb.com/manual/reference/operator/query/maxDistance/)
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
* @see Criteria.maxDistance
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.maxDistance(d: Double): Criteria =
|
||||
Criteria(nestedFieldName(this)).maxDistance(d)
|
||||
Criteria(asString(this)).maxDistance(d)
|
||||
|
||||
/**
|
||||
* Creates a geospatial criterion using a $minDistance operation, for use with $near or
|
||||
@@ -362,19 +362,19 @@ infix fun KProperty<GeoJson<*>>.maxDistance(d: Double): Criteria =
|
||||
* @see Criteria.minDistance
|
||||
*/
|
||||
infix fun KProperty<GeoJson<*>>.minDistance(d: Double): Criteria =
|
||||
Criteria(nestedFieldName(this)).minDistance(d)
|
||||
Criteria(asString(this)).minDistance(d)
|
||||
|
||||
/**
|
||||
* Creates a criterion using the $elemMatch operator
|
||||
*
|
||||
* See [MongoDB Query operator:
|
||||
* $elemMatch](https://docs.mongodb.com/manual/reference/operator/query/elemMatch/)
|
||||
* $elemMatch](https://docs.mongodb.com/manual/reference/operator/query/elemMatch/)
|
||||
* @author Tjeu Kayim
|
||||
* @since 2.2
|
||||
* @see Criteria.elemMatch
|
||||
*/
|
||||
infix fun KProperty<*>.elemMatch(c: Criteria): Criteria =
|
||||
Criteria(nestedFieldName(this)).elemMatch(c)
|
||||
Criteria(asString(this)).elemMatch(c)
|
||||
|
||||
/**
|
||||
* Use [Criteria.BitwiseCriteriaOperators] as gateway to create a criterion using one of the
|
||||
@@ -390,4 +390,4 @@ infix fun KProperty<*>.elemMatch(c: Criteria): Criteria =
|
||||
* @see Criteria.bits
|
||||
*/
|
||||
infix fun KProperty<*>.bits(bitwiseCriteria: Criteria.BitwiseCriteriaOperators.() -> Criteria) =
|
||||
Criteria(nestedFieldName(this)).bits().let(bitwiseCriteria)
|
||||
Criteria(asString(this)).bits().let(bitwiseCriteria)
|
||||
|
||||
@@ -21,12 +21,12 @@ import org.junit.Test
|
||||
/**
|
||||
* @author Tjeu Kayim
|
||||
*/
|
||||
class NestedPropertyTests {
|
||||
class KPropertyPathTests {
|
||||
|
||||
@Test
|
||||
fun `Convert normal KProperty to field name`() {
|
||||
|
||||
val property = nestedFieldName(Book::title)
|
||||
val property = asString(Book::title)
|
||||
|
||||
assertThat(property).isEqualTo("title")
|
||||
}
|
||||
@@ -34,7 +34,7 @@ class NestedPropertyTests {
|
||||
@Test
|
||||
fun `Convert nested KProperty to field name`() {
|
||||
|
||||
val property = nestedFieldName(Book::author / Author::name)
|
||||
val property = asString(Book::author / Author::name)
|
||||
|
||||
assertThat(property).isEqualTo("author.name")
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class NestedPropertyTests {
|
||||
|
||||
class Entity(val book: Book)
|
||||
|
||||
val property = nestedFieldName(Entity::book / Book::author / Author::name)
|
||||
val property = asString(Entity::book / Book::author / Author::name)
|
||||
|
||||
assertThat(property).isEqualTo("book.author.name")
|
||||
}
|
||||
@@ -55,11 +55,11 @@ class NestedPropertyTests {
|
||||
class Entity(val book: Book)
|
||||
class AnotherEntity(val entity: Entity)
|
||||
|
||||
val property = nestedFieldName(AnotherEntity::entity / Entity::book / Book::author / Author::name)
|
||||
val property = asString(AnotherEntity::entity / Entity::book / Book::author / Author::name)
|
||||
|
||||
assertThat(property).isEqualTo("entity.book.author.name")
|
||||
}
|
||||
|
||||
class Book(val title: String, val author: Author)
|
||||
class Author(val name: String)
|
||||
}
|
||||
}
|
||||
@@ -15,361 +15,396 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query
|
||||
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.bson.BsonRegularExpression
|
||||
import org.junit.Test
|
||||
import org.springframework.data.geo.Circle
|
||||
import org.springframework.data.geo.Point
|
||||
import org.springframework.data.mongodb.core.geo.GeoJsonPoint
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.*
|
||||
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* @author Tjeu Kayim
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class TypedCriteriaExtensionsTests {
|
||||
|
||||
@Test
|
||||
fun `isEqualTo() should equal classic criteria`() {
|
||||
fun `isEqualTo() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title isEqualTo "Moby-Dick"
|
||||
val classic = Criteria("title").isEqualTo("Moby-Dick")
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").isEqualTo("Moby-Dick")
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ne() should equal classic criteria`() {
|
||||
fun `ne() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title ne "Moby-Dick"
|
||||
val classic = Criteria("title").ne("Moby-Dick")
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").ne("Moby-Dick")
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lt() should equal classic criteria`() {
|
||||
fun `lt() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price lt 100
|
||||
val classic = Criteria("price").lt(100)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").lt(100)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lte() should equal classic criteria`() {
|
||||
fun `lte() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price lte 100
|
||||
val classic = Criteria("price").lte(100)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").lte(100)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `gt() should equal classic criteria`() {
|
||||
fun `gt() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price gt 100
|
||||
val classic = Criteria("price").gt(100)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").gt(100)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `gte() should equal classic criteria`() {
|
||||
fun `gte() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price gte 100
|
||||
val classic = Criteria("price").gte(100)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").gte(100)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `inValues(vararg) should equal classic criteria`() {
|
||||
fun `inValues(vararg) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price.inValues(1, 2, 3)
|
||||
val classic = Criteria("price").inValues(1, 2, 3)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").inValues(1, 2, 3)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `inValues(list) should equal classic criteria`() {
|
||||
fun `inValues(list) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price inValues listOf(1, 2, 3)
|
||||
val classic = Criteria("price").inValues(listOf(1, 2, 3))
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").inValues(listOf(1, 2, 3))
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nin(vararg) should equal classic criteria`() {
|
||||
fun `nin(vararg) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price.nin(1, 2, 3)
|
||||
val classic = Criteria("price").nin(1, 2, 3)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").nin(1, 2, 3)
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nin(list) should equal classic criteria`() {
|
||||
fun `nin(list) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price nin listOf(1, 2, 3)
|
||||
val classic = Criteria("price").nin(listOf(1, 2, 3))
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").nin(listOf(1, 2, 3))
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mod() should equal classic criteria`() {
|
||||
fun `mod() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price.mod(2, 3)
|
||||
val classic = Criteria("price").mod(2, 3)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").mod(2, 3)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all(vararg) should equal classic criteria`() {
|
||||
fun `all(vararg) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::categories.all(1, 2, 3)
|
||||
val classic = Criteria("categories").all(1, 2, 3)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("categories").all(1, 2, 3)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all(list) should equal classic criteria`() {
|
||||
fun `all(list) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::categories all listOf(1, 2, 3)
|
||||
val classic = Criteria("categories").all(listOf(1, 2, 3))
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("categories").all(listOf(1, 2, 3))
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `size() should equal classic criteria`() {
|
||||
fun `size() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::categories size 4
|
||||
val classic = Criteria("categories").size(4)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("categories").size(4)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exists() should equal classic criteria`() {
|
||||
fun `exists() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title exists true
|
||||
val classic = Criteria("title").exists(true)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").exists(true)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `type(Int) should equal classic criteria`() {
|
||||
fun `type(Int) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title type 2
|
||||
val classic = Criteria("title").type(2)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").type(2)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `type(List) should equal classic criteria`() {
|
||||
fun `type(List) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title type listOf(Type.STRING, Type.BOOLEAN)
|
||||
val classic = Criteria("title").type(Type.STRING, Type.BOOLEAN)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").type(Type.STRING, Type.BOOLEAN)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `type(vararg) should equal classic criteria`() {
|
||||
fun `type(vararg) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title.type(Type.STRING, Type.BOOLEAN)
|
||||
val classic = Criteria("title").type(Type.STRING, Type.BOOLEAN)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").type(Type.STRING, Type.BOOLEAN)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `not() should equal classic criteria`() {
|
||||
fun `not() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::price.not().lt(123)
|
||||
val classic = Criteria("price").not().lt(123)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("price").not().lt(123)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `regex(string) should equal classic criteria`() {
|
||||
fun `regex(string) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title regex "ab+c"
|
||||
val classic = Criteria("title").regex("ab+c")
|
||||
assertEqualCriteriaByJson(typed, classic)
|
||||
val expected = Criteria("title").regex("ab+c")
|
||||
assertEqualCriteriaByJson(typed, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `regex(string, options) should equal classic criteria`() {
|
||||
fun `regex(string, options) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title.regex("ab+c", "g")
|
||||
val classic = Criteria("title").regex("ab+c", "g")
|
||||
assertEqualCriteriaByJson(typed, classic)
|
||||
val expected = Criteria("title").regex("ab+c", "g")
|
||||
|
||||
assertEqualCriteriaByJson(typed, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `regex(Regex) should equal classic criteria`() {
|
||||
fun `regex(Regex) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title regex Regex("ab+c")
|
||||
val classic = Criteria("title").regex(Pattern.compile("ab+c"))
|
||||
assertEqualCriteriaByJson(typed, classic)
|
||||
val expected = Criteria("title").regex(Pattern.compile("ab+c"))
|
||||
|
||||
assertEqualCriteriaByJson(typed, expected)
|
||||
}
|
||||
|
||||
private fun assertEqualCriteriaByJson(typed: Criteria, classic: Criteria) {
|
||||
assertThat(typed.criteriaObject.toJson()).isEqualTo(classic.criteriaObject.toJson())
|
||||
private fun assertEqualCriteriaByJson(typed: Criteria, expected: Criteria) {
|
||||
assertThat(typed.criteriaObject.toJson()).isEqualTo(expected.criteriaObject.toJson())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `regex(Pattern) should equal classic criteria`() {
|
||||
fun `regex(Pattern) should equal expected criteria`() {
|
||||
|
||||
val value = Pattern.compile("ab+c")
|
||||
val typed = Book::title regex value
|
||||
val classic = Criteria("title").regex(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").regex(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `regex(BsonRegularExpression) should equal classic criteria`() {
|
||||
fun `regex(BsonRegularExpression) should equal expected criteria`() {
|
||||
|
||||
val expression = BsonRegularExpression("ab+c")
|
||||
val typed = Book::title regex expression
|
||||
val classic = Criteria("title").regex(expression)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").regex(expression)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `withinSphere() should equal classic criteria`() {
|
||||
fun `withinSphere() should equal expected criteria`() {
|
||||
|
||||
val value = Circle(Point(928.76, 28.345), 65.243)
|
||||
val typed = Building::location withinSphere value
|
||||
val classic = Criteria("location").withinSphere(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").withinSphere(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `within() should equal classic criteria`() {
|
||||
fun `within() should equal expected criteria`() {
|
||||
|
||||
val value = Circle(Point(5.43421, 12.456), 52.67)
|
||||
val typed = Building::location within value
|
||||
val classic = Criteria("location").within(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").within(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `near() should equal classic criteria`() {
|
||||
fun `near() should equal expected criteria`() {
|
||||
|
||||
val value = Point(57.431, 71.345)
|
||||
val typed = Building::location near value
|
||||
val classic = Criteria("location").near(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").near(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nearSphere() should equal classic criteria`() {
|
||||
fun `nearSphere() should equal expected criteria`() {
|
||||
|
||||
val value = Point(5.4321, 12.345)
|
||||
val typed = Building::location nearSphere value
|
||||
val classic = Criteria("location").nearSphere(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").nearSphere(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `intersects() should equal classic criteria`() {
|
||||
fun `intersects() should equal expected criteria`() {
|
||||
|
||||
val value = GeoJsonPoint(5.481573, 51.451726)
|
||||
val typed = Building::location intersects value
|
||||
val classic = Criteria("location").intersects(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").intersects(value)
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `maxDistance() should equal classic criteria`() {
|
||||
fun `maxDistance() should equal expected criteria`() {
|
||||
|
||||
val typed = Building::location maxDistance 3.0
|
||||
val classic = Criteria("location").maxDistance(3.0)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").maxDistance(3.0)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `minDistance() should equal classic criteria`() {
|
||||
fun `minDistance() should equal expected criteria`() {
|
||||
|
||||
val typed = Building::location minDistance 3.0
|
||||
val classic = Criteria("location").minDistance(3.0)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("location").minDistance(3.0)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `elemMatch() should equal classic criteria`() {
|
||||
fun `elemMatch() should equal expected criteria`() {
|
||||
|
||||
val value = Criteria("price").lt(950)
|
||||
val typed = Book::title elemMatch value
|
||||
val classic = Criteria("title").elemMatch(value)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").elemMatch(value)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `elemMatch(TypedCriteria) should equal classic criteria`() {
|
||||
fun `elemMatch(TypedCriteria) should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title elemMatch (Book::price lt 950)
|
||||
val classic = Criteria("title").elemMatch(Criteria("price").lt(950))
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").elemMatch(Criteria("price").lt(950))
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bits() should equal classic criteria`() {
|
||||
fun `bits() should equal expected criteria`() {
|
||||
|
||||
val typed = Book::title bits { allClear(123) }
|
||||
val classic = Criteria("title").bits().allClear(123)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").bits().allClear(123)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `One level nested should equal classic criteria`() {
|
||||
fun `One level nested should equal expected criteria`() {
|
||||
|
||||
val typed = Book::author / Author::name isEqualTo "Herman Melville"
|
||||
val expected = Criteria("author.name").isEqualTo("Herman Melville")
|
||||
|
||||
val classic = Criteria("author.name").isEqualTo("Herman Melville")
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Two levels nested should equal classic criteria`() {
|
||||
fun `Two levels nested should equal expected criteria`() {
|
||||
|
||||
data class Entity(val book: Book)
|
||||
|
||||
val typed = Entity::book / Book::author / Author::name isEqualTo "Herman Melville"
|
||||
val classic = Criteria("book.author.name").isEqualTo("Herman Melville")
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("book.author.name").isEqualTo("Herman Melville")
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `typed criteria inside orOperator() should equal classic criteria`() {
|
||||
fun `typed criteria inside orOperator() should equal expected criteria`() {
|
||||
|
||||
val typed = (Book::title isEqualTo "Moby-Dick").orOperator(
|
||||
Book::price lt 1200,
|
||||
Book::price gt 240
|
||||
Book::price lt 1200,
|
||||
Book::price gt 240
|
||||
)
|
||||
val classic = Criteria("title").isEqualTo("Moby-Dick")
|
||||
.orOperator(
|
||||
Criteria("price").lt(1200),
|
||||
Criteria("price").gt(240)
|
||||
)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
val expected = Criteria("title").isEqualTo("Moby-Dick")
|
||||
.orOperator(
|
||||
Criteria("price").lt(1200),
|
||||
Criteria("price").gt(240)
|
||||
)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `chaining gt & isEqualTo() should equal classic criteria`() {
|
||||
fun `chaining gt & isEqualTo() should equal expected criteria`() {
|
||||
|
||||
val typed = (Book::title isEqualTo "Moby-Dick")
|
||||
.and(Book::price).lt(950)
|
||||
val classic = Criteria("title").isEqualTo("Moby-Dick")
|
||||
.and("price").lt(950)
|
||||
assertThat(typed).isEqualTo(classic)
|
||||
.and(Book::price).lt(950)
|
||||
val expected = Criteria("title").isEqualTo("Moby-Dick")
|
||||
.and("price").lt(950)
|
||||
|
||||
assertThat(typed).isEqualTo(expected)
|
||||
}
|
||||
|
||||
data class Book(
|
||||
val title: String = "Moby-Dick",
|
||||
val price: Int = 123,
|
||||
val available: Boolean = true,
|
||||
val categories: List<String> = emptyList(),
|
||||
val author: Author = Author()
|
||||
val title: String = "Moby-Dick",
|
||||
val price: Int = 123,
|
||||
val available: Boolean = true,
|
||||
val categories: List<String> = emptyList(),
|
||||
val author: Author = Author()
|
||||
)
|
||||
|
||||
data class Author(
|
||||
val name: String = "Herman Melville"
|
||||
val name: String = "Herman Melville"
|
||||
)
|
||||
|
||||
data class Building(
|
||||
val location: GeoJsonPoint
|
||||
val location: GeoJsonPoint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
[[new-features]]
|
||||
= New & Noteworthy
|
||||
|
||||
[[new-features.2-2-0]]
|
||||
== What's New in Spring Data MongoDB 2.2
|
||||
* <<mongo.query.kotlin-support,Type-safe Queries for Kotlin>>
|
||||
|
||||
[[new-features.2-1-0]]
|
||||
== What's New in Spring Data MongoDB 2.1
|
||||
|
||||
|
||||
@@ -1795,10 +1795,12 @@ GeoResults<Jedi> results = mongoOps.query(SWCharacter.class)
|
||||
[[mongo.query.kotlin-support]]
|
||||
=== Type-safe Queries for Kotlin
|
||||
|
||||
https://kotlinlang.org/docs/reference/reflection.html#property-references[Kotlin property references]
|
||||
can be used to build type-safe queries.
|
||||
Kotlin embraces domain-specific language creation through its language syntax and its extension system.
|
||||
Spring Data MongoDB ships with a Kotlin Extension for `Criteria` using https://kotlinlang.org/docs/reference/reflection.html#property-references[Kotlin property references] to build type-safe queries.
|
||||
Queries using this extension are typically benefit from improved readability.
|
||||
Most keywords on `Criteria` have a matching Kotlin extension, such as `inValues` and `regex`.
|
||||
|
||||
Most methods in `Criteria` have a matching Kotlin extension, like `inValues` and `regex`.
|
||||
Consider the following example explaining Type-safe Queries:
|
||||
|
||||
====
|
||||
[source,kotlin]
|
||||
@@ -1806,21 +1808,29 @@ Most methods in `Criteria` have a matching Kotlin extension, like `inValues` and
|
||||
import org.springframework.data.mongodb.core.query.*
|
||||
|
||||
mongoOperations.find<Book>(
|
||||
Query(Book::title isEqualTo "Moby-Dick") <1>
|
||||
Query(Book::title isEqualTo "Moby-Dick") <1>
|
||||
)
|
||||
|
||||
Book::title exists true
|
||||
mongoOperations.find<Book>(
|
||||
Query(titlePredicate = Book::title exists true)
|
||||
)
|
||||
|
||||
Criteria().andOperator(
|
||||
Book::price gt 5,
|
||||
Book::price lt 10
|
||||
mongoOperations.find<Book>(
|
||||
Criteria().andOperator(
|
||||
Book::price gt 5,
|
||||
Book::price lt 10
|
||||
)
|
||||
)
|
||||
|
||||
// Binary operators
|
||||
BinaryMessage::payload bits { allClear(0b101) } <2>
|
||||
mongoOperations.find<BinaryMessage>(
|
||||
Query(BinaryMessage::payload bits { allClear(0b101) }) <2>
|
||||
)
|
||||
|
||||
// Nested Properties (i.e. refer to "book.author")
|
||||
Book::author / Author::name regex "^H" <3>
|
||||
mongoOperations.find<Book>(
|
||||
Query(Book::author / Author::name regex "^H") <3>
|
||||
)
|
||||
----
|
||||
<1> `isEqualTo()` is an infix extension function with receiver type `KProperty<T>` that returns `Criteria`.
|
||||
<2> For bitwise operators, pass a lambda argument where you call one of the methods of `Criteria.BitwiseCriteriaOperators`.
|
||||
|
||||
Reference in New Issue
Block a user