Skip to content

Commit adc970e

Browse files
authored
Add extension methods for Indexes (#1532)
JAVA-5604
1 parent 7ade570 commit adc970e

File tree

4 files changed

+226
-7
lines changed

4 files changed

+226
-7
lines changed

Diff for: config/spotbugs/exclude.xml

+6
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@
235235
<Method name="~include|exclude"/>
236236
<Bug pattern="BC_BAD_CAST_TO_ABSTRACT_COLLECTION"/>
237237
</Match>
238+
<Match>
239+
<!-- MongoDB status: "False Positive", SpotBugs rank: 17 -->
240+
<Class name="com.mongodb.kotlin.client.model.Indexes"/>
241+
<Method name="~ascending|descending|geo2dsphere"/>
242+
<Bug pattern="BC_BAD_CAST_TO_ABSTRACT_COLLECTION"/>
243+
</Match>
238244

239245
<!-- Spotbugs reports false positives for suspendable operations with default params
240246
see: https://github.com/Kotlin/kotlinx.coroutines/issues/3099
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
* Copyright (C) 2016/2022 Litote
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* @custom-license-header
18+
*/
19+
package com.mongodb.kotlin.client.model
20+
21+
import com.mongodb.client.model.Indexes
22+
import kotlin.reflect.KProperty
23+
import org.bson.conversions.Bson
24+
25+
/**
26+
* Indexes extension methods to improve Kotlin interop
27+
*
28+
* @since 5.3
29+
*/
30+
public object Indexes {
31+
/**
32+
* Create an index key for an ascending index on the given fields.
33+
*
34+
* @param properties the properties, which must contain at least one
35+
* @return the index specification @mongodb.driver.manual core/indexes indexes
36+
*/
37+
public fun ascending(vararg properties: KProperty<*>): Bson = Indexes.ascending(properties.map { it.path() })
38+
39+
/**
40+
* Create an index key for an ascending index on the given fields.
41+
*
42+
* @param properties the properties, which must contain at least one
43+
* @return the index specification @mongodb.driver.manual core/indexes indexes
44+
*/
45+
public fun ascending(properties: Iterable<KProperty<*>>): Bson = Indexes.ascending(properties.map { it.path() })
46+
47+
/**
48+
* Create an index key for a descending index on the given fields.
49+
*
50+
* @param properties the properties, which must contain at least one
51+
* @return the index specification @mongodb.driver.manual core/indexes indexes
52+
*/
53+
public fun descending(vararg properties: KProperty<*>): Bson = Indexes.descending(properties.map { it.path() })
54+
55+
/**
56+
* Create an index key for a descending index on the given fields.
57+
*
58+
* @param properties the properties, which must contain at least one
59+
* @return the index specification @mongodb.driver.manual core/indexes indexes
60+
*/
61+
public fun descending(properties: Iterable<KProperty<*>>): Bson = Indexes.descending(properties.map { it.path() })
62+
63+
/**
64+
* Create an index key for an 2dsphere index on the given fields.
65+
*
66+
* @param properties the properties, which must contain at least one
67+
* @return the index specification @mongodb.driver.manual core/2dsphere 2dsphere Index
68+
*/
69+
public fun geo2dsphere(vararg properties: KProperty<*>): Bson = Indexes.geo2dsphere(properties.map { it.path() })
70+
71+
/**
72+
* Create an index key for an 2dsphere index on the given fields.
73+
*
74+
* @param properties the properties, which must contain at least one
75+
* @return the index specification @mongodb.driver.manual core/2dsphere 2dsphere Index
76+
*/
77+
public fun geo2dsphere(properties: Iterable<KProperty<*>>): Bson = Indexes.geo2dsphere(properties.map { it.path() })
78+
79+
/**
80+
* Create an index key for a text index on the given property.
81+
*
82+
* @param property the property to create a text index on
83+
* @return the index specification @mongodb.driver.manual core/text text index
84+
*/
85+
public fun <T> text(property: KProperty<T>): Bson = Indexes.text(property.path())
86+
87+
/**
88+
* Create an index key for a hashed index on the given property.
89+
*
90+
* @param property the property to create a hashed index on
91+
* @return the index specification @mongodb.driver.manual core/hashed hashed index
92+
*/
93+
public fun <T> hashed(property: KProperty<T>): Bson = Indexes.hashed(property.path())
94+
95+
/**
96+
* Create an index key for a 2d index on the given field.
97+
*
98+
* <p>
99+
* <strong>Note: </strong>A 2d index is for data stored as points on a two-dimensional plane. The 2d index is
100+
* intended for legacy coordinate pairs used in MongoDB 2.2 and earlier. </p>
101+
*
102+
* @param property the property to create a 2d index on
103+
* @return the index specification @mongodb.driver.manual core/2d 2d index
104+
*/
105+
public fun <T> geo2d(property: KProperty<T>): Bson = Indexes.geo2d(property.path())
106+
}

Diff for: driver-kotlin-extensions/src/test/kotlin/com/mongodb/kotlin/client/model/ExtensionsApiTest.kt

+20-7
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,24 @@ class ExtensionsApiTest {
4848
assertTrue(notImplemented.isEmpty(), "Some possible Updates were not implemented: $notImplemented")
4949
}
5050

51+
@Test
52+
fun shouldHaveAllIndexesExtensions() {
53+
val kotlinExtensions: Set<String> = getKotlinExtensions("Indexes")
54+
val javaMethods: Set<String> = getJavaMethods("Indexes")
55+
val notImplemented = javaMethods subtract kotlinExtensions
56+
assertTrue(notImplemented.isEmpty(), "Some possible Indexes were not implemented: $notImplemented")
57+
}
58+
5159
private fun getKotlinExtensions(className: String): Set<String> {
5260
return ClassGraph()
5361
.enableClassInfo()
5462
.enableMethodInfo()
5563
.acceptPackages("com.mongodb.kotlin.client.model")
5664
.scan()
57-
.use {
58-
it.allClasses
59-
.filter { it.simpleName == "${className}" }
65+
.use { result ->
66+
result.allClasses
67+
.filter { it.simpleName == className }
68+
.asSequence()
6069
.flatMap { it.methodInfo }
6170
.filter { it.isPublic }
6271
.map { it.name }
@@ -69,10 +78,14 @@ class ExtensionsApiTest {
6978
return ClassGraph().enableClassInfo().enableMethodInfo().acceptPackages("com.mongodb.client.model").scan().use {
7079
it.getClassInfo("com.mongodb.client.model.$className")
7180
.methodInfo
72-
.filter {
73-
it.isPublic &&
74-
it.parameterInfo.isNotEmpty() &&
75-
it.parameterInfo[0].typeDescriptor.toStringWithSimpleNames().equals("String")
81+
.filter { methodInfo ->
82+
methodInfo.isPublic &&
83+
methodInfo.parameterInfo.isNotEmpty() &&
84+
methodInfo.parameterInfo[0]
85+
.typeDescriptor
86+
.toStringWithSimpleNames()
87+
.equals("String") // only method starting
88+
// with a String (property name)
7689
}
7790
.map { m -> m.name }
7891
.toSet()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
* Copyright (C) 2016/2022 Litote
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
* @custom-license-header
18+
*/
19+
package com.mongodb.kotlin.client.model
20+
21+
import com.mongodb.client.model.Indexes
22+
import com.mongodb.client.model.Indexes.compoundIndex
23+
import com.mongodb.kotlin.client.model.Indexes.ascending
24+
import com.mongodb.kotlin.client.model.Indexes.descending
25+
import com.mongodb.kotlin.client.model.Indexes.geo2d
26+
import com.mongodb.kotlin.client.model.Indexes.geo2dsphere
27+
import com.mongodb.kotlin.client.model.Indexes.hashed
28+
import com.mongodb.kotlin.client.model.Indexes.text
29+
import kotlin.test.assertEquals
30+
import org.bson.BsonDocument
31+
import org.bson.conversions.Bson
32+
import org.junit.Test
33+
34+
class IndexesTest {
35+
36+
@Test
37+
fun `ascending index`() {
38+
assertEquals(""" {name: 1} """, ascending(Person::name))
39+
assertEquals(""" {name: 1, age: 1} """, ascending(Person::name, Person::age))
40+
assertEquals(""" {name: 1, age: 1} """, ascending(listOf(Person::name, Person::age)))
41+
}
42+
43+
@Test
44+
fun `descending index`() {
45+
assertEquals(""" {name: -1} """, descending(Person::name))
46+
assertEquals(""" {name: -1, age: -1} """, descending(Person::name, Person::age))
47+
assertEquals(""" {name: -1, age: -1} """, descending(listOf(Person::name, Person::age)))
48+
}
49+
50+
@Test
51+
fun `geo2dsphere index`() {
52+
assertEquals(""" {name: "2dsphere"} """, geo2dsphere(Person::name))
53+
assertEquals(""" {name: "2dsphere", age: "2dsphere"} """, geo2dsphere(Person::name, Person::age))
54+
assertEquals(""" {name: "2dsphere", age: "2dsphere"} """, geo2dsphere(listOf(Person::name, Person::age)))
55+
}
56+
57+
@Test
58+
fun `geo2d index`() {
59+
assertEquals(""" {name: "2d"} """, geo2d(Person::name))
60+
}
61+
62+
@Test
63+
fun `text helper`() {
64+
assertEquals(""" {name: "text"} """, text(Person::name))
65+
assertEquals(""" { "${'$'}**" : "text"} """, Indexes.text())
66+
}
67+
68+
@Test
69+
fun `hashed index`() {
70+
assertEquals(""" {name: "hashed"} """, hashed(Person::name))
71+
}
72+
73+
@Test
74+
fun `compound index`() {
75+
assertEquals(""" {name : 1, age : -1} """, compoundIndex(ascending(Person::name), descending(Person::age)))
76+
}
77+
78+
@Test
79+
fun `should test equals on CompoundIndex`() {
80+
assertEquals(
81+
compoundIndex(ascending(Person::name), descending(Person::age)),
82+
compoundIndex(ascending(Person::name), descending(Person::age)))
83+
84+
assertEquals(
85+
compoundIndex(listOf(ascending(Person::name), descending(Person::age))),
86+
compoundIndex(listOf(ascending(Person::name), descending(Person::age))))
87+
}
88+
89+
// Utils
90+
private data class Person(val name: String, val age: Int)
91+
92+
private fun assertEquals(expected: String, result: Bson) =
93+
assertEquals(BsonDocument.parse(expected), result.toBsonDocument())
94+
}

0 commit comments

Comments
 (0)