Skip to content

Commit e31340f

Browse files
committed
Expand bounds for distinct and MongoIterable#map (mongodb#1352)
Allow for nullable types in those scenarios. JAVA-5358
1 parent c0b5ba4 commit e31340f

File tree

7 files changed

+200
-9
lines changed

7 files changed

+200
-9
lines changed

driver-kotlin-coroutine/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ dependencies {
7575
testImplementation("io.github.classgraph:classgraph:4.8.154")
7676

7777
integrationTestImplementation("org.jetbrains.kotlin:kotlin-test-junit")
78+
integrationTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test")
7879
integrationTestImplementation(project(path = ":driver-sync"))
7980
integrationTestImplementation(project(path = ":driver-core"))
8081
integrationTestImplementation(project(path = ":bson"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
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 com.mongodb.kotlin.client.coroutine
17+
18+
import com.mongodb.client.Fixture.getDefaultDatabaseName
19+
import com.mongodb.client.Fixture.getMongoClientSettings
20+
import kotlin.test.assertContentEquals
21+
import kotlin.test.assertEquals
22+
import kotlinx.coroutines.ExperimentalCoroutinesApi
23+
import kotlinx.coroutines.flow.map
24+
import kotlinx.coroutines.flow.toList
25+
import kotlinx.coroutines.flow.toSet
26+
import kotlinx.coroutines.runBlocking
27+
import kotlinx.coroutines.test.runTest
28+
import org.bson.Document
29+
import org.junit.jupiter.api.AfterAll
30+
import org.junit.jupiter.api.AfterEach
31+
import org.junit.jupiter.api.BeforeAll
32+
import org.junit.jupiter.api.DisplayName
33+
import org.junit.jupiter.api.Test
34+
35+
@OptIn(ExperimentalCoroutinesApi::class)
36+
class SmokeTests {
37+
38+
@AfterEach
39+
fun afterEach() {
40+
runBlocking { database?.drop() }
41+
}
42+
43+
@Test
44+
@DisplayName("distinct and return nulls")
45+
fun testDistinctNullable() = runTest {
46+
collection!!.insertMany(
47+
listOf(
48+
Document.parse("{_id: 1, a: 0}"),
49+
Document.parse("{_id: 2, a: 1}"),
50+
Document.parse("{_id: 3, a: 0}"),
51+
Document.parse("{_id: 4, a: null}")))
52+
53+
// nulls are auto excluded in reactive streams!
54+
val actual = collection!!.distinct<Int>("a").toSet()
55+
assertEquals(setOf(0, 1), actual)
56+
}
57+
58+
@Test
59+
@DisplayName("mapping can return nulls")
60+
fun testMongoIterableMap() = runTest {
61+
collection!!.insertMany(
62+
listOf(
63+
Document.parse("{_id: 1, a: 0}"),
64+
Document.parse("{_id: 2, a: 1}"),
65+
Document.parse("{_id: 3, a: 0}"),
66+
Document.parse("{_id: 4, a: null}")))
67+
68+
val actual = collection!!.find().map { it["a"] as Int? }.toList()
69+
assertContentEquals(listOf(0, 1, 0, null), actual)
70+
}
71+
72+
companion object {
73+
74+
private var mongoClient: MongoClient? = null
75+
private var database: MongoDatabase? = null
76+
private var collection: MongoCollection<Document>? = null
77+
78+
@BeforeAll
79+
@JvmStatic
80+
internal fun beforeAll() {
81+
runBlocking {
82+
mongoClient = MongoClient.create(getMongoClientSettings())
83+
database = mongoClient?.getDatabase(getDefaultDatabaseName())
84+
database?.drop()
85+
collection = database?.getCollection("SmokeTests")
86+
}
87+
}
88+
89+
@AfterAll
90+
@JvmStatic
91+
internal fun afterAll() {
92+
runBlocking {
93+
collection = null
94+
database?.drop()
95+
database = null
96+
mongoClient?.close()
97+
mongoClient = null
98+
}
99+
}
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
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 com.mongodb.kotlin.client
17+
18+
import com.mongodb.client.Fixture.getDefaultDatabaseName
19+
import com.mongodb.client.Fixture.getMongoClientSettings
20+
import kotlin.test.assertContentEquals
21+
import org.bson.Document
22+
import org.junit.jupiter.api.AfterAll
23+
import org.junit.jupiter.api.AfterEach
24+
import org.junit.jupiter.api.Assertions.assertEquals
25+
import org.junit.jupiter.api.BeforeAll
26+
import org.junit.jupiter.api.DisplayName
27+
import org.junit.jupiter.api.Test
28+
29+
class SmokeTests {
30+
31+
@AfterEach
32+
fun afterEach() {
33+
database?.drop()
34+
}
35+
36+
@Test
37+
@DisplayName("distinct and return nulls")
38+
fun testDistinctNullable() {
39+
collection!!.insertMany(
40+
listOf(
41+
Document.parse("{_id: 1, a: 0}"),
42+
Document.parse("{_id: 2, a: 1}"),
43+
Document.parse("{_id: 3, a: 0}"),
44+
Document.parse("{_id: 4, a: null}")))
45+
46+
val actual = collection!!.distinct<Int?>("a").toList().toSet()
47+
assertEquals(setOf(null, 0, 1), actual)
48+
}
49+
50+
@Test
51+
@DisplayName("mapping can return nulls")
52+
fun testMongoIterableMap() {
53+
collection!!.insertMany(
54+
listOf(
55+
Document.parse("{_id: 1, a: 0}"),
56+
Document.parse("{_id: 2, a: 1}"),
57+
Document.parse("{_id: 3, a: 0}"),
58+
Document.parse("{_id: 4, a: null}")))
59+
60+
val actual = collection!!.find().map { it["a"] }.toList()
61+
assertContentEquals(listOf(0, 1, 0, null), actual)
62+
}
63+
64+
companion object {
65+
66+
private var mongoClient: MongoClient? = null
67+
private var database: MongoDatabase? = null
68+
private var collection: MongoCollection<Document>? = null
69+
70+
@BeforeAll
71+
@JvmStatic
72+
internal fun beforeAll() {
73+
mongoClient = MongoClient.create(getMongoClientSettings())
74+
database = mongoClient?.getDatabase(getDefaultDatabaseName())
75+
database?.drop()
76+
collection = database?.getCollection("SmokeTests")
77+
}
78+
79+
@AfterAll
80+
@JvmStatic
81+
internal fun afterAll() {
82+
collection = null
83+
database?.drop()
84+
database = null
85+
mongoClient?.close()
86+
mongoClient = null
87+
}
88+
}
89+
}

driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/DistinctIterable.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import org.bson.conversions.Bson
2727
* @param T The type of the result.
2828
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
2929
*/
30-
public class DistinctIterable<T : Any>(private val wrapped: JDistinctIterable<T>) : MongoIterable<T>(wrapped) {
30+
public class DistinctIterable<T : Any?>(private val wrapped: JDistinctIterable<T>) : MongoIterable<T>(wrapped) {
3131
/**
3232
* Sets the number of documents to return per batch.
3333
*

driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCollection.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
219219
* @return an iterable of distinct values
220220
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
221221
*/
222-
public fun <R : Any> distinct(
222+
public fun <R : Any?> distinct(
223223
fieldName: String,
224224
filter: Bson = BsonDocument(),
225225
resultClass: Class<R>
@@ -236,7 +236,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
236236
* @return an iterable of distinct values
237237
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
238238
*/
239-
public fun <R : Any> distinct(
239+
public fun <R : Any?> distinct(
240240
clientSession: ClientSession,
241241
fieldName: String,
242242
filter: Bson = BsonDocument(),
@@ -252,7 +252,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
252252
* @return an iterable of distinct values
253253
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
254254
*/
255-
public inline fun <reified R : Any> distinct(
255+
public inline fun <reified R : Any?> distinct(
256256
fieldName: String,
257257
filter: Bson = BsonDocument()
258258
): DistinctIterable<R> = distinct(fieldName, filter, R::class.java)
@@ -267,7 +267,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
267267
* @return an iterable of distinct values
268268
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
269269
*/
270-
public inline fun <reified R : Any> distinct(
270+
public inline fun <reified R : Any?> distinct(
271271
clientSession: ClientSession,
272272
fieldName: String,
273273
filter: Bson = BsonDocument()

driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoCursor.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import org.bson.BsonDocument
3636
*
3737
* @param T The type of documents the cursor contains
3838
*/
39-
public sealed interface MongoCursor<T : Any> : Iterator<T>, Closeable {
39+
public sealed interface MongoCursor<T : Any?> : Iterator<T>, Closeable {
4040

4141
/**
4242
* Gets the number of results available locally without blocking, which may be 0.
@@ -90,7 +90,7 @@ public sealed interface MongoChangeStreamCursor<T : Any> : MongoCursor<T> {
9090
public val resumeToken: BsonDocument?
9191
}
9292

93-
internal class MongoCursorImpl<T : Any>(private val wrapped: JMongoCursor<T>) : MongoCursor<T> {
93+
internal class MongoCursorImpl<T : Any?>(private val wrapped: JMongoCursor<T>) : MongoCursor<T> {
9494

9595
override fun hasNext(): Boolean = wrapped.hasNext()
9696

driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/MongoIterable.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import com.mongodb.client.MongoIterable as JMongoIterable
2323
*
2424
* @param T The type that this iterable will decode documents to.
2525
*/
26-
public open class MongoIterable<T : Any>(private val delegate: JMongoIterable<T>) {
26+
public open class MongoIterable<T : Any?>(private val delegate: JMongoIterable<T>) {
2727

2828
/**
2929
* Returns a cursor used for iterating over elements of type `T. The cursor is primarily used for change streams.
@@ -71,7 +71,7 @@ public open class MongoIterable<T : Any>(private val delegate: JMongoIterable<T>
7171
* @param transform a function that maps from the source to the target document type
7272
* @return an iterable which maps T to U
7373
*/
74-
public fun <R : Any> map(transform: (T) -> R): MongoIterable<R> = MongoIterable(delegate.map(transform))
74+
public fun <R : Any?> map(transform: (T) -> R): MongoIterable<R> = MongoIterable(delegate.map(transform))
7575

7676
/** Performs the given [action] on each element and safely closes the cursor. */
7777
public fun forEach(action: (T) -> Unit): Unit = use { it.forEach(action) }

0 commit comments

Comments
 (0)