DATAMONGO-2417 - Typesafe Kotlin Extension for distinct queries.

We now provide extensions for imperative and reactive distinct queries accepting Kotlin's KProperty and KProperty1 to express type-safe queries:

mongo.query<Customer>().distinct(Customer::name)

mongo.distinct(Customer::name)

Original Pull Request: #809
This commit is contained in:
Mark Paluch
2019-11-22 12:16:52 +01:00
committed by Christoph Strobl
parent d0c2c820d7
commit 2c9956723e
4 changed files with 93 additions and 0 deletions

View File

@@ -15,7 +15,10 @@
*/
package org.springframework.data.mongodb.core
import org.springframework.data.mongodb.core.query.asString
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
/**
* Extension for [ExecutableFindOperation.query] providing a [KClass] based variant.
@@ -38,6 +41,15 @@ fun <T : Any> ExecutableFindOperation.query(entityClass: KClass<T>): ExecutableF
inline fun <reified T : Any> ExecutableFindOperation.query(): ExecutableFindOperation.ExecutableFind<T> =
query(T::class.java)
/**
* Extension for [ExecutableFindOperation.query] for a type-safe projection of distinct values.
*
* @author Mark Paluch
* @since 2.3
*/
inline fun <reified T : Any> ExecutableFindOperation.distinct(field : KProperty1<T, *>): ExecutableFindOperation.TerminatingDistinct<Any> =
query(T::class.java).distinct(field.name)
/**
* Extension for [ExecutableFindOperation.FindWithProjection.as] providing a [KClass] based variant.
*
@@ -78,3 +90,12 @@ fun <T : Any> ExecutableFindOperation.DistinctWithProjection.asType(resultType:
*/
inline fun <reified T : Any> ExecutableFindOperation.DistinctWithProjection.asType(): ExecutableFindOperation.TerminatingDistinct<T> =
`as`(T::class.java)
/**
* Extension for [ExecutableFindOperation.FindDistinct.distinct] leveraging KProperty.
*
* @author Mark Paluch
* @since 2.3
*/
fun ExecutableFindOperation.FindDistinct.distinct(key: KProperty<*>): ExecutableFindOperation.TerminatingDistinct<Any> =
distinct(asString(key))

View File

@@ -20,7 +20,10 @@ import kotlinx.coroutines.reactive.asFlow
import kotlinx.coroutines.reactive.awaitFirstOrNull
import kotlinx.coroutines.reactive.awaitSingle
import org.springframework.data.geo.GeoResult
import org.springframework.data.mongodb.core.query.asString
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
/**
* Extension for [ReactiveFindOperation.query] providing a [KClass] based variant.
@@ -41,6 +44,15 @@ fun <T : Any> ReactiveFindOperation.query(entityClass: KClass<T>): ReactiveFindO
inline fun <reified T : Any> ReactiveFindOperation.query(): ReactiveFindOperation.ReactiveFind<T> =
query(T::class.java)
/**
* Extension for [ReactiveFindOperation.query] for a type-safe projection of distinct values.
*
* @author Mark Paluch
* @since 2.3
*/
inline fun <reified T : Any> ReactiveFindOperation.distinct(field : KProperty1<T, *>): ReactiveFindOperation.TerminatingDistinct<Any> =
query(T::class.java).distinct(field.name)
/**
* Extension for [ReactiveFindOperation.FindWithProjection.as] providing a [KClass] based variant.
*
@@ -79,6 +91,15 @@ fun <T : Any> ReactiveFindOperation.DistinctWithProjection.asType(resultType: KC
inline fun <reified T : Any> ReactiveFindOperation.DistinctWithProjection.asType(): ReactiveFindOperation.TerminatingDistinct<T> =
`as`(T::class.java)
/**
* Extension for [ReactiveFindOperation.FindDistinct.distinct] leveraging KProperty.
*
* @author Mark Paluch
* @since 2.3
*/
fun ReactiveFindOperation.FindDistinct.distinct(key: KProperty<*>): ReactiveFindOperation.TerminatingDistinct<Any> =
distinct(asString(key))
/**
* Non-nullable Coroutines variant of [ReactiveFindOperation.TerminatingFind.one].
*

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core
import example.first.First
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
@@ -32,6 +33,10 @@ class ExecutableFindOperationExtensionsTests {
val distinctWithProjection = mockk<ExecutableFindOperation.DistinctWithProjection>(relaxed = true)
val findDistinct = mockk<ExecutableFindOperation.FindDistinct>(relaxed = true)
val executableFind = mockk<ExecutableFindOperation.ExecutableFind<KotlinUser>>(relaxed = true)
@Test // DATAMONGO-1689
@Suppress("DEPRECATION")
fun `ExecutableFindOperation#query(KClass) extension should call its Java counterpart`() {
@@ -76,4 +81,25 @@ class ExecutableFindOperationExtensionsTests {
distinctWithProjection.asType<User>()
verify { distinctWithProjection.`as`(User::class.java) }
}
@Test // DATAMONGO-2417
fun `ExecutableFindOperation#distrinct() using KProperty1 should call its Java counterpart`() {
every { operation.query(KotlinUser::class.java) } returns executableFind
operation.distinct(KotlinUser::username)
verify {
operation.query(KotlinUser::class.java)
executableFind.distinct("username")
}
}
@Test // DATAMONGO-2417
fun `ExecutableFindOperation#FindDistinct#field() using KProperty should call its Java counterpart`() {
findDistinct.distinct(KotlinUser::username)
verify { findDistinct.distinct("username") }
}
data class KotlinUser(val username: String)
}

View File

@@ -42,6 +42,10 @@ class ReactiveFindOperationExtensionsTests {
val distinctWithProjection = mockk<ReactiveFindOperation.DistinctWithProjection>(relaxed = true)
val findDistinct = mockk<ReactiveFindOperation.FindDistinct>(relaxed = true)
val reactiveFind = mockk<ReactiveFindOperation.ReactiveFind<KotlinUser>>(relaxed = true)
@Test // DATAMONGO-1719
@Suppress("DEPRECATION")
fun `ReactiveFind#query(KClass) extension should call its Java counterpart`() {
@@ -87,6 +91,25 @@ class ReactiveFindOperationExtensionsTests {
verify { distinctWithProjection.`as`(User::class.java) }
}
@Test // DATAMONGO-2417
fun `ReactiveFind#distrinct() using KProperty1 should call its Java counterpart`() {
every { operation.query(KotlinUser::class.java) } returns reactiveFind
operation.distinct(KotlinUser::username)
verify {
operation.query(KotlinUser::class.java)
reactiveFind.distinct("username")
}
}
@Test // DATAMONGO-2417
fun `ReactiveFind#FindDistinct#field() using KProperty should call its Java counterpart`() {
findDistinct.distinct(KotlinUser::username)
verify { findDistinct.distinct("username") }
}
@Test // DATAMONGO-2209
fun terminatingFindAwaitOneWithValue() {
@@ -299,4 +322,6 @@ class ReactiveFindOperationExtensionsTests {
spec.all()
}
}
data class KotlinUser(val username: String)
}