Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand bounds for distinct and MongoIterable#map #1352

Merged
merged 1 commit into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions driver-kotlin-coroutine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ dependencies {
testImplementation("io.github.classgraph:classgraph:4.8.154")

integrationTestImplementation("org.jetbrains.kotlin:kotlin-test-junit")
integrationTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test")
integrationTestImplementation(project(path = ":driver-sync"))
integrationTestImplementation(project(path = ":driver-core"))
integrationTestImplementation(project(path = ":bson"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.kotlin.client.coroutine

import com.mongodb.client.Fixture.getDefaultDatabaseName
import com.mongodb.client.Fixture.getMongoClientSettings
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.toSet
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.bson.Document
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

@OptIn(ExperimentalCoroutinesApi::class)
class SmokeTests {

@AfterEach
fun afterEach() {
runBlocking { database?.drop() }
}

@Test
@DisplayName("distinct and return nulls")
fun testDistinctNullable() = runTest {
collection!!.insertMany(
listOf(
Document.parse("{_id: 1, a: 0}"),
Document.parse("{_id: 2, a: 1}"),
Document.parse("{_id: 3, a: 0}"),
Document.parse("{_id: 4, a: null}")))

// nulls are auto excluded in reactive streams!
val actual = collection!!.distinct<Int>("a").toSet()
assertEquals(setOf(0, 1), actual)
}

@Test
@DisplayName("mapping can return nulls")
fun testMongoIterableMap() = runTest {
collection!!.insertMany(
listOf(
Document.parse("{_id: 1, a: 0}"),
Document.parse("{_id: 2, a: 1}"),
Document.parse("{_id: 3, a: 0}"),
Document.parse("{_id: 4, a: null}")))

val actual = collection!!.find().map { it["a"] as Int? }.toList()
assertContentEquals(listOf(0, 1, 0, null), actual)
}

companion object {

private var mongoClient: MongoClient? = null
private var database: MongoDatabase? = null
private var collection: MongoCollection<Document>? = null

@BeforeAll
@JvmStatic
internal fun beforeAll() {
runBlocking {
mongoClient = MongoClient.create(getMongoClientSettings())
database = mongoClient?.getDatabase(getDefaultDatabaseName())
database?.drop()
collection = database?.getCollection("SmokeTests")
}
}

@AfterAll
@JvmStatic
internal fun afterAll() {
runBlocking {
collection = null
database?.drop()
database = null
mongoClient?.close()
mongoClient = null
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2008-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.kotlin.client

import com.mongodb.client.Fixture.getDefaultDatabaseName
import com.mongodb.client.Fixture.getMongoClientSettings
import kotlin.test.assertContentEquals
import org.bson.Document
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test

class SmokeTests {

@AfterEach
fun afterEach() {
database?.drop()
}

@Test
@DisplayName("distinct and return nulls")
fun testDistinctNullable() {
collection!!.insertMany(
listOf(
Document.parse("{_id: 1, a: 0}"),
Document.parse("{_id: 2, a: 1}"),
Document.parse("{_id: 3, a: 0}"),
Document.parse("{_id: 4, a: null}")))

val actual = collection!!.distinct<Int?>("a").toList().toSet()
assertEquals(setOf(null, 0, 1), actual)
}

@Test
@DisplayName("mapping can return nulls")
fun testMongoIterableMap() {
collection!!.insertMany(
listOf(
Document.parse("{_id: 1, a: 0}"),
Document.parse("{_id: 2, a: 1}"),
Document.parse("{_id: 3, a: 0}"),
Document.parse("{_id: 4, a: null}")))

val actual = collection!!.find().map { it["a"] }.toList()
assertContentEquals(listOf(0, 1, 0, null), actual)
}

companion object {

private var mongoClient: MongoClient? = null
private var database: MongoDatabase? = null
private var collection: MongoCollection<Document>? = null

@BeforeAll
@JvmStatic
internal fun beforeAll() {
mongoClient = MongoClient.create(getMongoClientSettings())
database = mongoClient?.getDatabase(getDefaultDatabaseName())
database?.drop()
collection = database?.getCollection("SmokeTests")
}

@AfterAll
@JvmStatic
internal fun afterAll() {
collection = null
database?.drop()
database = null
mongoClient?.close()
mongoClient = null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import org.bson.conversions.Bson
* @param T The type of the result.
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
*/
public class DistinctIterable<T : Any>(private val wrapped: JDistinctIterable<T>) : MongoIterable<T>(wrapped) {
public class DistinctIterable<T : Any?>(private val wrapped: JDistinctIterable<T>) : MongoIterable<T>(wrapped) {
/**
* Sets the number of documents to return per batch.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
* @return an iterable of distinct values
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
*/
public fun <R : Any> distinct(
public fun <R : Any?> distinct(
fieldName: String,
filter: Bson = BsonDocument(),
resultClass: Class<R>
Expand All @@ -236,7 +236,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
* @return an iterable of distinct values
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
*/
public fun <R : Any> distinct(
public fun <R : Any?> distinct(
clientSession: ClientSession,
fieldName: String,
filter: Bson = BsonDocument(),
Expand All @@ -252,7 +252,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
* @return an iterable of distinct values
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
*/
public inline fun <reified R : Any> distinct(
public inline fun <reified R : Any?> distinct(
fieldName: String,
filter: Bson = BsonDocument()
): DistinctIterable<R> = distinct(fieldName, filter, R::class.java)
Expand All @@ -267,7 +267,7 @@ public class MongoCollection<T : Any>(private val wrapped: JMongoCollection<T>)
* @return an iterable of distinct values
* @see [Distinct command](https://www.mongodb.com/docs/manual/reference/command/distinct/)
*/
public inline fun <reified R : Any> distinct(
public inline fun <reified R : Any?> distinct(
clientSession: ClientSession,
fieldName: String,
filter: Bson = BsonDocument()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import org.bson.BsonDocument
*
* @param T The type of documents the cursor contains
*/
public sealed interface MongoCursor<T : Any> : Iterator<T>, Closeable {
public sealed interface MongoCursor<T : Any?> : Iterator<T>, Closeable {

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

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.mongodb.client.MongoIterable as JMongoIterable
*
* @param T The type that this iterable will decode documents to.
*/
public open class MongoIterable<T : Any>(private val delegate: JMongoIterable<T>) {
public open class MongoIterable<T : Any?>(private val delegate: JMongoIterable<T>) {

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

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