Docs Menu

Migrate from KMongo

This page contains a high-level comparison of most of the ways the official MongoDB Kotlin and the community-developed KMongo driver differ. You can use this page to identify the changes you need to make to migrate from the deprecated KMongo driver to the official MongoDB Kotlin driver.

KMongo is a popular community-developed library for working with MongoDB from Kotlin applications. It is a wrapper around the Java driver that was created prior to the creation of the official Kotlin driver to serve the needs of the Kotlin community.

Important

As of July 2023, KMongo has been marked as deprecated.

The MongoDB Kotlin driver is the officially supported and maintained MongoDB driver for Kotlin. It is developed by the MongoDB team.

Although both drivers support synchronous and asynchronous operations, the examples on this page will use asynchronous coroutine-based operations.

Both drivers let you connect to and communicate with MongoDB clusters from a Kotlin application.

To connect to a MongoDB cluster using the MongoDB Kotlin driver:

import com.mongodb.kotlin.client.coroutine.MongoClient
data class Jedi(val name: String, val age: Int)
// Replace the placeholder with your MongoDB deployment's connection string
val uri = CONNECTION_STRING_URI_PLACEHOLDER
val mongoClient = MongoClient.create(uri)
val database = mongoClient.getDatabase("test")
// Get a collection of documents of type Jedi
val collection = database.getCollection<Jedi>("jedi")

See the Connect to MongoDB documentation for more information.

To connect to a MongoDB cluster using KMongo with coroutines:

import org.litote.kmongo.reactivestreams.*
import org.litote.kmongo.coroutine.*
data class Jedi(val name: String, val age: Int)
// Get new MongoClient instance using coroutine extension
val client = KMongo.createClient().coroutine
val database = client.getDatabase("test")
// Get a collection of documents of type Jedi
val col = database.getCollection<Jedi>()

Unlike the MongoDB Kotlin driver, KMongo allows the collection name to be inferred from the data class name.

Both drivers provide support for all MongoDB CRUD APIs and aggregation operations.

The MongoDB Kotlin driver also provides functions for all basic CRUD operations:

// Insert a document
val jedi =a Jedi("Luke Skywalker", 19)
collection.insertOne(jedi)
// Find a document
val luke = collection.find(Jedi::name.name, "Luke Skywalker")
val jedis = collection.find(lt(Jedi::age.name, 30)).toList()
// Update a document
val filter = Filters.eq(Jedi::name.name, "Luke Skywalker")
val update = Updates.set(Jedi::age.name, 20)
collection.updateOne(filter, update)
// Delete a document
val filter = Filters.eq(Jedi::name.name, "Luke Skywalker")
collection.deleteOne(filter)

Aggregation pipelines can be built using the aggregate method and the pipeline function:

data class Results(val avgAge: Double)
val resultsFlow = collection.aggregate<Results>(
listOf(
Aggregates.match(Filters.ne(Jedi::name.name, "Luke Skywalker")),
Aggregates.group("\$${Jedi::name.name}",
Accumulators.avg("avgAge", "\$${Jedi::age.name}"))
)
)
resultsFlow.collect { println(it) }

See the CRUD Operations and Aggregation documentation for more information.

KMongo provides functions for all basic CRUD operations:

// Insert a document
val jedi = Jedi("Luke Skywalker", 19)
col.insertOne(jedi)
// Find a document
val luke = col.findOne(Jedi::name eq "Luke Skywalker")
val jedis = col.find(Jedi::age lt 30).toList()
// Update a document
col.updateOne(Jedi::name eq "Luke Skywalker", setValue(Jedi::age, 20))
// Delete a document
col.deleteOne(Jedi::name eq "Luke Skywalker")

Aggregation pipelines can be built using the aggregate method and the pipeline function:

val avgAge = collection.aggregate<Double>(
pipeline(
match(Jedi::name ne "Luke Skywalker"),
group(Jedi::name, avg(Jedi::age))
)
).toList()

