Skip to content

Commit c6a49cd

Browse files
committed
DATAMONGO-2138 - Typed Queries
1 parent 9a0dad1 commit c6a49cd

File tree

4 files changed

+286
-0
lines changed

4 files changed

+286
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2010-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.query
17+
18+
import org.bson.Document
19+
import kotlin.reflect.KProperty
20+
21+
/**
22+
* @author Tjeu Kayim
23+
*/
24+
class TypedCriteria(
25+
property: KProperty<Any?>,
26+
val operation: Criteria.() -> Criteria
27+
) : CriteriaDefinition {
28+
val name = property.name
29+
val criteria by lazy { Criteria(name).operation() }
30+
31+
override fun getCriteriaObject(): Document = criteria.criteriaObject
32+
33+
override fun getKey(): String? = criteria.key
34+
}
35+
36+
fun typedCriteria(vararg typedCriteria: TypedCriteria): CriteriaDefinition {
37+
return chainCriteria(Criteria(), typedCriteria.toList())
38+
}
39+
40+
private fun chainCriteria(criteria: Criteria, tail: Collection<TypedCriteria>): Criteria {
41+
if (tail.isEmpty()) {
42+
return criteria
43+
}
44+
val head = tail.first()
45+
head.operation(criteria.and(head.name))
46+
return chainCriteria(criteria, tail.drop(1))
47+
}
48+
49+
infix fun <T> KProperty<T>.isEqualTo(value: T) = filter { isEqualTo(value) }
50+
infix fun <T> KProperty<T>.ne(value: T) = filter { ne(value) }
51+
infix fun <T> KProperty<T>.lt(value: T) = filter { lt(value) }
52+
infix fun <T> KProperty<T>.lte(value: T) = filter { lte(value) }
53+
infix fun <T> KProperty<T>.gt(value: T) = filter { gt(value) }
54+
infix fun <T> KProperty<T>.gte(value: T) = filter { gte(value) }
55+
infix fun <T> KProperty<T>.inValues(value: Collection<T>) = filter { `in`(value) }
56+
fun <T> KProperty<T>.inValues(vararg o: Any) = filter { `in`(*o) }
57+
infix fun <T> KProperty<T>.nin(value: Collection<T>) = filter { nin(value) }
58+
fun <T> KProperty<T>.nin(vararg o: Any) = filter { nin(*o) }
59+
fun KProperty<Number>.mod(value: Number, remainder: Number) = filter { mod(value, remainder) }
60+
infix fun <T : Collection<*>> KProperty<T>.all(value: T) = filter { all(value) }
61+
fun <T> KProperty<T>.all(vararg o: Any) = filter { all(*o) }
62+
fun KProperty<Collection<*>>.size(s: Int) = filter { size(s) }
63+
64+
private fun <T> KProperty<T>.filter(operation: Criteria.() -> Criteria) = TypedCriteria(this, operation)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2010-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.query
17+
18+
/**
19+
* Create a new Query with one or more TypedCriteria.
20+
* @author Tjeu Kayim
21+
*/
22+
fun typedQuery(vararg criteria: TypedCriteria): Query {
23+
return Query(typedCriteria(*criteria))
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2010-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.mongodb.core.query
18+
19+
import org.bson.types.ObjectId
20+
import org.junit.Assert.assertEquals
21+
import org.junit.Test
22+
import org.springframework.data.mongodb.core.mapping.Document
23+
24+
/**
25+
* @author Tjeu Kayim
26+
*/
27+
class TypedCriteriaExtensionsTest {
28+
@Test
29+
fun `Typed query gt and isEqualTo`() {
30+
val typed = typedCriteria(
31+
Book::price gt 1100,
32+
Book::available isEqualTo true
33+
)
34+
val classic = Criteria("price").gt(1100)
35+
.and("available").isEqualTo(true)
36+
assertCriteriaEquals(classic, typed)
37+
}
38+
39+
@Test
40+
fun `Typed criteria isEqualTo`() {
41+
val typed = Book::name isEqualTo "Moby-Dick"
42+
val classic = Criteria("name").isEqualTo("Moby-Dick")
43+
assertCriteriaEquals(classic, typed)
44+
}
45+
46+
@Test
47+
fun `Typed criteria ne`() {
48+
val typed = Book::name ne "Moby-Dick"
49+
val classic = Criteria("name").ne("Moby-Dick")
50+
assertCriteriaEquals(classic, typed)
51+
}
52+
53+
@Test
54+
fun `Typed criteria lt`() {
55+
val typed = Book::price lt 100
56+
val classic = Criteria("price").lt(100)
57+
assertCriteriaEquals(classic, typed)
58+
}
59+
60+
@Test
61+
fun `Typed criteria lte`() {
62+
val typed = Book::price lte 100
63+
val classic = Criteria("price").lte(100)
64+
assertCriteriaEquals(classic, typed)
65+
}
66+
67+
@Test
68+
fun `Typed criteria gt`() {
69+
val typed = Book::price gt 100
70+
val classic = Criteria("price").gt(100)
71+
assertCriteriaEquals(classic, typed)
72+
}
73+
74+
@Test
75+
fun `Typed criteria gte`() {
76+
val typed = Book::price gte 100
77+
val classic = Criteria("price").gte(100)
78+
assertCriteriaEquals(classic, typed)
79+
}
80+
81+
@Test
82+
fun `Typed criteria inValues`() {
83+
val typed = Book::price.inValues(1, 2, 3)
84+
val classic = Criteria("price").inValues(1, 2, 3)
85+
assertCriteriaEquals(classic, typed)
86+
}
87+
88+
@Test
89+
fun `Typed criteria inValues list`() {
90+
val typed = Book::price inValues listOf(1, 2, 3)
91+
val classic = Criteria("price").inValues(listOf(1, 2, 3))
92+
assertCriteriaEquals(classic, typed)
93+
}
94+
95+
@Test
96+
fun `Typed criteria nin`() {
97+
val typed = Book::price.nin(1, 2, 3)
98+
val classic = Criteria("price").nin(1, 2, 3)
99+
assertCriteriaEquals(classic, typed)
100+
}
101+
102+
@Test
103+
fun `Typed criteria nin list`() {
104+
val typed = Book::price nin listOf(1, 2, 3)
105+
val classic = Criteria("price").nin(listOf(1, 2, 3))
106+
assertCriteriaEquals(classic, typed)
107+
}
108+
109+
@Test
110+
fun `Typed criteria mod`() {
111+
val typed = Book::price.mod(2, 3)
112+
val classic = Criteria("price").mod(2, 3)
113+
assertCriteriaEquals(classic, typed)
114+
}
115+
116+
@Test
117+
fun `Typed criteria all`() {
118+
val typed = Book::authors.all(1, 2, 3)
119+
val classic = Criteria("authors").all(1, 2, 3)
120+
assertCriteriaEquals(classic, typed)
121+
}
122+
123+
@Test
124+
fun `Typed criteria all list`() {
125+
val typed = Book::authors.all(listOf(1, 2, 3))
126+
val classic = Criteria("authors").all(listOf(1, 2, 3))
127+
assertCriteriaEquals(classic, typed)
128+
}
129+
130+
@Test
131+
fun `Typed criteria size`() {
132+
val typed = Book::authors.size(4)
133+
val classic = Criteria("authors").size(4)
134+
assertCriteriaEquals(classic, typed)
135+
}
136+
137+
private fun assertCriteriaEquals(expected: CriteriaDefinition, actual: CriteriaDefinition) {
138+
assertEquals(expected.criteriaObject, actual.criteriaObject)
139+
}
140+
}
141+
142+
@Document("books")
143+
data class Book(
144+
val id: ObjectId,
145+
val name: String,
146+
val price: Int,
147+
val available: Boolean,
148+
val authors: List<Author>
149+
)
150+
151+
data class Author(
152+
val id: ObjectId,
153+
val name: String,
154+
val price: Int,
155+
val available: Boolean
156+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2010-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.mongodb.core.query
18+
19+
import org.junit.Assert.assertEquals
20+
import org.junit.Test
21+
22+
/**
23+
* @author Tjeu Kayim
24+
*/
25+
class TypedQueryExtensionsTest {
26+
27+
@Test
28+
fun `Call typedQuery with two TypedCriteria`() {
29+
val classic = Query(
30+
Criteria()
31+
.and("price").gt(1100)
32+
.and("available").isEqualTo(true)
33+
)
34+
35+
val typed = typedQuery(
36+
Book::price gt 1100,
37+
Book::available isEqualTo true
38+
)
39+
40+
assertEquals(classic.queryObject, typed.queryObject)
41+
}
42+
}

0 commit comments

Comments
 (0)