|
| 1 | +==================================== |
| 2 | +Field Names with (``.``) and (``$``) |
| 3 | +==================================== |
| 4 | + |
| 5 | +.. default-domain:: mongodb |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 1 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | +.. _crud-concepts-dot-dollar-considerations: |
| 14 | + |
| 15 | +Overview |
| 16 | +-------- |
| 17 | + |
| 18 | +MongoDB 5.0 adds improved support for field names that are (``$``) |
| 19 | +prefixed or that contain (``.``) characters. The validation rules for |
| 20 | +storing data have been updated to make it easier to work with data |
| 21 | +sources that use these characters. |
| 22 | + |
| 23 | +In most cases data that has been stored using field names like these |
| 24 | +is not directly accessible. You need to use helper methods like |
| 25 | +:expression:`$getField`, :expression:`$setField`, and |
| 26 | +:expression:`$literal` in queries that access those fields. |
| 27 | + |
| 28 | +The field name validation rules are not the same for all types of |
| 29 | +storage operations. This page summarizes how different insert and |
| 30 | +update operations handle (``$``) prefixed field names. |
| 31 | + |
| 32 | +Insert operations |
| 33 | +----------------- |
| 34 | + |
| 35 | +(``$``) prefixed fields are permitted as top level and nested field |
| 36 | +names for inserts. |
| 37 | + |
| 38 | +.. code-block:: javascript |
| 39 | + :emphasize-lines: 3 |
| 40 | + |
| 41 | + db.sales.insertOne( { |
| 42 | + "$price": 50.00, |
| 43 | + "quantity": 30 |
| 44 | + } ) |
| 45 | + |
| 46 | +(``$``) prefixed fields are permitted on inserts using otherwise |
| 47 | +reserved words. Operator names like :update:`$inc` can be used as |
| 48 | +field names as well as words like ``id``, ``db``, and ``ref``. |
| 49 | + |
| 50 | +.. code-block:: javascript |
| 51 | + :emphasize-lines: 2, 4-6 |
| 52 | + |
| 53 | + db.books.insertOne( { |
| 54 | + "$id": "h1961-01", |
| 55 | + "location": { |
| 56 | + "$db": "novels", |
| 57 | + "$ref": "2007042768", |
| 58 | + "$inc": true |
| 59 | + } } ) |
| 60 | + |
| 61 | +An update which creates a new document during an :term:`upsert` is |
| 62 | +treated as an ``insert`` rather than an ``update`` for field name |
| 63 | +validation. :term:`Upserts <upsert>` can accept (``$``) prefixed |
| 64 | +fields. However, :term:`upserts <upsert>` are a special case and |
| 65 | +similar update operations may cause an error if the ``match`` portion |
| 66 | +of the update selects an existing document. |
| 67 | + |
| 68 | +This code sample has ``upsert: true`` so it will insert a new document |
| 69 | +if the collection doesn't already contain a document that matches the |
| 70 | +query term, ``{ "date": "2021-07-07" }``. If this sample code matches |
| 71 | +an existing document, the update will fail since ``$hotel`` is (``$``) |
| 72 | +prefixed. |
| 73 | + |
| 74 | +.. code-block:: javascript |
| 75 | + :emphasize-lines: 5 |
| 76 | + |
| 77 | + db.expenses.updateOne( |
| 78 | + { "date": "2021-07-07" }, |
| 79 | + { $set: { |
| 80 | + "phone": 25.17, |
| 81 | + "$hotel": 320.10 |
| 82 | + } }, |
| 83 | + { upsert: true } |
| 84 | + ) |
| 85 | + |
| 86 | +Document Replacing Updates |
| 87 | +-------------------------- |
| 88 | + |
| 89 | +Update operators either replace existing fields with new documents |
| 90 | +or else modify those fields. In cases where the update performs a |
| 91 | +replacement, (``$``) prefixed fields are not permitted as top level |
| 92 | +field names. |
| 93 | + |
| 94 | +Consider a document like |
| 95 | + |
| 96 | +.. code-block:: javascript:: |
| 97 | + |
| 98 | + { |
| 99 | + "_id": "E123", |
| 100 | + "address": { |
| 101 | + "$number": 123, |
| 102 | + "$street": "Elm Road" |
| 103 | + }, |
| 104 | + "$rooms": { |
| 105 | + "br": 2, |
| 106 | + "bath": 1 |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | +You could use an update operator that replaces an existing document to |
| 111 | +modify the ``address.$street`` field but you could not update the |
| 112 | +``$rooms`` field that way. |
| 113 | + |
| 114 | +.. code-block:: |
| 115 | + |
| 116 | + db.housing.updateOne( |
| 117 | + { "_id": "E123" }, |
| 118 | + { $set: { "address.$street": "Elm Ave" } } |
| 119 | + ) |
| 120 | + |
| 121 | +Use :expression:`$setField` as part of an aggregation pipeline to |
| 122 | +:ref:`update top level <dotDollar-aggregate-update>` (``$``) prefixed |
| 123 | +fields like ``$rooms``. |
| 124 | + |
| 125 | +Document Modifying Updates |
| 126 | +-------------------------- |
| 127 | + |
| 128 | +When an update modifies, rather than replaces, existing document |
| 129 | +fields, (``$``) prefixed fields can be top level field names. |
| 130 | +Subfields can be accessed directly, but you need a helper method to |
| 131 | +access the top level fields. |
| 132 | + |
| 133 | +.. seealso:: |
| 134 | + |
| 135 | + :expression:`$getField`, :expression:`$setField`, |
| 136 | + :expression:`$literal`, :pipeline:`$replaceWith` |
| 137 | + |
| 138 | +Consider a collection with documents like this inventory record: |
| 139 | + |
| 140 | +.. code-block:: |
| 141 | + :copyable: false |
| 142 | + |
| 143 | + { |
| 144 | + _id: ObjectId("610023ad7d58ecda39b8d161"), |
| 145 | + "part": "AB305", |
| 146 | + "$bin": 200, |
| 147 | + "quantity": 100, |
| 148 | + "pricing": { sale: true, "$discount": 60 } |
| 149 | + } |
| 150 | + |
| 151 | +The ``pricing.$discount`` subfield can be queried directly. |
| 152 | + |
| 153 | +.. code-block:: |
| 154 | + |
| 155 | + db.inventory.findAndModify( { |
| 156 | + query: { "part": { $eq: "AB305" } }, |
| 157 | + update: { $inc: { "pricing.$discount": 10 } } |
| 158 | + } ) |
| 159 | + |
| 160 | + |
| 161 | +Use :expression:`$getField` and :expression:`$literal` to access the |
| 162 | +value of the top level ``$bin`` field. |
| 163 | + |
| 164 | +.. code-block:: |
| 165 | + :emphasize-lines: 3 |
| 166 | + |
| 167 | + db.inventory.findAndModify( { |
| 168 | + query: { $expr: { |
| 169 | + $eq: [ { $getField: { $literal: "$bin" } }, 200 ] |
| 170 | + } }, |
| 171 | + update: { $inc: { "quantity": 10 } } |
| 172 | + } ) |
| 173 | + |
| 174 | +.. _dotDollar-aggregate-update: |
| 175 | + |
| 176 | +Updates Using Aggregation Pipelines |
| 177 | +----------------------------------- |
| 178 | + |
| 179 | +Use :expression:`$setField`, :expression:`$getField`, and |
| 180 | +:expression:`$literal` in the :pipeline:`$replaceWith` stage to modify |
| 181 | +(``$``) prefixed fields in an aggregation :term:`pipeline`. |
| 182 | + |
| 183 | +Consider a collection of school records like: |
| 184 | + |
| 185 | +.. code-block:: javascript |
| 186 | + :copyable: false |
| 187 | + |
| 188 | + { |
| 189 | + "_id": 100001, |
| 190 | + "$term": "fall", |
| 191 | + "registered": true, |
| 192 | + "grade": 4 |
| 193 | + } |
| 194 | + |
| 195 | +Create a new collection for the spring semester using a |
| 196 | +:term:`pipeline` to update the (``$``) prefixed ``$term`` field. |
| 197 | + |
| 198 | +.. code-block:: javascript |
| 199 | + :emphasize-lines: 3-5 |
| 200 | + |
| 201 | + db.school.aggregate( [ |
| 202 | + { $match: { "registered": true } }, |
| 203 | + { $replaceWith: { |
| 204 | + $setField: { |
| 205 | + field: { $literal: "$term" }, |
| 206 | + input: "$$ROOT", |
| 207 | + value: "spring" |
| 208 | + } } }, |
| 209 | + { $out: "spring2022" } |
| 210 | + ] ) |
| 211 | + |
| 212 | +General Restrictions |
| 213 | +-------------------- |
| 214 | + |
| 215 | +In addition to the storage validation rules above, there are some |
| 216 | +general restrictions on using (``$``) prefixed field names. These |
| 217 | +fields cannot: |
| 218 | + |
| 219 | +- Be indexed |
| 220 | +- Be used as part of a shard key |
| 221 | +- Be validated using :query:`$jsonSchema` |
| 222 | +- Be be modified with an escape sequence |
| 223 | +- Be used with |
| 224 | + :driver:`Field Level Encryption </security/client-side-field-level-encryption-guide>` |
| 225 | +- Be used as a subfield in an ``_id`` document |
| 226 | + |
| 227 | +.. include:: /includes/warning-possible-data-loss.rst |
| 228 | + |
0 commit comments