For more information on available methods, see the Extensions Overview KMongo documentation.

Both drivers provide support for type-safe queries using property references.

The MongoDB Kotlin driver uses the Builders API to construct queries. Alternatively, you can use the Document class.

data class Person(val name: String, val email: String, val gender: String, val age: Int)
data class Results(val email: String)
val collection = database.getCollection<Person>("people")
// Using Builders
val filter = and(eq("gender", "female"), gt("age", 29))
val projection = fields(excludeId(), include("email"))
val results = collection.find<Results>(filter).projection(projection)
// Using Document class
val filter = Document().append("gender", "female").append("age", Document().append("\$gt", 29))
val projection = Document().append("_id", 0).append("email", 1)
val results = collection.find<Results>(filter).projection(projection)

To map a KMongo string query to the Kotlin driver, you can use the JsonObject class.

val query = JsonObject("{\"name\": \"Gabriel Garc\\u00eda M\\u00e1rquez\"}")
val jsonResult = collection.find(query).firstOrNull()

For more information, see the following Kotlin driver documentation:

With KMongo, you can create queries using property references on the data class that represents objects in a collection and infix operators that the library provides.

data class Jedi(val name: String)
val yoda = col.findOne(Jedi::name eq "Yoda")
// Compile error (2 is not a String)
val error = col.findOne(Jedi::name eq 2)
// Use property reference with instances
val yoda2 = col.findOne(yoda::name regex "Yo.*")

KMongo also supports string queries that let you construct queries with MongoDB Query Language:

import org.litote.kmongo.MongoOperator.lt
import org.litote.kmongo.MongoOperator.match
import org.litote.kmongo.MongoOperator.regex
import org.litote.kmongo.MongoOperator.sample
val yoda = col.findOne("{name: {$regex: 'Yo.*'}}")!!
val luke = col.aggregate<Jedi>("""[ {$match:{age:{$lt : ${yoda.age}}}},
{$sample:{size:1}}
]""").first()

For more information, see the following KMongo documentation:

Both drivers support the use of Kotlin data classes as well as the Document class to model the data stored in a MongoDB collection. The Document class lets you model data represented in a MongoDB collection in a flexible format.

You can use data classes and Document classes to model data with the MongoDB Kotlin driver:

// With data class
data class Movie(val title: String, val year: Int, val rating: Float)
val dataClassCollection = database.getCollection<Movie>("movies")
val movieDataClass = dataClassCollection.findOneOrNull()
val movieNameDataClass = movieDataClass.title
// With Document class
val documentCollection = database.getCollection<Movie>("movies")
val movieDocument = documentCollection.findOneOrNull()
val movieTitleDocument = movieDocument.getString("title")

You can use data classes and Document classes to model data in KMongo:

// With data class
data class Movie(val title: String, val year: Int, val rating: Float)
val collection = database.getCollection<Movie>("movies")
val movieDataClass = dataClassCollection.findOne()
val movieNameDataClass = movieDataClass.title
// With Document class
val documentCollection = database.getCollection("movies")
val movieDocument = documentCollection.findOne()
val movieTitleDocument = movieDocument.getString("title")

Both drivers provide support for serializing and deserializing data objects in Kotlin to and from BSON.

You can serialize data classes in the Kotlin driver using both automatic data class codecs as well as the kotlinx.serialization library. The driver provides an efficient Bson serializer that handles the serialization of Kotlin objects to BSON data.

@Serializable
data class LightSaber(
@SerialName("_id") // Use instead of @BsonId
@Contextual val id: ObjectId?,
val color: String,
val qty: Int,
@SerialName("brand")
val manufacturer: String = "Acme" // Use instead of @BsonProperty
)

To learn more, see the Kotlin Serialization documentation.

If you use the Document class to represent your collection, you can serialize it to JSON and EJSON using the .toJson() method:

