Skip to main content

mongodb/action/
find_and_modify.rs

1use std::{borrow::Borrow, time::Duration};
2
3use crate::bson::{Bson, Document, RawDocumentBuf};
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::{
7    coll::options::{
8        FindOneAndDeleteOptions,
9        FindOneAndReplaceOptions,
10        FindOneAndUpdateOptions,
11        Hint,
12        ReturnDocument,
13        UpdateModifications,
14    },
15    collation::Collation,
16    error::Result,
17    operation::{
18        find_and_modify::options::{FindAndModifyOptions, Modification},
19        FindAndModify as Op,
20        UpdateOrReplace,
21    },
22    options::WriteConcern,
23    ClientSession,
24    Collection,
25};
26
27use super::{action_impl, deeplink, export_doc, option_setters, options_doc};
28
29impl<T: DeserializeOwned + Send + Sync> Collection<T> {
30    async fn find_and_modify(
31        &self,
32        filter: Document,
33        modification: Modification,
34        options: Option<FindAndModifyOptions>,
35        session: Option<&mut ClientSession>,
36    ) -> Result<Option<T>> {
37        let op = Op::<T>::with_modification(self.clone_with_type(), filter, modification, options)?;
38        self.client().execute_operation(op, session).await
39    }
40
41    /// Atomically finds up to one document in the collection matching `filter` and deletes it.
42    ///
43    /// This operation will retry once upon failure if the connection and encountered error support
44    /// retryability. See the documentation
45    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
46    /// retryable writes.
47    ///
48    /// `await` will return d[`Result<Option<T>>`].
49    #[deeplink]
50    #[options_doc(find_one_and_delete)]
51    pub fn find_one_and_delete(&self, filter: Document) -> FindOneAndDelete<'_, T> {
52        FindOneAndDelete {
53            coll: self,
54            filter,
55            options: None,
56            session: None,
57        }
58    }
59
60    /// Atomically finds up to one document in the collection matching `filter` and updates it.
61    /// Both `Document` and `Vec<Document>` implement `Into<UpdateModifications>`, so either can be
62    /// passed in place of constructing the enum case.
63    ///
64    /// This operation will retry once upon failure if the connection and encountered error support
65    /// retryability. See the documentation
66    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
67    /// retryable writes.
68    ///
69    /// `await` will return d[`Result<Option<T>>`].
70    #[deeplink]
71    #[options_doc(find_one_and_update)]
72    pub fn find_one_and_update(
73        &self,
74        filter: Document,
75        update: impl Into<UpdateModifications>,
76    ) -> FindOneAndUpdate<'_, T> {
77        FindOneAndUpdate {
78            coll: self,
79            filter,
80            update: update.into(),
81            options: None,
82            session: None,
83        }
84    }
85}
86
87impl<T: Serialize + DeserializeOwned + Send + Sync> Collection<T> {
88    /// Atomically finds up to one document in the collection matching `filter` and replaces it with
89    /// `replacement`.
90    ///
91    /// This operation will retry once upon failure if the connection and encountered error support
92    /// retryability. See the documentation
93    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
94    /// retryable writes.
95    ///
96    /// `await` will return d[`Result<Option<T>>`].
97    #[deeplink]
98    #[options_doc(find_one_and_replace)]
99    pub fn find_one_and_replace(
100        &self,
101        filter: Document,
102        replacement: impl Borrow<T>,
103    ) -> FindOneAndReplace<'_, T> {
104        FindOneAndReplace {
105            coll: self,
106            filter,
107            replacement: crate::bson_compat::serialize_to_raw_document_buf(replacement.borrow())
108                .map_err(Into::into),
109            options: None,
110            session: None,
111        }
112    }
113}
114
115#[cfg(feature = "sync")]
116impl<T: DeserializeOwned + Send + Sync> crate::sync::Collection<T> {
117    /// Atomically finds up to one document in the collection matching `filter` and deletes it.
118    ///
119    /// This operation will retry once upon failure if the connection and encountered error support
120    /// retryability. See the documentation
121    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
122    /// retryable writes.
123    ///
124    /// [`run`](FindOneAndDelete::run) will return d[`Result<Option<T>>`].
125    #[deeplink]
126    #[options_doc(find_one_and_delete, "run")]
127    pub fn find_one_and_delete(&self, filter: Document) -> FindOneAndDelete<'_, T> {
128        self.async_collection.find_one_and_delete(filter)
129    }
130
131    /// Atomically finds up to one document in the collection matching `filter` and updates it.
132    /// Both `Document` and `Vec<Document>` implement `Into<UpdateModifications>`, so either can be
133    /// passed in place of constructing the enum case.
134    ///
135    /// This operation will retry once upon failure if the connection and encountered error support
136    /// retryability. See the documentation
137    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
138    /// retryable writes.
139    ///
140    /// [`run`](FindOneAndDelete::run) will return d[`Result<Option<T>>`].
141    #[deeplink]
142    #[options_doc(find_one_and_update, "run")]
143    pub fn find_one_and_update(
144        &self,
145        filter: Document,
146        update: impl Into<UpdateModifications>,
147    ) -> FindOneAndUpdate<'_, T> {
148        self.async_collection.find_one_and_update(filter, update)
149    }
150}
151
152#[cfg(feature = "sync")]
153impl<T: Serialize + DeserializeOwned + Send + Sync> crate::sync::Collection<T> {
154    /// Atomically finds up to one document in the collection matching `filter` and replaces it with
155    /// `replacement`.
156    ///
157    /// This operation will retry once upon failure if the connection and encountered error support
158    /// retryability. See the documentation
159    /// [here](https://www.mongodb.com/docs/manual/core/retryable-writes/) for more information on
160    /// retryable writes.
161    ///
162    /// [`run`](FindOneAndReplace::run) will return d[`Result<Option<T>>`].
163    #[deeplink]
164    #[options_doc(find_one_and_replace, "run")]
165    pub fn find_one_and_replace(
166        &self,
167        filter: Document,
168        replacement: impl Borrow<T>,
169    ) -> FindOneAndReplace<'_, T> {
170        self.async_collection
171            .find_one_and_replace(filter, replacement)
172    }
173}
174
175/// Atomically finds up to one document in the collection matching a filter and deletes it.
176/// Construct with [`Collection::find_one_and_delete`].
177#[must_use]
178pub struct FindOneAndDelete<'a, T: Send + Sync> {
179    coll: &'a Collection<T>,
180    filter: Document,
181    options: Option<FindOneAndDeleteOptions>,
182    session: Option<&'a mut ClientSession>,
183}
184
185#[option_setters(crate::coll::options::FindOneAndDeleteOptions)]
186#[export_doc(find_one_and_delete)]
187impl<'a, T: Send + Sync> FindOneAndDelete<'a, T> {
188    /// Use the provided session when running the operation.
189    pub fn session(mut self, value: impl Into<&'a mut ClientSession>) -> Self {
190        self.session = Some(value.into());
191        self
192    }
193}
194
195#[action_impl]
196impl<'a, T: DeserializeOwned + Send + Sync> Action for FindOneAndDelete<'a, T> {
197    type Future = FindOneAndDeleteFuture;
198
199    async fn execute(self) -> Result<Option<T>> {
200        self.coll
201            .find_and_modify(
202                self.filter,
203                Modification::Delete,
204                self.options.map(FindAndModifyOptions::from),
205                self.session,
206            )
207            .await
208    }
209}
210
211/// Atomically finds up to one document in the collection matching a filter and updates it.
212/// Construct with [`Collection::find_one_and_update`].
213#[must_use]
214pub struct FindOneAndUpdate<'a, T: Send + Sync> {
215    coll: &'a Collection<T>,
216    filter: Document,
217    update: UpdateModifications,
218    options: Option<FindOneAndUpdateOptions>,
219    session: Option<&'a mut ClientSession>,
220}
221
222#[option_setters(crate::coll::options::FindOneAndUpdateOptions)]
223#[export_doc(find_one_and_update)]
224impl<'a, T: Send + Sync> FindOneAndUpdate<'a, T> {
225    /// Use the provided session when running the operation.
226    pub fn session(mut self, value: impl Into<&'a mut ClientSession>) -> Self {
227        self.session = Some(value.into());
228        self
229    }
230}
231
232#[action_impl]
233impl<'a, T: DeserializeOwned + Send + Sync> Action for FindOneAndUpdate<'a, T> {
234    type Future = FindOneAndUpdateFuture;
235
236    async fn execute(self) -> Result<Option<T>> {
237        self.coll
238            .find_and_modify(
239                self.filter,
240                Modification::Update(self.update.into()),
241                self.options.map(FindAndModifyOptions::from),
242                self.session,
243            )
244            .await
245    }
246}
247
248/// Atomically finds up to one document in the collection matching a filter and replaces it.
249/// Construct with [`Collection::find_one_and_replace`].
250#[must_use]
251pub struct FindOneAndReplace<'a, T: Send + Sync> {
252    coll: &'a Collection<T>,
253    filter: Document,
254    replacement: Result<RawDocumentBuf>,
255    options: Option<FindOneAndReplaceOptions>,
256    session: Option<&'a mut ClientSession>,
257}
258
259#[option_setters(crate::coll::options::FindOneAndReplaceOptions)]
260#[export_doc(find_one_and_replace)]
261impl<'a, T: Send + Sync> FindOneAndReplace<'a, T> {
262    /// Use the provided session when running the operation.
263    pub fn session(mut self, value: impl Into<&'a mut ClientSession>) -> Self {
264        self.session = Some(value.into());
265        self
266    }
267}
268
269#[action_impl]
270impl<'a, T: DeserializeOwned + Send + Sync> Action for FindOneAndReplace<'a, T> {
271    type Future = FindOneAndReplaceFuture;
272
273    async fn execute(self) -> Result<Option<T>> {
274        self.coll
275            .find_and_modify(
276                self.filter,
277                Modification::Update(UpdateOrReplace::Replacement(self.replacement?)),
278                self.options.map(FindAndModifyOptions::from),
279                self.session,
280            )
281            .await
282    }
283}