Skip to content

Commit 8eeabf5

Browse files
Docsp 9568 compound (#80)
Co-authored-by: kyuan-mongodb <[email protected]>
1 parent c2da3ef commit 8eeabf5

File tree

4 files changed

+577
-4
lines changed

4 files changed

+577
-4
lines changed

source/fundamentals/crud.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ CRUD Operations
1010
/fundamentals/crud/read-operations
1111
/fundamentals/crud/write-operations
1212
/fundamentals/crud/query-document
13+
/fundamentals/crud/compound-operations
1314

1415
CRUD (Create, Read, Update, Delete) operations enable you to work with
1516
data stored in MongoDB.
@@ -19,7 +20,6 @@ data stored in MongoDB.
1920
- :doc:`Write Operations </fundamentals/crud/write-operations>` insert, modify,
2021
or delete documents in your database.
2122

22-
..
23-
Some operations combine aspects of read and write operations. See our
24-
guide on :doc:`compound operations </fundamentals/crud/compound-operations>`
25-
to learn more about these hybrid methods.
23+
Some operations combine aspects of read and write operations. See our
24+
guide on :doc:`compound operations </fundamentals/crud/compound-operations>`
25+
to learn more about these hybrid methods.
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
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

Comments
 (0)