|
| 1 | +=================== |
| 2 | +Compound Operations |
| 3 | +=================== |
| 4 | + |
| 5 | +.. default-domain:: mongodb |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 2 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | +Overview |
| 14 | +-------- |
| 15 | + |
| 16 | +In this guide, you will learn how to perform **compound operations** with the |
| 17 | +MongoDB Java driver. |
| 18 | + |
| 19 | +Compound operations consist of a read and write operation performed as one |
| 20 | +**atomic operation**. An atomic operation is an operation which either completes |
| 21 | +entirely, or does not complete at all. Atomic operations cannot partially complete. |
| 22 | + |
| 23 | +Atomic operations can help you avoid **race conditions** in your code. A |
| 24 | +race condition occurs when your code's behavior is dependent on the order of |
| 25 | +uncontrollable events. |
| 26 | + |
| 27 | +MongoDB supports the following compound operations: |
| 28 | + |
| 29 | +- Find and update one document |
| 30 | +- Find and replace one document |
| 31 | +- Find and delete one document |
| 32 | + |
| 33 | +If you need to perform more complex tasks atomically, such as reading and |
| 34 | +writing to more than one document, use **transactions**. Transactions are a |
| 35 | +feature of MongoDB and other databases that lets you define an arbitrary |
| 36 | +sequence of database commands as an atomic operation. |
| 37 | + |
| 38 | +For more information on atomic operations and atomicity, see |
| 39 | +:manual:`the MongoDB manual entry for atomicity and transactions </core/write-operations-atomicity/>`. |
| 40 | + |
| 41 | +For more information on transactions, |
| 42 | +:manual:`see the MongoDB manual entry for transactions </core/transactions/>`. |
| 43 | + |
| 44 | +How to Use Compound Operations |
| 45 | +------------------------------ |
| 46 | + |
| 47 | +This section shows how to use each compound operation with the MongoDB Java Driver. |
| 48 | + |
| 49 | +The following examples use a collection containing these two sample documents. |
| 50 | + |
| 51 | +.. code-block:: json |
| 52 | + :copyable: false |
| 53 | + |
| 54 | + {"_id": 1, "food": "donut", "color": "green"} |
| 55 | + {"_id": 2, "food": "pear", "color": "yellow"} |
| 56 | + |
| 57 | +`The full code for the following examples is available on Github here. <https://github.com/mongodb/docs-java/blob/master/source/includes/fundamentals/code-snippets/CompoundOperatorsIndividualExamples.java>`__ |
| 58 | + |
| 59 | +.. note:: Before or After the Write? |
| 60 | + |
| 61 | + By default, each compound operation returns your found document in the state |
| 62 | + before your write operation. You can retrieve your found document in the |
| 63 | + state after your write operation by using the options class corresponding to |
| 64 | + your compound operation. You can see an example of this configuration in the |
| 65 | + :ref:`Find and Replace example below <java-find-and-replace-example>`. |
| 66 | + |
| 67 | +Find and Update |
| 68 | +~~~~~~~~~~~~~~~ |
| 69 | + |
| 70 | +To find and update one document, use the ``findOneAndUpdate()`` method of the |
| 71 | +``MongoCollection`` class. The ``findOneAndUpdate()`` method returns your found |
| 72 | +document or ``null`` if no documents match your query. |
| 73 | + |
| 74 | +Example |
| 75 | +^^^^^^^ |
| 76 | + |
| 77 | +The following example uses the ``findOneAndUpdate()`` method to find a |
| 78 | +document with the ``color`` field set to ``"green"`` and update the |
| 79 | +``food`` field in that document to ``"pizza"``. |
| 80 | + |
| 81 | +The example also uses a ``FindOneAndUpdateOptions`` instance to specify the |
| 82 | +following options: |
| 83 | + |
| 84 | +- Exclude the ``_id`` field from the found document with a projection. |
| 85 | +- Specify an upsert, which inserts the query document if no documents match the query. |
| 86 | +- Set a maximum execution time of 5 seconds for this operation on the MongoDB |
| 87 | + instance. If the operation takes longer, the ``findOneAndUpdate()`` method |
| 88 | + will throw a ``MongoExecutionTimeoutException``. |
| 89 | + |
| 90 | +.. literalinclude:: /includes/fundamentals/code-snippets/CompoundOperatorsIndividualExamples.java |
| 91 | + :language: java |
| 92 | + :dedent: |
| 93 | + :start-after: start findOneAndUpdate-example |
| 94 | + :end-before: end findOneAndUpdate-example |
| 95 | + |
| 96 | +The output of the above code should look like this: |
| 97 | + |
| 98 | +.. code-block:: json |
| 99 | + :copyable: false |
| 100 | + |
| 101 | + {"food": "donut", "color": "green"} |
| 102 | + |
| 103 | +For more information on the ``Projections`` class, see our |
| 104 | +:doc:`guide on the Projections builder </fundamentals/builders/projections/>`. |
| 105 | + |
| 106 | +For more information on the upsert operation, see our |
| 107 | +:doc:`guide on upserts </fundamentals/crud/write-operations/upsert/#insert-or-update-in-a-single-operation/>`. |
| 108 | + |
| 109 | +For more information on the methods and classes in this section, see the |
| 110 | +following API documentation pages: |
| 111 | + |
| 112 | +- :java-docs:`findOneAndUpdate() <apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#findOneAndUpdate(org.bson.conversions.Bson,java.util.List,com.mongodb.client.model.FindOneAndUpdateOptions)>` |
| 113 | +- :java-docs:`FindOneAndUpdateOptions <apidocs/mongodb-driver-core/com/mongodb/client/model/FindOneAndUpdateOptions.html>` |
| 114 | +- :java-docs:`MongoExecutionTimeoutException <apidocs/mongodb-driver-core/com/mongodb/MongoExecutionTimeoutException.html>` |
| 115 | + |
| 116 | + |
| 117 | +.. _java-find-and-replace-example: |
| 118 | + |
| 119 | +Find and Replace |
| 120 | +~~~~~~~~~~~~~~~~ |
| 121 | + |
| 122 | +To find and replace one document, use the ``findOneAndReplace()`` method of the |
| 123 | +``MongoCollection`` class. The ``findOneAndReplace()`` method returns your found |
| 124 | +document or ``null`` if no documents match your query. |
| 125 | + |
| 126 | +Example |
| 127 | +^^^^^^^ |
| 128 | + |
| 129 | +The following example uses the ``findOneAndReplace()`` method to find a |
| 130 | +document with the ``color`` field set to ``"green"`` and replace it |
| 131 | +with the following document: |
| 132 | + |
| 133 | +.. code-block:: json |
| 134 | + :copyable: false |
| 135 | + |
| 136 | + {"music": "classical", "color": "green"} |
| 137 | + |
| 138 | +The example also uses a ``FindOneAndReplaceOptions`` instance to specify that |
| 139 | +the returned document should be in the state after our replace operation. |
| 140 | + |
| 141 | +.. literalinclude:: /includes/fundamentals/code-snippets/CompoundOperatorsIndividualExamples.java |
| 142 | + :language: java |
| 143 | + :dedent: |
| 144 | + :start-after: start findOneAndReplace-example |
| 145 | + :end-before: end findOneAndReplace-example |
| 146 | + |
| 147 | +The output of the above code should look like this: |
| 148 | + |
| 149 | +.. code-block:: json |
| 150 | + :copyable: false |
| 151 | + |
| 152 | + {"_id": 1, "music": "classical", "color": "green"} |
| 153 | + |
| 154 | +For more information on the methods and classes in this section, see the |
| 155 | +following API documentation pages: |
| 156 | + |
| 157 | +- :java-docs:`findOneAndReplace() <apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#findOneAndReplace(org.bson.conversions.Bson,TDocument,com.mongodb.client.model.FindOneAndReplaceOptions)>` |
| 158 | +- :java-docs:`FindOneAndReplaceOptions <apidocs/mongodb-driver-core/com/mongodb/client/model/FindOneAndReplaceOptions.html>` |
| 159 | + |
| 160 | +Find and Delete |
| 161 | +~~~~~~~~~~~~~~~ |
| 162 | + |
| 163 | +To find and delete one document, use the ``findOneAndDelete()`` method of the |
| 164 | +``MongoCollection`` class. The ``findOneAndDelete()`` method returns your found |
| 165 | +document or ``null`` if no documents match your query. |
| 166 | + |
| 167 | +Example |
| 168 | +^^^^^^^ |
| 169 | + |
| 170 | +The following example uses the ``findOneAndDelete()`` method to find and |
| 171 | +delete the document with the largest value in the ``_id`` field. |
| 172 | + |
| 173 | +The example uses a ``FindOneAndDeleteOptions`` instance to specify a |
| 174 | +descending sort on the ``_id`` field. |
| 175 | + |
| 176 | +.. literalinclude:: /includes/fundamentals/code-snippets/CompoundOperatorsIndividualExamples.java |
| 177 | + :language: java |
| 178 | + :dedent: |
| 179 | + :start-after: start findOneAndDelete-example |
| 180 | + :end-before: end findOneAndDelete-example |
| 181 | + |
| 182 | +The output of the above code should look like this: |
| 183 | + |
| 184 | +.. code-block:: json |
| 185 | + :copyable: false |
| 186 | + |
| 187 | + {"_id": 2, "food": "pear", "color": "yellow"} |
| 188 | + |
| 189 | +For more information on the ``Sorts`` class, see our |
| 190 | +:doc:`guide on the Sorts builder </fundamentals/builders/sort/>`. |
| 191 | + |
| 192 | +For more information on the methods and classes in this section, see the |
| 193 | +following API documentation pages: |
| 194 | + |
| 195 | +- :java-docs:`findOneAndDelete() <apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#findOneAndDelete(org.bson.conversions.Bson,com.mongodb.client.model.FindOneAndDeleteOptions)>` |
| 196 | +- :java-docs:`FindOneAndDeleteOptions <apidocs/mongodb-driver-core/com/mongodb/client/model/FindOneAndDeleteOptions.html>` |
| 197 | + |
| 198 | +Avoiding a Race Condition |
| 199 | +------------------------- |
| 200 | + |
| 201 | +In this section we explore two examples. The first example contains a |
| 202 | +race condition, the second example uses a compound operation to |
| 203 | +avoid the race condition present in the first example. |
| 204 | + |
| 205 | +For both examples, let's imagine that we run a hotel with one room and that we |
| 206 | +have a small Java program to help us checkout this room to a guest. |
| 207 | + |
| 208 | +The following document in MongoDB represents the room: |
| 209 | + |
| 210 | +.. code-block:: json |
| 211 | + :copyable: false |
| 212 | + |
| 213 | + {"_id": 1, "guest": null, "room": "Blue Room", "reserved": false} |
| 214 | + |
| 215 | +`The full code for this example is available on Github here. <https://github.com/mongodb/docs-java/blob/master/source/includes/fundamentals/code-snippets/CompoundOperators.java>`__ |
| 216 | + |
| 217 | +Example With Race Condition |
| 218 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 219 | + |
| 220 | +Let's say our app uses this ``bookARoom`` method to checkout our room to |
| 221 | +a guest: |
| 222 | + |
| 223 | +.. literalinclude:: /includes/fundamentals/code-snippets/CompoundOperators.java |
| 224 | + :language: java |
| 225 | + :dedent: |
| 226 | + :emphasize-lines: 3,12 |
| 227 | + :start-after: start the-unsafe-book-a-room |
| 228 | + :end-before: end the-unsafe-book-a-room |
| 229 | + |
| 230 | +Imagine two separate guests, Jan and Pat, try to book the room with this method |
| 231 | +at the same time. |
| 232 | + |
| 233 | +Jan sees this output: |
| 234 | + |
| 235 | +.. code-block:: none |
| 236 | + :copyable: false |
| 237 | + |
| 238 | + You got the Blue Room Jan |
| 239 | + |
| 240 | +And Pat sees this output: |
| 241 | + |
| 242 | +.. code-block:: none |
| 243 | + :copyable: false |
| 244 | + |
| 245 | + You got the Blue Room Pat |
| 246 | + |
| 247 | +When we look at our database, we see the following: |
| 248 | + |
| 249 | +.. code-block:: json |
| 250 | + :copyable: false |
| 251 | + |
| 252 | + {"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": true} |
| 253 | + |
| 254 | +Pat will be unhappy. When Pat shows up to our hotel, Jan will be |
| 255 | +occupying her room. What went wrong? |
| 256 | + |
| 257 | +Here is the sequence of events that happened from the perspective of our MongoDB |
| 258 | +instance: |
| 259 | + |
| 260 | +- Find and return an empty room for Jan |
| 261 | +- Find and return an empty room for Pat |
| 262 | +- Update the room to booked for Pat |
| 263 | +- Update the room to booked for Jan |
| 264 | + |
| 265 | +Notice that for a brief moment Pat had reserved the room, but as Jan's update |
| 266 | +operation was the last to execute our document has ``"Jan"`` as the guest. |
| 267 | + |
| 268 | +Example Without Race Condition |
| 269 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 270 | + |
| 271 | +Let's use a compound operation to avoid the race condition and |
| 272 | +always give our users the correct message. |
| 273 | + |
| 274 | +.. literalinclude:: /includes/fundamentals/code-snippets/CompoundOperators.java |
| 275 | + :language: java |
| 276 | + :dedent: |
| 277 | + :emphasize-lines: 4 |
| 278 | + :start-after: start the-safe-book-a-room |
| 279 | + :end-before: end the-safe-book-a-room |
| 280 | + |
| 281 | +Imagine two separate guests, Jan and Pat, try to book the room with this method |
| 282 | +at the same time. |
| 283 | + |
| 284 | +Jan sees this output: |
| 285 | + |
| 286 | +.. code-block:: none |
| 287 | + :copyable: false |
| 288 | + |
| 289 | + You got the Blue Room Jan |
| 290 | + |
| 291 | +And Pat sees this output: |
| 292 | + |
| 293 | +.. code-block:: none |
| 294 | + :copyable: false |
| 295 | + |
| 296 | + Sorry, we are booked Pat |
| 297 | + |
| 298 | +When we look at our database, we see the following: |
| 299 | + |
| 300 | +.. code-block:: json |
| 301 | + :copyable: false |
| 302 | + |
| 303 | + {"_id":1, "guest":"Jan", "room":"Blue Room", "reserved":true} |
| 304 | + |
| 305 | +Pat got the correct message. While she might be sad she didn't get the |
| 306 | +reservation, at least she knows not to travel to our hotel. |
| 307 | + |
| 308 | +Here is the sequence of events that happened from the perspective of our MongoDB |
| 309 | +instance: |
| 310 | + |
| 311 | +- Find an empty room for Jan and reserve it. |
| 312 | +- Try to find an empty room for Pat and reserve it. As there are not any rooms |
| 313 | + left, return ``null``. |
| 314 | + |
| 315 | +.. note:: Write Lock |
| 316 | + |
| 317 | + Your MongoDB instance places a write lock on the document you are modifying |
| 318 | + for the duration of your compound operation. |
| 319 | + |
| 320 | +For information on the ``Updates`` class, see our |
| 321 | +:doc:`guide on the Updates builder </fundamentals/builders/updates/>`. |
| 322 | + |
| 323 | +For more information of the ``Filters`` class, see our |
| 324 | +:doc:`guide on the Filters builder </fundamentals/builders/filters/>`. |
| 325 | + |
| 326 | +For more information on the ``findOneAndUpdate()`` method, see |
| 327 | +:java-docs:`the API documentation for the MongoCollection class <apidocs/mongodb-driver-sync/com/mongodb/client/MongoCollection.html#findOneAndUpdate(org.bson.conversions.Bson,java.util.List,com.mongodb.client.model.FindOneAndUpdateOptions)>`. |
0 commit comments