|
| 1 | +.. _scala-macros: |
| 2 | + |
| 3 | +====== |
| 4 | +Macros |
| 5 | +====== |
| 6 | + |
| 7 | +.. facet:: |
| 8 | + :name: genre |
| 9 | + :values: reference |
| 10 | + |
| 11 | +.. meta:: |
| 12 | + :keywords: representation, storage, codec |
| 13 | + |
| 14 | +.. contents:: On this page |
| 15 | + :local: |
| 16 | + :backlinks: none |
| 17 | + :depth: 2 |
| 18 | + :class: singlecol |
| 19 | + |
| 20 | +Overview |
| 21 | +-------- |
| 22 | + |
| 23 | +The {+driver-short+} allows you to use case classes to represent |
| 24 | +documents in a collection by using the ``Macros`` helper. Simple case classes and |
| 25 | +nested case classes are supported. Hierarchical modeling can be achieved |
| 26 | +by using a sealed trait or class and then by having case classes implement the |
| 27 | +parent trait. |
| 28 | + |
| 29 | +Many simple Scala types are supported and can be marshaled into their |
| 30 | +corresponding ``BsonValue`` type. The following list describes Scala types and their |
| 31 | +type-safe BSON representation: |
| 32 | + |
| 33 | +.. list-table:: |
| 34 | + :header-rows: 1 |
| 35 | + :class: compatibility-large |
| 36 | + |
| 37 | + * - Scala Type |
| 38 | + - BSON Type |
| 39 | + |
| 40 | + * - Case class |
| 41 | + - Document |
| 42 | + |
| 43 | + * - ``Iterable`` |
| 44 | + - Array |
| 45 | + |
| 46 | + * - ``Date`` |
| 47 | + - Date |
| 48 | + |
| 49 | + * - ``Boolean`` |
| 50 | + - Boolean |
| 51 | + |
| 52 | + * - ``Double`` |
| 53 | + - Double |
| 54 | + |
| 55 | + * - ``Int`` |
| 56 | + - Int32 |
| 57 | + |
| 58 | + * - ``Long`` |
| 59 | + - Int64 |
| 60 | + |
| 61 | + * - ``String`` |
| 62 | + - String |
| 63 | + |
| 64 | + * - ``Array[Byte]`` |
| 65 | + - Binary |
| 66 | + |
| 67 | + * - ``None`` |
| 68 | + - Null |
| 69 | + |
| 70 | +Creating Codecs |
| 71 | +--------------- |
| 72 | + |
| 73 | +To create a codec for your case class, use the ``Macros`` object helper |
| 74 | +methods. You should use the |
| 75 | +``Macros.createCodecProvider()`` method to create a ``CodecProvider``. A |
| 76 | +``CodecProvider`` passes the configured ``CodecRegistry`` to the underlying |
| 77 | +``Codec`` and provides access to all the configured codecs. |
| 78 | + |
| 79 | +To create a ``CodecProvider``, set the case class type when calling |
| 80 | +``createCodecProvider()`` as shown in the following code: |
| 81 | + |
| 82 | +.. code-block:: scala |
| 83 | + |
| 84 | + import org.mongodb.scala.bson.codecs.Macros |
| 85 | + |
| 86 | + case class Person(firstName: String, secondName: String) |
| 87 | + |
| 88 | + val personCodecProvider = Macros.createCodecProvider[Person]() |
| 89 | + |
| 90 | +The ``personCodecProvider`` can then be used when converted into a |
| 91 | +``CodecRegistry`` by using the ``CodecRegistries`` static helpers. The |
| 92 | +following code creates a new codec registry, combining the new ``personCodecProvider`` |
| 93 | +and the default codec registry: |
| 94 | + |
| 95 | +.. code-block:: scala |
| 96 | + |
| 97 | + import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY |
| 98 | + import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders} |
| 99 | + |
| 100 | + val codecRegistry = fromRegistries( fromProviders(personCodecProvider), DEFAULT_CODEC_REGISTRY ) |
| 101 | + |
| 102 | +The ``Macros`` helper also has an implicit ``createCodecProvider()`` method that |
| 103 | +takes the ``Class[T]`` and creates a ``CodecProvider`` from that. This method |
| 104 | +is more concise especially when defining multiple providers: |
| 105 | + |
| 106 | +.. code-block:: scala |
| 107 | + |
| 108 | + import org.mongodb.scala.bson.codecs.Macros._ |
| 109 | + import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY |
| 110 | + import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders} |
| 111 | + |
| 112 | + case class Address(firstLine: String, secondLine: String, thirdLine: String, town: String, zipCode: String) |
| 113 | + case class ClubMember(person: Person, address: Address, paid: Boolean) |
| 114 | + |
| 115 | + val codecRegistry = fromRegistries( fromProviders(classOf[ClubMember], classOf[Person], classOf[Address]), DEFAULT_CODEC_REGISTRY ) |
| 116 | + |
| 117 | +Sealed Classes and ADTs |
| 118 | +----------------------- |
| 119 | + |
| 120 | +Hierarchical class structures are supported through sealed traits and |
| 121 | +classes. Each subclass is handled specifically by the generated codec, |
| 122 | +so you only need to create a ``CodecProvider`` for the parent sealed |
| 123 | +trait or class. Internally an extra field (``_t``) is stored alongside the data |
| 124 | +so that the correct subclass can be hydrated when decoding the data. |
| 125 | +The following code is an example of a tree-like structure containing branch and leaf |
| 126 | +nodes: |
| 127 | + |
| 128 | +.. code-block:: scala |
| 129 | + |
| 130 | + sealed class Tree |
| 131 | + case class Branch(b1: Tree, b2: Tree, value: Int) extends Tree |
| 132 | + case class Leaf(value: Int) extends Tree |
| 133 | + |
| 134 | + val codecRegistry = fromRegistries( fromProviders(classOf[Tree]), DEFAULT_CODEC_REGISTRY ) |
| 135 | + |
| 136 | +Options and None Values |
| 137 | +----------------------- |
| 138 | + |
| 139 | +By default, ``Option`` values are always stored. In driver v2.1.0, new |
| 140 | +helpers were added so that ``None`` values would not be stored in the |
| 141 | +database. In the following example, the driver only stores an address if |
| 142 | +if one is present: |
| 143 | + |
| 144 | +.. code-block:: scala |
| 145 | + |
| 146 | + import org.mongodb.scala.bson.codecs.Macros |
| 147 | + |
| 148 | + case class Person(firstName: String, secondName: String, address: Option[Address]) |
| 149 | + |
| 150 | + val personCodecProvider = Macros.createCodecProviderIgnoreNone[Person]() |
| 151 | + |
| 152 | +Alternative Field Names |
| 153 | +----------------------- |
| 154 | + |
| 155 | +The ``BsonProperty`` annotation can be used to configure the BSON field |
| 156 | +key to be used for a given property. The following example uses the |
| 157 | +``BsonProperty`` annotation to change how the ``firstName`` field is stored: |
| 158 | + |
| 159 | +.. code-block:: scala |
| 160 | + |
| 161 | + case class Person(@BsonProperty("first_name") firstName: String, secondName: String) |
0 commit comments