diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensions.kt index a36deec24..815b24d22 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensions.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/CriteriaExtensions.kt @@ -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 Criteria.inValues(c: Collection) : Criteria = `in`(c) +fun Criteria.inValues(c: Collection): Criteria = `in`(c) /** * Extension for [Criteria.in] providing an `inValues` alias since `in` is a reserved keyword in Kotlin. @@ -39,19 +39,20 @@ fun Criteria.inValues(c: Collection) : 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)) diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/NestedProperty.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/KPropertyPath.kt similarity index 75% rename from spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/NestedProperty.kt rename to spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/KPropertyPath.kt index 3fba3cf3a..48f4ab124 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/NestedProperty.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/KPropertyPath.kt @@ -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( - internal val parent: KProperty, - internal val child: KProperty1 +class KPropertyPath( + internal val parent: KProperty, + internal val child: KProperty1 ) : KProperty 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 KProperty.div(other: KProperty1) = - NestedProperty(this, other) + KPropertyPath(this, other) diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt index 6e7654aef..39d7151b4 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt @@ -31,7 +31,7 @@ import kotlin.reflect.KProperty * @see Criteria.isEqualTo */ infix fun KProperty.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 KProperty.isEqualTo(value: T) = * @see Criteria.ne */ infix fun KProperty.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 KProperty.ne(value: T): Criteria = * @see Criteria.lt */ infix fun KProperty.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 KProperty.lt(value: T): Criteria = * @see Criteria.lte */ infix fun KProperty.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 KProperty.lte(value: T): Criteria = * @see Criteria.gt */ infix fun KProperty.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 KProperty.gt(value: T): Criteria = * @see Criteria.gte */ infix fun KProperty.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 KProperty.gte(value: T): Criteria = * @see Criteria.inValues */ fun KProperty.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 KProperty.inValues(vararg o: Any): Criteria = * @see Criteria.inValues */ infix fun KProperty.inValues(value: Collection): Criteria = - Criteria(nestedFieldName(this)).`in`(value) + Criteria(asString(this)).`in`(value) /** * Creates a criterion using the $nin operator. @@ -119,7 +119,7 @@ infix fun KProperty.inValues(value: Collection): Criteria = * @see Criteria.nin */ fun KProperty.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 KProperty.nin(vararg o: Any): Criteria = * @see Criteria.nin */ infix fun KProperty.nin(value: Collection): Criteria = - Criteria(nestedFieldName(this)).nin(value) + Criteria(asString(this)).nin(value) /** * Creates a criterion using the $mod operator. @@ -141,7 +141,7 @@ infix fun KProperty.nin(value: Collection): Criteria = * @see Criteria.mod */ fun KProperty.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.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): 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): 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.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.regex(re: String): Criteria = * @see Criteria.regex */ fun KProperty.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.regex(re: String, options: String?): Criteria = * @see Criteria.regex */ infix fun KProperty.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.regex(re: Regex): Criteria = * @see Criteria.regex */ infix fun KProperty.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.regex(re: Pattern): Criteria = * @see Criteria.regex */ infix fun KProperty.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>.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>.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>.within(shape: Shape): Criteria = * @see Criteria.near */ infix fun KProperty>.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>.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>.nearSphere(point: Point): Criteria = * @see Criteria.intersects */ infix fun KProperty>.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>.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>.maxDistance(d: Double): Criteria = * @see Criteria.minDistance */ infix fun KProperty>.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) diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/NestedPropertyTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt similarity index 81% rename from spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/NestedPropertyTests.kt rename to spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt index 40f4fd8c6..1836c952c 100644 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/NestedPropertyTests.kt +++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/KPropertyPathTests.kt @@ -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) -} \ No newline at end of file +} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt index 75ae0d863..bec2b2d98 100644 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt +++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt @@ -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 = emptyList(), - val author: Author = Author() + val title: String = "Moby-Dick", + val price: Int = 123, + val available: Boolean = true, + val categories: List = 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 ) -} \ No newline at end of file +} diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 184b514b3..2439883e4 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -1,6 +1,10 @@ [[new-features]] = New & Noteworthy +[[new-features.2-2-0]] +== What's New in Spring Data MongoDB 2.2 +* <> + [[new-features.2-1-0]] == What's New in Spring Data MongoDB 2.1 diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index cfef0d875..0f5a11211 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1795,10 +1795,12 @@ GeoResults 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( - Query(Book::title isEqualTo "Moby-Dick") <1> + Query(Book::title isEqualTo "Moby-Dick") <1> ) -Book::title exists true +mongoOperations.find( + Query(titlePredicate = Book::title exists true) +) -Criteria().andOperator( - Book::price gt 5, - Book::price lt 10 +mongoOperations.find( + Criteria().andOperator( + Book::price gt 5, + Book::price lt 10 + ) ) // Binary operators -BinaryMessage::payload bits { allClear(0b101) } <2> +mongoOperations.find( + Query(BinaryMessage::payload bits { allClear(0b101) }) <2> +) // Nested Properties (i.e. refer to "book.author") -Book::author / Author::name regex "^H" <3> +mongoOperations.find( + Query(Book::author / Author::name regex "^H") <3> +) ---- <1> `isEqualTo()` is an infix extension function with receiver type `KProperty` that returns `Criteria`. <2> For bitwise operators, pass a lambda argument where you call one of the methods of `Criteria.BitwiseCriteriaOperators`.