diff --git a/snooty.toml b/snooty.toml index 314aed4..f6e00a7 100644 --- a/snooty.toml +++ b/snooty.toml @@ -33,3 +33,4 @@ api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}/apidocs/mo java-api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}" core-api = "{+java-api+}/apidocs/mongodb-driver-core" kotlin-docs = "https://kotlinlang.org" +serialization-version = "1.5.1" diff --git a/source/data-formats.txt b/source/data-formats.txt index 33c8179..6abdd8c 100644 --- a/source/data-formats.txt +++ b/source/data-formats.txt @@ -21,6 +21,7 @@ Specialized Data Formats :titlesonly: :maxdepth: 1 + /data-formats/serialization /data-formats/bson /data-formats/time-series @@ -30,7 +31,13 @@ Specialized Data Formats Overview -------- -You can use several types of specialized document data formats in your {+driver-short+} +The {+driver-short+} supports the ``kotlinx.serialization`` library for +serializing and deserializing {+language+} objects. You can use this +library to convert between MongoDB documents and different data formats +in your application. To learn more about serialization, see the +:ref:`kotlin-sync-serialization` guide. + +You can use several types of specialized document data formats in your application. To learn how to work with these data formats, see the following sections: diff --git a/source/data-formats/serialization.txt b/source/data-formats/serialization.txt new file mode 100644 index 0000000..a043600 --- /dev/null +++ b/source/data-formats/serialization.txt @@ -0,0 +1,302 @@ +.. _kotlin-sync-serialization: + +==================== +Kotlin Serialization +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, data model, conversion, polymorphism + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +You can use the ``kotlinx.serialization`` library when +serializing and deserializing {+language+} objects in your application. + +The driver provides an efficient ``Bson`` serializer that you can use with +classes marked with the ``@Serializable`` annotation to handle the +serialization of {+language+} objects to BSON data. + +Although you can use the {+language+} serialization ``Json`` +library, the ``Json`` serializer does *not* directly support BSON value types such +as ``ObjectId``. You must provide a custom serializer that can handle the +conversion between BSON and JSON. + +We recommend installing the ``bson-kotlinx`` library to support +custom codecs that have configurations to encode defaults, nulls, and +define class discriminators. + +.. TODO fix the link in the following note + +.. note:: + + To learn how to use the ``Codec`` interface instead of the + ``kotlinx.serialization`` library to specify custom + serialization behavior, see the Codecs guide. + + You might choose to use {+language+} serialization if you + are already familiar with the library or if you prefer + to use an idiomatic approach. + +Supported Types +~~~~~~~~~~~~~~~ + +The {+driver-short+} supports the following types: + +- All {+language+} types that are supported by the ``kotlinx.serialization`` library +- All :manual:`BSON types ` + +.. _kotlin-sync-add-serialization: + +Add the Serialization Dependencies to Your Project +-------------------------------------------------- + +You must install the official {+language+} serialization library, +``kotlinx.serialization``, to serialize and deserialize data in your +application. To learn more about this library, see the +`kotlinx.serialization GitHub repository +`__. + +Select from the following tabs to see how to add the serialization +dependencies to your project by using either :guilabel:`Gradle` or +:guilabel:`Maven`: + +.. tabs:: + + .. tab:: + :tabid: Gradle + + If you are using `Gradle `__ to manage your + dependencies, add the following to your ``build.gradle.kts`` + dependencies list: + + .. include:: /includes/data-formats/serialization-gradle-versioned.rst + + .. tab:: + :tabid: Maven + + If you are using `Maven `__ to manage your + dependencies, add the following to your ``pom.xml`` dependencies + list: + + .. include:: /includes/data-formats/serialization-maven-versioned.rst + +.. note:: bson-kotlin Dependency + + You can also optionally install the ``bson-kotlin`` dependency + through the default codec registry. This dependency uses reflection + and the codec registry to support {+language+} data classes, but it does + not support certain POJO annotations such as ``BsonDiscriminator``, + ``BsonExtraElements``, and ``BsonConstructor``. To learn more, see + the `bson-kotlin API documentation <{+java-api+}/apidocs/bson-kotlin/index.html>`__. + + Generally, we recommend that you install and use the faster + ``bson-kotlinx`` library for codec configuration. + +.. _kotlin-sync-data-class-annotation: + +Annotate Data Classes +--------------------- + +To declare a class as serializable, annotate your {+language+} data +classes with the ``@Serializable`` annotation. + +You can use your data classes in your code as you use any other data +class after you mark them as serializable. The {+driver-short+} and the +{+language+} serialization framework handle BSON serialization and +deserialization. + +This example shows a sample data class with the following annotations: + +- ``@Serializable``: Marks the class as serializable. +- ``@SerialName``: Specifies the name of the ``id`` and ``manufacturer`` properties + in the BSON document. This annotation can be used in place of the ``@BsonId`` and + ``@BsonProperty`` annotations, which are unsupported in serializable classes. +- ``@Contextual``: Marks the BSON ``id`` property to use the built-in + ``ObjectIdSerializer``. This annotation is required for the driver to + serialize BSON types correctly. + +.. code-block:: kotlin + + @Serializable + data class PaintOrder( + @SerialName("_id") // Use instead of @BsonId + @Contextual val id: ObjectId?, + val color: String, + val qty: Int, + @SerialName("brand") + val manufacturer: String = "Grumbacher" // Use instead of @BsonProperty + ) + +.. note:: POJO Annotations Limitation + + You cannot use annotations from the + ``org.bson.codecs.pojo.annotations`` package on a data class marked + with the ``@Serializable`` annotation. + +To learn more about serializable classes and available annotations, +see `Serializable classes +`__ +in the ``kotlinx.serialization`` library documentation. + +Custom Serializer Example +~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create a custom serializer to handle how your data is +represented in BSON. The {+driver-short+} uses the ``KSerializer`` +interface from the ``kotlinx.serialization`` library to implement custom +serializers. You can specify the custom serializer as the parameter to +the ``@Serializable`` annotation for a specific field. + +The following example shows how to create a custom +``KSerializer`` instance to convert a ``kotlinx.datetime.Instant`` to a +``BsonDateTime``: + +.. literalinclude:: /includes/data-formats/serialization.kt + :language: kotlin + :start-after: start-kserializer + :end-before: end-kserializer + :dedent: + +The following code shows the ``PaintOrder`` data class in which the +``orderDate`` field has an annotation that specifies the custom +serializer class defined in the preceding code: + +.. literalinclude:: /includes/data-formats/serialization.kt + :language: kotlin + :start-after: start-ks-dataclass + :end-before: end-ks-dataclass + :emphasize-lines: 5 + :dedent: + +To learn more about the methods and classes mentioned in this section, +see the following API documentation: + +- `KSerializer + <{+kotlin-docs+}/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/>`__ + in the {+language+} documentation + +- `Instant + <{+kotlin-docs+}/api/kotlinx-datetime/kotlinx-datetime/kotlinx.datetime/-instant/>`__ in the {+language+} documentation + +- `BsonEncoder + <{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-encoder/index.html>`__ + +- `BsonDecoder + <{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-decoder/index.html>`__ + +.. _kotlin-sync-custom-codec: + +Customize the Serializer Configuration +-------------------------------------- + +You can use the ``KotlinSerializerCodec`` class from the ``org.bson.codecs.kotlinx`` +package to create a codec for your ``@Serializable`` data classes and +customize what the driver stores in MongoDB. + +Use the ``BsonConfiguration`` class to define the configuration, +which can include whether to encode defaults, encode nulls, or define +class discriminators. + +To create a custom codec, your project must have the ``bson-kotlinx`` +dependency. See the :ref:`kotlin-sync-add-serialization` section of this +guide for installation instructions. + +You can define your codec by using the `KotlinSerializerCodec.create() +<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/index.html>`__ +method, then you can add the codec to the registry. + +Custom Codec Example +~~~~~~~~~~~~~~~~~~~~ + +The following example shows how to create a codec by using the +``KotlinSerializerCodec.create()`` method and then configure it to *not* +encode defaults: + +.. literalinclude:: /includes/data-formats/serialization.kt + :language: kotlin + :start-after: start-codec + :end-before: end-codec + :dedent: + +To learn more about the methods and classes mentioned in this section, +see the following API documentation: + +- `KotlinSerializerCodec + <{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/index.html>`__ + +- `KotlinSerializerCodec.create() + <{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__ + +- `BsonConfiguration + <{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-configuration/index.html>`__ + +.. _kotlin-sync-polymorphic: + +Polymorphic Serialization +------------------------- + +The {+driver-short+} natively supports serialization and deserialization +of polymorphic classes. When you mark a sealed interface and data +classes that inherit that interface with the ``@Serializable`` +annotation, the driver uses a ``KSerializer`` implementation to handle +conversion of your types to and from BSON. + +When you insert an instance of a polymorphic data class into MongoDB, +the driver adds the field ``_t``, the +discriminator field. The value of this field is the data class name. + +Polymorphic Data Classes Example +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following example creates an interface and two data classes that +inherit that interface. In the data classes, the ``id`` field is marked +with the annotations described in the +:ref:`kotlin-sync-data-class-annotation` section: + +.. literalinclude:: /includes/data-formats/serialization.kt + :language: kotlin + :start-after: start-poly-classes + :end-before: end-poly-classes + :dedent: + +Then, you can perform operations with data classes as usual. The +following example parametrizes the collection with the ``Person`` +interface, then performs operations with the polymorphic classes +``Teacher`` and ``Student``. When you retrieve documents, the driver +automatically detects the type based on the discriminator value and +deserializes them accordingly. + +.. io-code-block:: + :copyable: true + + .. input:: /includes/data-formats/serialization.kt + :language: kotlin + :start-after: start-poly-operations + :end-before: end-poly-operations + :dedent: + + .. output:: + :language: console + + Retrieving by using data classes + Teacher(id=..., name=Vivian Lee, department=History) + Student(id=..., name=Kate Parker, grade=10) + + Retrieving by using Person interface + Teacher(id=..., name=Vivian Lee, department=History) + Student(id=..., name=Kate Parker, grade=10) + + Retrieving as Document type + Document{{_id=..., _t=Teacher, name=Vivian Lee, department=History}} + Document{{_id=..., _t=Student, name=Kate Parker, grade=10}} \ No newline at end of file diff --git a/source/includes/data-formats/serialization-gradle-versioned.rst b/source/includes/data-formats/serialization-gradle-versioned.rst new file mode 100644 index 0000000..92017c4 --- /dev/null +++ b/source/includes/data-formats/serialization-gradle-versioned.rst @@ -0,0 +1,5 @@ +.. code-block:: kotlin + :caption: build.gradle.kts + + implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:{+serialization-version+}") + implementation("org.mongodb:bson-kotlinx:{+full-version+}") \ No newline at end of file diff --git a/source/includes/data-formats/serialization-maven-versioned.rst b/source/includes/data-formats/serialization-maven-versioned.rst new file mode 100644 index 0000000..04391ad --- /dev/null +++ b/source/includes/data-formats/serialization-maven-versioned.rst @@ -0,0 +1,13 @@ +.. code-block:: xml + :caption: pom.xml + + + org.jetbrains.kotlinx + kotlinx-serialization-core + {+serialization-version+} + + + org.mongodb + bson-kotlinx + {+full-version+} + \ No newline at end of file diff --git a/source/includes/data-formats/serialization.kt b/source/includes/data-formats/serialization.kt new file mode 100644 index 0000000..b942c37 --- /dev/null +++ b/source/includes/data-formats/serialization.kt @@ -0,0 +1,130 @@ +import com.mongodb.client.model.Filters +import com.mongodb.client.model.Filters.* +import com.mongodb.client.model.Updates.* +import com.mongodb.kotlin.client.MongoClient +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import org.bson.BsonDateTime +import org.bson.Document +import org.bson.codecs.kotlinx.BsonDecoder +import org.bson.codecs.kotlinx.BsonEncoder +import java.time.Instant +import org.bson.codecs.configuration.CodecRegistries +import org.bson.codecs.kotlinx.BsonConfiguration +import org.bson.codecs.kotlinx.KotlinSerializerCodec +import org.bson.types.ObjectId +import kotlinx.serialization.Serializable + +// start-kserializer +object InstantAsBsonDateTime : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantAsBsonDateTime", PrimitiveKind.LONG) + + override fun serialize(encoder: Encoder, value: Instant) { + when (encoder) { + is BsonEncoder -> encoder.encodeBsonValue(BsonDateTime(value.toEpochMilli())) + else -> throw SerializationException("Instant is not supported by ${encoder::class}") + } + } + + override fun deserialize(decoder: Decoder): Instant { + return when (decoder) { + is BsonDecoder -> Instant.ofEpochMilli(decoder.decodeBsonValue().asDateTime().value) + else -> throw SerializationException("Instant is not supported by ${decoder::class}") + } + } +} +// end-kserializer + +// start-ks-dataclass +@Serializable +data class PaintOrder( + val color: String, + val qty: Int, + @Serializable(with = InstantAsBsonDateTime::class) + val orderDate: Instant, +) +// end-ks-dataclass + +// start-poly-classes +@Serializable +sealed interface Person { + val name: String +} + +@Serializable +data class Student( + @Contextual + @SerialName("_id") + val id: ObjectId, + override val name: String, + val grade: Int, +) : Person + +@Serializable +data class Teacher( + @Contextual + @SerialName("_id") + val id: ObjectId, + override val name: String, + val department: String, +) : Person +// end-poly-classes + + +fun main() { + + val uri = "" + + val mongoClient = MongoClient.create(uri) + val database = mongoClient.getDatabase("sample_db") + val collection = database.getCollection("orders") + + // start-codec + val myCustomCodec = KotlinSerializerCodec.create( + bsonConfiguration = BsonConfiguration(encodeDefaults = false) + ) + + val registry = CodecRegistries.fromRegistries( + CodecRegistries.fromCodecs(myCustomCodec), collection.codecRegistry + ) + // end-codec + + // start-poly-operations + val collection = database.getCollection("school") + + val teacherDoc = Teacher(ObjectId(), "Vivian Lee", "History") + val studentDoc = Student(ObjectId(), "Kate Parker", 10) + + collection.insertOne(teacherDoc) + collection.insertOne(studentDoc) + + println("Retrieving by using data classes") + val resultTeacher = collection.withDocumentClass() + .find(Filters.exists("department")) + .firstOrNull() + println(resultTeacher) + + val resultStudent = collection.withDocumentClass() + .find(Filters.exists("grade")) + .firstOrNull() + println(resultStudent) + + println("\nRetrieving by using Person interface") + val resultsPerson = collection.withDocumentClass().find() + resultsPerson.forEach { result -> + println(result) + } + + println("\nRetrieving as Document type") + val resultsDocument = collection.withDocumentClass().find() + resultsDocument.forEach { result -> + println(result) + } + // end-poly-operations + +} + diff --git a/source/indexes/atlas-search-index.txt b/source/indexes/atlas-search-index.txt index e786313..38c837a 100644 --- a/source/indexes/atlas-search-index.txt +++ b/source/indexes/atlas-search-index.txt @@ -35,7 +35,7 @@ indexes: .. note:: - The Atlas Search index management methods run asynchronously, and might return before + The Atlas Search index management methods run asynchronously and might return before confirming that they ran successfully. To determine the current status of the indexes, call the ``listSearchIndexes()`` method. @@ -47,11 +47,18 @@ each of the preceding methods. Create a Search Index --------------------- -You can use the `createSearchIndex() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-search-index.html>`__ +You can use the `createSearchIndex() +<{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-search-index.html>`__ and the `createSearchIndexes() <{+api+}/com.mongodb.kotlin.client/-mongo-collection/create-search-indexes.html>`__ methods to create one or more Atlas Search indexes. -The following code example shows how to create a single index: +You can also use these methods to create Atlas Vector Search Indexes. +Atlas Vector Search enables you to perform semantic searches on vector +embeddings stored in MongoDB Atlas. To learn more about this feature, +see the :atlas:`Atlas Vector Search Overview +`. + +The following code example shows how to create an Atlas Search index: .. literalinclude:: /includes/indexes/indexes.kt :language: kotlin diff --git a/source/whats-new.txt b/source/whats-new.txt index a5743df..bc33a32 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -120,10 +120,9 @@ Improvements in 5.1 New Features in 5.1 ~~~~~~~~~~~~~~~~~~~ -- Support for polymorphic serialization. - -.. TODO: Add link to Kotlin Serialization page when ready: https://jira.mongodb.org/browse/DOCSP-42666 . -.. ex. To learn more, see the :ref:`kotlin-sync-polymorphic` section of the Kotlin Sync Serialization guide. +- Support for polymorphic serialization. To learn more, see the + :ref:`kotlin-sync-polymorphic` section of the {+language+} + Serialization guide. - Introduces the ``serverMonitoringMode`` connection URI option. To learn more, see the :ref:`kotlin-sync-connection-options` guide.