diff --git a/source/faq.txt b/source/faq.txt index 8ff74535..2c0cd2b7 100644 --- a/source/faq.txt +++ b/source/faq.txt @@ -25,11 +25,7 @@ developed by the MongoDB team and provides a native API for Kotlin applications to connect to MongoDB and work with data. It is implemented by wrapping the :driver:`MongoDB Java driver `. -`KMongo `__ is a popular community-developed library -for working with MongoDB from Kotlin applications. -It is also a wrapper around the Java driver. -It was created prior to the creation of the official Kotlin driver to serve -the needs of the Kotlin community. +.. include:: /includes/kmongo-description.rst The Kotlin driver was developed in collaboration with the creator of KMongo, Julien Buret, to give users an officially-supported driver. @@ -48,11 +44,13 @@ key differences: - The official driver does *not* have built-in support for `reactor `__, `rxjava2 `__, `Jackson `__, or `GSON `__. -- The official driver does not support MongoDB shell commands. +- The official driver does *not* support MongoDB shell commands. - The official driver supports type-safe queries with the Builders API, whereas KMongo uses infix functions and property references for type-safe queries. +For more detailed information, see :ref:`Migrate from KMongo `. + .. _kotlin-faq-connection-pool: How Does Connection Pooling Work in the Kotlin Driver? diff --git a/source/includes/kmongo-description.rst b/source/includes/kmongo-description.rst new file mode 100644 index 00000000..cc3ecbda --- /dev/null +++ b/source/includes/kmongo-description.rst @@ -0,0 +1,5 @@ +`KMongo `__ is a popular community-developed library +for working with MongoDB from Kotlin applications. +It is also a wrapper around the Java driver. +It was created prior to the creation of the official Kotlin driver to serve +the needs of the Kotlin community. As of July 2023, it has been marked as deprecated. \ No newline at end of file diff --git a/source/index.txt b/source/index.txt index 9ff9d615..cbfd973c 100644 --- a/source/index.txt +++ b/source/index.txt @@ -16,6 +16,7 @@ MongoDB Kotlin Driver /connection-troubleshooting /issues-and-help /compatibility + /migrate-kmongo View the Source Introduction diff --git a/source/migrate-kmongo.txt b/source/migrate-kmongo.txt new file mode 100644 index 00000000..8b8938be --- /dev/null +++ b/source/migrate-kmongo.txt @@ -0,0 +1,518 @@ +.. _kotlin-migrate-kmongo: + +=================== +Migrate from KMongo +=================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + + +Overview +-------- + +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. + +.. include:: /includes/kmongo-description.rst + +The MongoDB Kotlin driver is the officially supported and maintained MongoDB driver for +Kotlin. It is developed by the MongoDB team. + +Although both drivers :ref:`support synchronous and asynchronous operations `, +the examples on this page will use asynchronous coroutine-based operations. + +Connect to MongoDB Cluster +-------------------------- + +Both drivers let you connect to and communicate with MongoDB clusters from a +Kotlin application. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + To connect to a MongoDB cluster using the MongoDB Kotlin driver: + + .. code-block:: kotlin + + 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") + + See the :ref:`Connect to MongoDB ` documentation for more + information. + + .. tab:: + :tabid: KMongo + + To connect to a MongoDB cluster using KMongo with coroutines: + + .. code-block:: kotlin + + 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() + + Unlike the MongoDB Kotlin driver, KMongo allows the collection name to be + inferred from the data class name. + +CRUD and Aggregation +-------------------- + +Both drivers provide support for all MongoDB CRUD APIs and aggregation +operations. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + The MongoDB Kotlin driver also provides functions for all basic CRUD operations: + + .. code-block:: kotlin + + // 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: + + .. code-block:: kotlin + + data class Results(val avgAge: Double) + + val resultsFlow = collection.aggregate( + 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 :ref:`CRUD Operations ` and + :ref:`Aggregation ` documentation for more information. + + .. tab:: + :tabid: KMongo + + KMongo provides functions for all basic CRUD operations: + + .. code-block:: kotlin + + // 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: + + .. code-block:: kotlin + + val avgAge = collection.aggregate( + 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. + +Construct Queries +----------------- + +Both drivers provide support for type-safe queries using property references. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + The MongoDB Kotlin driver uses the Builders API to construct queries. + Alternatively, you can use the ``Document`` class. + + .. code-block:: kotlin + + 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("people") + + // Using Builders + val filter = and(eq("gender", "female"), gt("age", 29)) + val projection = fields(excludeId(), include("email")) + val results = collection.find(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(filter).projection(projection) + + To map a KMongo string query to the Kotlin driver, you can use the ``JsonObject`` class. + + .. code-block:: kotlin + + val query = JsonObject("{\"name\": \"Gabriel Garc\\u00eda M\\u00e1rquez\"}") + val jsonResult = collection.find(query).firstOrNull() + + For more information, see the following Kotlin driver documentation: + + - :ref:`Builders ` + - :ref:`Documents ` guide + - `JsonObject <{+api+}/apidocs/bson/org/bson/json/JsonObject.html>`__ API Documentation + + .. tab:: + :tabid: KMongo + + 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. + + .. code-block:: kotlin + + 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: + + .. code-block:: kotlin + + 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("""[ {$match:{age:{$lt : ${yoda.age}}}}, + {$sample:{size:1}} + ]""").first() + + For more information, see the following KMongo documentation: + + - `Typed Queries `_ + - `Mongo Shell Queries `__ + +Data Typing +----------- + +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. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + You can use data classes and ``Document`` classes to model data with the + MongoDB Kotlin driver: + + .. code-block:: kotlin + + // With data class + data class Movie(val title: String, val year: Int, val rating: Float) + + val dataClassCollection = database.getCollection("movies") + val movieDataClass = dataClassCollection.findOneOrNull() + val movieNameDataClass = movieDataClass.title + + // With Document class + val documentCollection = database.getCollection("movies") + val movieDocument = documentCollection.findOneOrNull() + val movieTitleDocument = movieDocument.getString("title") + + .. tab:: + :tabid: KMongo + + You can use data classes and ``Document`` classes to model data in KMongo: + + .. code-block:: kotlin + + // With data class + data class Movie(val title: String, val year: Int, val rating: Float) + + val collection = database.getCollection("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") + +Data Serialization +------------------ + +Both drivers provide support for serializing and deserializing data objects +in Kotlin to and from BSON. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + 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. + + .. code-block:: kotlin + + @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 :ref:`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: + + .. code-block:: kotlin + + 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 + :ref:`Document Data Format - Extended JSON ` documentation. + + .. tab:: + :tabid: KMongo + + You can serialize data in KMongo using the following serialization libraries: + + * ``Jackson`` (default) + * ``POJO Codec engine`` + * ``kotlinx.serialization`` + + .. code-block:: kotlin + + // Using KotlinX Serialization + @Serializable + data class Data(@Contextual val _id: Id = 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. + +.. _kotlin-sync-async-support: + +Synchronous and Asynchronous Support +------------------------------------ + +Both drivers support synchronous and asynchronous operations. + +.. tabs:: + + .. tab:: + :tabid: {+driver-long+} + + 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. + + .. list-table:: + :header-rows: 1 + :stub-columns: 1 + :widths: 10 40 + + * - 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: + + .. code-block:: kotlin + + import com.mongodb.kotlin.client.MongoClient + + // Instantiate your collection + data class Jedi(val name: String, val age: Int) + val uri = " + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("test") + val collection = database.getCollection("jedi") + + // Synchronous operations + val jedi =a Jedi("Luke Skywalker", 19) + collection.insertOne(jedi) + + To write asynchronous coroutine code: + + .. code-block:: kotlin + + import com.mongodb.kotlin.client.coroutine.MongoClient + + // Instantiate your collection + data class Jedi(val name: String, val age: Int) + val uri = " + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("test") + val collection = database.getCollection("jedi") + + runBlocking { + + // Async operations + val jedi =a Jedi("Luke Skywalker", 19) + collection.insertOne(jedi) + } + + .. tab:: + :tabid: KMongo + + 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: + + .. list-table:: + :header-rows: 1 + :stub-columns: 1 + :widths: 10 40 + + * - 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: + + .. code-block:: kotlin + + 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() + + // Synchronous operations + col.insertOne(Jedi("Luke Skywalker", 19)) + val yoda : Jedi? = col.findOne(Jedi::name eq "Yoda") + + To write async coroutine code with KMongo: + + .. code-block:: kotlin + + 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() + + 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. + +What Next? +---------- + +Now that you have learned about the differences between KMongo and the MongoDB +Kotlin driver, see the :ref:`Quick Start ` to get +started using the KMongo Kotlin driver. diff --git a/source/quick-start.txt b/source/quick-start.txt index d7a86939..cb0b143c 100644 --- a/source/quick-start.txt +++ b/source/quick-start.txt @@ -1,4 +1,4 @@ -.. _kotlin-sync-quickstart: +.. _kotlin-quickstart: =========== Quick Start