val document = Document("_id", 1).append("color", "blue")
// Serialize to JSON
document.toJson()
// Serialize to EJSON
val settings = JsonWriterSettings.builder().outputMode(JsonMode.STRICT).build()
val json = doc.toJson(settings)

To learn more about serializing data with the Document class, refer to Document Data Format - Extended JSON documentation.

You can serialize data in KMongo using the following serialization libraries:

  • Jackson (default)

  • POJO Codec engine

  • kotlinx.serialization

// Using KotlinX Serialization
@Serializable
data class Data(@Contextual val _id: Id<Data> = newId())
val json = Json { serializersModule = IdKotlinXSerializationModule }
val data = Data()
val json = json.encodeToString(data)

To learn more about the KMongo serialization methods, refer to the Object Mapping KMongo documentation.

Both drivers support synchronous and asynchronous operations.

The MongoDB Kotlin driver also has separate libraries for synchronous and asynchronous operations. However, the Kotlin driver only has built-in support for coroutines as an asynchronous paradigm. The MongoDB Kotlin driver does not currently provide support for other asynchronous paradigms such as Reactive Streams, Reactor, or RxJava2.

Driver
Package
Sync
com.mongodb.kotlin.client
Coroutines
com.mongodb.kotlin.client.coroutine

Unlike KMongo, if you want to write asynchronous code, you only need to import the relevant package.

To write synchronous code:

import com.mongodb.kotlin.client.MongoClient
// Instantiate your collection
data class Jedi(val name: String, val age: Int)
val uri = "<your-connection-string">
val mongoClient = MongoClient.create(uri)
val database = mongoClient.getDatabase("test")
val collection = database.getCollection<Jedi>("jedi")
// Synchronous operations
val jedi =a Jedi("Luke Skywalker", 19)
collection.insertOne(jedi)

To write asynchronous coroutine code:

import com.mongodb.kotlin.client.coroutine.MongoClient
// Instantiate your collection
data class Jedi(val name: String, val age: Int)
val uri = "<your-connection-string">
val mongoClient = MongoClient.create(uri)
val database = mongoClient.getDatabase("test")
val collection = database.getCollection<Jedi>("jedi")
runBlocking {
// Async operations
val jedi =a Jedi("Luke Skywalker", 19)
collection.insertOne(jedi)
}

KMongo has a core library org.litote.kmongo:kmongo with main functionality and separate companion libraries that provide asynchronous support to the core library.

KMongo supports the following asynchronous paradigms:

Async Style
Package
Reactive Streams
org.litote.kmongo:kmongo-async
Coroutines
com.mongodb.kotlin.client.coroutine and org.litote.kmongo.coroutine
Reactor
org.litote.kmongo:kmongo-reactor
RxJava2
org.litote.kmongo:kmongo-rxjava2

To write synchronous code with KMongo:

import org.litote.kmongo.*
// Instantiate your collection
data class Jedi(val name: String, val age: Int)
val client = KMongo.createClient()
val database = client.getDatabase("test")
val col = database.getCollection<Jedi>()
// Synchronous operations
col.insertOne(Jedi("Luke Skywalker", 19))
val yoda : Jedi? = col.findOne(Jedi::name eq "Yoda")

To write async coroutine code with KMongo:

import org.litote.kmongo.reactivestreams.*
import org.litote.kmongo.coroutine.*
// Instantiate your collection
data class Jedi(val name: String, val age: Int)
val client = KMongo.createClient()
val database = client.getDatabase("test")
val col = database.getCollection<Jedi>()
runBlocking {
// Async operations
col.insertOne(Jedi("Luke Skywalker", 19))
val yoda : Jedi? = col.findOne(Jedi::name eq "Yoda")
}

To learn more, refer to the Quick Start in the KMongo documentation.

Now that you have learned about the differences between KMongo and the MongoDB Kotlin driver, see the Quick Start to get started using the KMongo Kotlin driver.