diff --git a/docs/website/docs/tutorials/from-kmongo/index.md b/docs/website/docs/tutorials/from-kmongo/index.md index d535dbe0..4f304a20 100644 --- a/docs/website/docs/tutorials/from-kmongo/index.md +++ b/docs/website/docs/tutorials/from-kmongo/index.md @@ -245,3 +245,7 @@ After: {"name": {"$eq": "Bob"}} ``` This transparency makes debugging complex requests much easier. + +## Get started + +You can start your KtMongo journey with our [**getting started guide**](setup.md). diff --git a/docs/website/docs/tutorials/from-kmongo/setup.md b/docs/website/docs/tutorials/from-kmongo/setup.md new file mode 100644 index 00000000..137779c6 --- /dev/null +++ b/docs/website/docs/tutorials/from-kmongo/setup.md @@ -0,0 +1,75 @@ +# Configure your existing KMongo project to use the new KtMongo DSL + +!!! info "" + **This page is about configuring KtMongo in an existing project using the deprecated library [KMongo](https://litote.org/kmongo/).** + + - To learn why you should migrate, [read the dedicated article](index.md). + - If you are using the official MongoDB driver for Java or for Kotlin, [see our dedicated guide](../official/index.md). + - If you're unsure in which situation you are, [see our dedicated guide](../index.md). + +## Dependencies + +KtMongo and KMongo are compatible, meaning that both can be used in the same project. Additionally, we provide dedicated modules with simple conversion functions. + +=== "Without coroutines" + + Add the dependency ([list of versions](../../news)): + ```kotlin + implementation("dev.opensavvy.ktmongo:driver-sync-kmongo:VERSION") + ``` + + This will add the [`MongoCollection.asKtMongo()`](../../api/driver-sync-kmongo/opensavvy.ktmongo.sync.kmongo/as-kt-mongo.md) extension function which converts from a KMongo `MongoCollection` to a KtMongo [`JvmMongoCollection`](../../api/driver-sync/opensavvy.ktmongo.sync/-jvm-mongo-collection/index.md). + +=== "With coroutines" + + Add the dependency ([list of versions](../../news)): + ```kotlin + implementation("dev.opensavvy.ktmongo:driver-coroutines-kmongo:VERSION") + ``` + + This will add the [`MongoCollection.asKtMongo()`](../../api/driver-coroutines-kmongo/opensavvy.ktmongo.coroutines.kmongo/as-kt-mongo.md) extension function which converts from a KMongo `MongoCollection` to a KtMongo [`JvmMongoCollection`](../../api/driver-coroutines/opensavvy.ktmongo.coroutines/-jvm-mongo-collection/index.md). + +## Serialization + +Under the hood, both KMongo and KtMongo rely on the official driver's `Codec` system. This means you can use KtMongo and your objects will be serialized exactly in the same way. + +However, the `Codec` system doesn't provide a way for a library to observe how the data is structured. This means that special annotations from serialization libraries that change the structure of the objects require explicit configuration. By default, the KtMongo compatibility module for KMongo adds support for the annotations: + +- `@kotlinx.serialization.SerialName` from KotlinX.Serialization. +- `@org.bson.codecs.pojo.annotations.BsonId` from the official MongoDB Java driver. + +If you use other annotations (for example with Jackson), you will need to customize KtMongo to be aware of them. To learn more, see [`PropertyNameStrategy`](../../api/dsl/opensavvy.ktmongo.dsl.path/-property-name-strategy/index.md) which is an optional argument to `MongoCollection.asKtMongo()`. + +The annotations listed above are only supported by the KMongo compatibility module. +You can learn more about other serialization aspects in our guides for the official drivers, since they share the same system: + +- [Using KotlinX.Serialization](../official/serialization-kotlinx.md) +- [Using reflection](../official/serialization-reflection.md) +- [Using any other library](../official/serialization-custom.md) + +## Mixed usage + +The KtMongo DSL is only available on KtMongo types. For example, if you have a KMongo collection, you should use the `asKtMongo()` function to obtain the KtMongo equivalent. + +```kotlin +// Before +users.find( + User::name eq "Bob" +) + +// After +users.asKtMongo().find { + User::name eq "Bob" +} +``` + +!!! warning "Avoid mixing both libraries in a single request" + KtMongo and KMongo can be used together in the same project, but not in the same request. Although both libraries use the syntax `User::profile / Profile::name` and have many similar operators, they are distinct implementations that do not understand each other. + + KtMongo operators are always used within a DSL block, in which they have resolution priority. + +If you want to use KtMongo only in place where its DSL is more advanced than KMongo's, you can call `.asKtMongo()` on each operation where you want to switch library. + +If you want to migrate, you can call `.asKtMongo()` early in your program (when instantiating collections) and inject KtMongo's types everywhere. Note that there is no reverse operation (get a KMongo type from a KtMongo type) due to a limitation of the official Kotlin driver. + +We recommend migrating your repositories one by one over multiple versions. This ensures that you can catch behavior differences early in case there are any and avoids the pressure of delivering a large refactor. diff --git a/docs/website/docs/tutorials/official/index.md b/docs/website/docs/tutorials/official/index.md index cb362f89..84431643 100644 --- a/docs/website/docs/tutorials/official/index.md +++ b/docs/website/docs/tutorials/official/index.md @@ -4,7 +4,7 @@ KtMongo is a modern DSL for interacting with MongoDB in Kotlin. On the JVM, KtMo This guide shows you how to create an application that uses the KtMongo driver to connect to a MongoDB instance and interact with data. -If you are a KMongo user, read the [KMongo migration guide](from-kmongo/setup.md) instead. +If you are a KMongo user, read the [KMongo migration guide](../from-kmongo/setup.md) instead. ??? tip "TL;DR" If you're used to Kotlin projects using Gradle, here are the important steps: diff --git a/docs/website/mkdocs.yml b/docs/website/mkdocs.yml index 5443ca3a..1e41a3e9 100644 --- a/docs/website/mkdocs.yml +++ b/docs/website/mkdocs.yml @@ -125,6 +125,7 @@ nav: - Migrating from KMongo: - tutorials/from-kmongo/index.md + - Get started: tutorials/from-kmongo/setup.md - Finding data: tutorials/from-kmongo/find.md - Nested documents: tutorials/from-kmongo/nested-fields.md - Updating data: tutorials/from-kmongo/update.md -- 2.51.2 From 6b03ef77b7fedba458154d257014e025b01cedc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Thu, 28 May 2026 21:47:15 +0200 Subject: [PATCH 2/4] docs(website): Link to the reference in the KMongo migration explanation --- docs/website/docs/tutorials/from-kmongo/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/website/docs/tutorials/from-kmongo/index.md b/docs/website/docs/tutorials/from-kmongo/index.md index 4f304a20..f56530b7 100644 --- a/docs/website/docs/tutorials/from-kmongo/index.md +++ b/docs/website/docs/tutorials/from-kmongo/index.md @@ -214,7 +214,7 @@ KtMongo protects against both of these errors at compile-time. We strive for making KtMongo's documentation as comprehensive as possible. In particular: -- The documentation is split between the website (this page) and the reference. +- The documentation is split between the guides (this page) and the [reference](../../api/index.md). - The website provides guides to discover a concept, then a feature page describing the concepts further, and finally the reference provides an exhaustive description of all methods. - Most methods in the reference have at least one example. - Most methods in the reference have a link to the official MongoDB documentation. -- 2.51.2 From dd15b39eae28df430cf2f000fb0a447be735b018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Thu, 28 May 2026 21:48:49 +0200 Subject: [PATCH 3/4] feat(driver-xxx-kmongo): Allow overriding the PropertyNameStrategy --- driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt | 8 ++++++-- driver-sync-kmongo/src/jvmMain/kotlin/KMongoExt.kt | 7 +++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt b/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt index 160a954a..d9a13705 100644 --- a/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt +++ b/driver-coroutines-kmongo/src/jvmMain/kotlin/KMongoExt.kt @@ -19,9 +19,13 @@ package opensavvy.ktmongo.coroutines.kmongo import com.mongodb.reactivestreams.client.MongoCollection import opensavvy.ktmongo.coroutines.JvmMongoCollection import opensavvy.ktmongo.coroutines.asKtMongo +import opensavvy.ktmongo.dsl.path.PropertyNameStrategy +import opensavvy.ktmongo.utils.kmongo.KMongoNameStrategy /** * Converts a collection from the official Java MongoDB driver into a KtMongo collection. */ -inline fun MongoCollection.asKtMongo(): JvmMongoCollection = - com.mongodb.kotlin.client.coroutine.MongoCollection(this).asKtMongo() +inline fun MongoCollection.asKtMongo( + nameStrategy: PropertyNameStrategy = KMongoNameStrategy(), +): JvmMongoCollection = + com.mongodb.kotlin.client.coroutine.MongoCollection(this).asKtMongo(nameStrategy) diff --git a/driver-sync-kmongo/src/jvmMain/kotlin/KMongoExt.kt b/driver-sync-kmongo/src/jvmMain/kotlin/KMongoExt.kt index d35e7f86..76c5022f 100644 --- a/driver-sync-kmongo/src/jvmMain/kotlin/KMongoExt.kt +++ b/driver-sync-kmongo/src/jvmMain/kotlin/KMongoExt.kt @@ -17,6 +17,7 @@ package opensavvy.ktmongo.sync.kmongo import com.mongodb.kotlin.client.MongoCollection +import opensavvy.ktmongo.dsl.path.PropertyNameStrategy import opensavvy.ktmongo.sync.JvmMongoCollection import opensavvy.ktmongo.sync.asKtMongo import opensavvy.ktmongo.utils.kmongo.KMongoNameStrategy @@ -24,5 +25,7 @@ import opensavvy.ktmongo.utils.kmongo.KMongoNameStrategy /** * Converts a collection from the official Java MongoDB driver into a KtMongo collection. */ -inline fun com.mongodb.client.MongoCollection.asKtMongo(): JvmMongoCollection = - MongoCollection(this).asKtMongo(nameStrategy = KMongoNameStrategy()) +inline fun com.mongodb.client.MongoCollection.asKtMongo( + nameStrategy: PropertyNameStrategy = KMongoNameStrategy(), +): JvmMongoCollection = + MongoCollection(this).asKtMongo(nameStrategy = nameStrategy) -- 2.51.2 From bbbe6987f0ab4e562d5206aa07e33018cfd09069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20=E2=80=9CCLOVIS=E2=80=9D=20Canet?= Date: Thu, 28 May 2026 23:56:05 +0200 Subject: [PATCH 4/4] =?UTF-8?q?docs(website):=20Rewrite=20KMongo=20?= =?UTF-8?q?=E2=86=92=20KtMongo=20syntax=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/tutorials/from-kmongo/find.md | 93 ------- .../docs/tutorials/from-kmongo/migrate.md | 249 ++++++++++++++++++ .../tutorials/from-kmongo/nested-fields.md | 52 ---- docs/website/mkdocs.yml | 4 +- 4 files changed, 250 insertions(+), 148 deletions(-) delete mode 100644 docs/website/docs/tutorials/from-kmongo/find.md create mode 100644 docs/website/docs/tutorials/from-kmongo/migrate.md delete mode 100644 docs/website/docs/tutorials/from-kmongo/nested-fields.md diff --git a/docs/website/docs/tutorials/from-kmongo/find.md b/docs/website/docs/tutorials/from-kmongo/find.md deleted file mode 100644 index e231da77..00000000 --- a/docs/website/docs/tutorials/from-kmongo/find.md +++ /dev/null @@ -1,93 +0,0 @@ -# Converting your KMongo queries to KtMongo - -KMongo operators primarily work by accepting varargs which are combined into `Bson` documents. - -KtMongo operators primarily work by exposing a DSL in which operators can be bound. - -For example, - -```kotlin title="Using KMongo" -collection.findOne( - and( - User::name.exists(), - User::age gt 18, - ) -) -``` - -becomes: - -```kotlin title="Using KtMongo" -collection.findOne { - and { - User::name.exists() - User::age gt 18 - } -} -``` - -Note how: - -- the parentheses become braces, -- the trailing commas are gone. - -## Default composition operators - -Each operation has a default operator when multiple values are passed. For example, `findOne` has a default of `$and`, meaning that these two snippets are identical: - -```kotlin -collection.findOne { - and { - User::name.exists() - User::age gt 18 - } -} -``` - -```kotlin -collection.findOne { - User::name.exists() - User::age gt 18 -} -``` - -## Complex requests - -One advantage of the DSL syntax is using conditionals and loops directly into the request itself, making complex requests much easier to write. - -Here's an example of a complex request with KMongo: - -```kotlin title="Using KMongo" -val bson = ArrayList() - -if (criteria.name != null) - bson.add(User::name eq criteria.name) - -if (criteria.age != null) - bson.add(User::age eq criteria.age) - -collection.findOne(and(bson)) -``` - -When these kinds of requests grow, they become harder to understand because the criteria are defined further from the operation call. - -With KtMongo, everything is co-located and the intermediate list is eliminated: - -```kotlin title="Using KtMongo" -collection.findOne { - if (criteria.name != null) - User::name eq criteria.name - - if (criteria.age != null) - User::age eq criteria.age -} -``` - -Since the specific use-case of optional filter criteria is so common, KtMongo offers specific operators that only apply to the request when their argument is non-`null`: - -```kotlin title="Using KtMongo" -collection.findOne { - User::name eqNotNull criteria.name - User::age eqNotNull criteria.age -} -``` diff --git a/docs/website/docs/tutorials/from-kmongo/migrate.md b/docs/website/docs/tutorials/from-kmongo/migrate.md new file mode 100644 index 00000000..9aa68240 --- /dev/null +++ b/docs/website/docs/tutorials/from-kmongo/migrate.md @@ -0,0 +1,249 @@ +# Convert your existing KMongo queries to KtMongo's DSL + +!!! info "" + **This page is about converting queries written with the deprecated library [KMongo](https://litote.org/kmongo/) to use KtMongo's DSL instead.** + + - To learn why you should migrate, [read the dedicated article](index.md). + - To configure KtMongo in your existing KMongo project, [see our dedicated guide](setup.md). + - If you are using the official MongoDB driver for Java or for Kotlin, [see our dedicated guide](../official/index.md). + - If you're unsure in which situation you are, [see our dedicated guide](../index.md). + +## Access the KtMongo DSL + +The KMongo library offers extension functions directly on top of the official Java driver. This leads to overload pollution: you may confuse methods from the official Java driver with those from KMongo, and vice versa. + +Instead, KtMongo has its own collection type. KtMongo methods are only available on that type. Therefore, it's never ambiguous which library you're trying to call. + +To convert from a KMongo collection to a KtMongo collection, you can use the `MongoCollection.asKtMongo()` extension function. +To learn more, [see the dedicated guide](setup.md). + +## Main differences + +### Vararg vs DSL + +The main difference between KMongo operators and the KtMongo DSL is that KMongo accepts parameters as a `vararg`, whereas KtMongo uses a DSL: + +```kotlin title="Using KMongo" +collection.findOne( + and( + User::name.exists(), + User::age gt 18, + ) +) +``` + +```kotlin title="Using KtMongo" +collection.findOne { + and { + User::name.exists() + User::age gt 18 + } +} +``` + +Notice how: + +- the parentheses become braces, +- the trailing commas are gone. + +The KMongo operators are top-level functions that return an opaque type-unsafe `Bson` type, whereas KtMongo operators directly attach themselves into the current operation and have no return value. + +As a consequence, KtMongo is more type-safe: + +- Operators will not compile when called in the wrong context (e.g. `$eq` in an update). +- Operators will correctly disambiguate between different contexts, even if they have the exact same Kotlin syntax (e.g. `$eq` in find and `$eq` in an aggregation). + +### Dynamic queries + +This allows us to more easily create complex queries. Compare the following KMongo query: +```kotlin title="Using KMongo" +collection.findOne( + and( + listOfNotNull( + (User::name eq criteria.name).takeUnless { criteria.name == null }, + (User::age eq criteria.age).takeUnless { criteria.age == null }, + ) + ) +) +``` + +When these kinds of requests grow, they become harder to understand and easier to get wrong. Also, with these way of writing them, the criteria is instantiated even if it isn't used later. + +With KtMongo, everything is co-located and the intermediate list is eliminated: + +```kotlin title="Using KtMongo" +collection.findOne { + if (criteria.name != null) + User::name eq criteria.name + + if (criteria.age != null) + User::age eq criteria.age +} +``` + +Note that you also don't need the root `and()` operator with KtMongo, because KtMongo automatically detects that a `findOne` with multiple criteria must be using an `$and` operator. + +For the specific case of optional criteria, KtMongo provides [a dedicated syntax that is even more concise](../../features/optional-filters.md). + +### Extracting into functions + +To make parts of complex queries reusable, it is common to extract them as independent functions. +In KMongo, this is done by creating a function that returns a `Bson` instance: +```kotlin title="Using KMongo" +fun filter(criteria: UserFilterCriteria): Bson = and( + User::name eq criteria.name, + User::age gte criteria.minAge, +) +``` + +KtMongo needs to know the type of the document for type-safety reasons, and operators automatically bind themselves to the current DSL. Each DSL provides its own scope: +```kotlin title="Using KtMongo" +fun FilterQuery.filter(criteria: UserFilterCriteria) = and { + User::name eq criteria.name + User::age gte criteria.minAge +} +``` + +### Options + +Options are passed as their own DSL and are usually written first. For example, the following query: +```kotlin title="Using KMongo" +users.countDocuments( + filter = User::name eq "Patrick", + options = CountOptions().limit(10) +) +``` +is written: +```kotlin title="Using KtMongo" +users.count( + options = { limit(10) }, + filter = { + User::name eq "Patrick" + } +) +``` + +## Nested fields + +### Nested documents + +Both KMongo and KtMongo use the syntax `User::profile / Profile::name`. + +!!! danger + Although both libraries use the same syntax, they are implemented differently and do not recognize each other. Do not use the KMongo `/` operator in a KtMongo query, as it will result in an incorrect query. Using the KtMongo operator in a KMongo query will not compile. + + The KMongo `/` operator needs to be explicitly imported. The KtMongo `/` operator is only available within a KtMongo command's DSL and doesn't need an import. + + You can follow the progress of lifting this restriction [here](https://gitlab.com/opensavvy/ktmongo/-/work_items/96). + +### Unsafe nested documents + +To unsafely access a nested field (without type-safety), KMongo adds the `%` operator: `User::profile % Car::name`. + +Instead, KtMongo provides the [`unsafe` extension function](../../api/dsl/opensavvy.ktmongo.dsl.path/-field-dsl/index.md#unsafe): `User::profile unsafe Car::name`. + +You can also [unsafely cast](../../api/dsl/opensavvy.ktmongo.dsl.path/-field/index.md#unsafecast) a field to another type and then use any type-safe operator: `User::profile.unsafeCast() / Car::name`. + +### Nested arrays + +| MongoDB syntax | KMongo | KtMongo | Meaning | +|---------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +| `"friends.2"` | `User::friends.pos(2)` | [`User::friends[2]`](../../api/dsl/opensavvy.ktmongo.dsl.path/-field-dsl/index.md#get) | The friend at index 2. | +| `"friends.$"` | `User::friends.posOp` | [`User::friends.selected`](../../api/dsl/opensavvy.ktmongo.dsl.query/-upsert-query/index.md#selected)
Available only in updates. | The first friend selected
by the update's filter. | +| `"friends.$[]"` | `User::friends.allPosOp` | [`User::friends.all`](../../api/dsl/opensavvy.ktmongo.dsl.query/-update-query/index.md#all)
Available only in updates. | All friends. | +| `"friends.$[]"` | `User::friends`
`.filteredPosOp("id")` | [`User::friends.filter {}`](../../api/dsl/opensavvy.ktmongo.dsl.query/-update-query/index.md#filter)
Available only in updates. | Creates an array filter. | + +## Find + +Use `find()` (returns a cursor) and `findOne()` (returns a single element). + +As explained above, the `vararg` becomes a DSL. +```kotlin title="Using KMongo" +collection.findOne( + and( + User::name.exists(), + User::age gt 18, + ) +) +``` + +Filter operations with multiple criteria imply a root `$and`: +```kotlin title="Using KtMongo" +collection.findOne { + User::name.exists() + User::age gt 18 +} +``` + +Complex query building can be replaced by simple `if` and `for` directly within the DSL. + +## Update (simple) + +KtMongo provides: + +- `updateMany`: updates all documents that match a filter. +- `updateOne`: updates a single document that matches a filter. +- `upsertOne`: upserts a single document that matches a filter. + +They each follow the same pattern as the `find()` methods. + +```kotlin title="With KMongo" +collection.updateMany( + filter = and( + User::name.exists(), + User::age gt 18, + ), + set( + User::isLegal setTo true, + ) +) +``` + +```kotlin title="With KtMongo" +collection.updateMany( + filter = { + User::name.exists() + User::age gt 18 + }, + update = { + User::isLegal set true + } +) +``` + +Unlike in KMongo, there is no need to combine multiple operators yourself. +```kotlin title="With KMongo" +collection.updateMany( + filter = …, + set( + User::name setTo "foo", + User::isLegal setTo true, + ), + inc( + User::age setTo 1 + ) +) +``` + +```kotlin title="With KtMongo" +collection.updateMany( + filter = { … }, + update = { + User::name set "foo" + User::isLegal set true + User::age inc 1 + } +) +``` + +## Update (aggregation pipeline) + +MongoDB provides two syntaxes for updates: one with regular query operators, and one with aggregation operators. + +In KMongo, they are differentiated by the overload of `updateOne` (or similar): the overload that takes a `vararg Bson` uses the query syntax, but the overload that takes a `List` uses the aggregation syntax. This is confusing, especially because KMongo doesn't provide the aggregation operators out of the box. + +KtMongo uses a suffix to use the aggregation pipeline syntax: + +- `updateManyWithPipeline`: updates all documents that match a filter. +- `updateOneWithPipeline`: updates a single document that matches a filter. +- `upsertOneWithPipeline`: upserts a single document that matches a filter. diff --git a/docs/website/docs/tutorials/from-kmongo/nested-fields.md b/docs/website/docs/tutorials/from-kmongo/nested-fields.md deleted file mode 100644 index 7f13ae6c..00000000 --- a/docs/website/docs/tutorials/from-kmongo/nested-fields.md +++ /dev/null @@ -1,52 +0,0 @@ -# Referring to nested documents - -For the rest of this article, let's take the following example, in which the collection we're interested in is `User`: - -```kotlin -class User( - val name: String, - val country: Country, - val pets: List, -) - -class Country( - val id: String, - val code: String, -) - -class Pet( - val id: String, - val name: String, -) -``` - -!!! tip - Note that the KtMongo syntax is only available within the various DSL methods offered by the library. - -## Top-level field - -Referring to a non-nested field is identical with KMongo and KtMongo: - -```kotlin -User::name eq "foo" -``` - -## Nested field - -Referring to nested documents is identical with both libraries: - -```kotlin -User::country / Country::code eq "FR" -``` - -## Array items - -Referring to an array item by index uses the `get` operator: - -```kotlin title="Using KMongo" -User::pets.pos(4) / Pet::name eq "Chocolat" -``` - -```kotlin title="Using KtMongo" -User::pets[4] / Pet::name eq "Chocolat" -``` diff --git a/docs/website/mkdocs.yml b/docs/website/mkdocs.yml index 1e41a3e9..3799ae0b 100644 --- a/docs/website/mkdocs.yml +++ b/docs/website/mkdocs.yml @@ -126,9 +126,7 @@ nav: - Migrating from KMongo: - tutorials/from-kmongo/index.md - Get started: tutorials/from-kmongo/setup.md - - Finding data: tutorials/from-kmongo/find.md - - Nested documents: tutorials/from-kmongo/nested-fields.md - - Updating data: tutorials/from-kmongo/update.md + - Convert queries: tutorials/from-kmongo/migrate.md - Kotlin Multiplatform: - tutorials/multiplatform/index.md