Skip to content

Commit d895a6d

Browse files
RUST-1858 Add builders for bulk write models (#1104)
1 parent 17da603 commit d895a6d

File tree

4 files changed

+338
-312
lines changed

4 files changed

+338
-312
lines changed

src/action/bulk_write.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@ use super::{action_impl, option_setters};
1717
impl Client {
1818
pub fn bulk_write(
1919
&self,
20-
models: impl IntoIterator<Item = WriteModel>,
20+
models: impl IntoIterator<Item = impl Into<WriteModel>>,
2121
) -> BulkWrite<SummaryBulkWriteResult> {
22-
BulkWrite::new(self, models.into_iter().collect())
22+
let mut models_vec = Vec::new();
23+
for model in models.into_iter() {
24+
models_vec.push(model.into());
25+
}
26+
BulkWrite::new(self, models_vec)
2327
}
2428
}
2529

src/client/options/bulk_write.rs

+220-79
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
#![allow(missing_docs)]
22

3+
use std::borrow::Borrow;
4+
35
use serde::{Deserialize, Serialize};
46
use serde_with::skip_serializing_none;
7+
use typed_builder::TypedBuilder;
58

69
use crate::{
710
bson::{rawdoc, Array, Bson, Document, RawDocumentBuf},
811
bson_util::{get_or_prepend_id_field, replacement_document_check, update_document_check},
912
error::Result,
1013
options::{UpdateModifications, WriteConcern},
1114
serde_util::serialize_bool_or_true,
15+
Collection,
1216
Namespace,
1317
};
1418

@@ -32,66 +36,205 @@ pub struct BulkWriteOptions {
3236
#[serde(untagged)]
3337
#[non_exhaustive]
3438
pub enum WriteModel {
35-
#[non_exhaustive]
36-
InsertOne {
37-
#[serde(skip)]
38-
namespace: Namespace,
39-
document: Document,
40-
},
41-
#[non_exhaustive]
42-
#[serde(rename_all = "camelCase")]
43-
UpdateOne {
44-
#[serde(skip)]
45-
namespace: Namespace,
46-
filter: Document,
47-
#[serde(rename = "updateMods")]
48-
update: UpdateModifications,
49-
array_filters: Option<Array>,
50-
collation: Option<Document>,
51-
hint: Option<Bson>,
52-
upsert: Option<bool>,
53-
},
54-
#[non_exhaustive]
55-
#[serde(rename_all = "camelCase")]
56-
UpdateMany {
57-
#[serde(skip)]
58-
namespace: Namespace,
59-
filter: Document,
60-
#[serde(rename = "updateMods")]
61-
update: UpdateModifications,
62-
array_filters: Option<Array>,
63-
collation: Option<Document>,
64-
hint: Option<Bson>,
65-
upsert: Option<bool>,
66-
},
67-
#[non_exhaustive]
68-
#[serde(rename_all = "camelCase")]
69-
ReplaceOne {
70-
#[serde(skip)]
71-
namespace: Namespace,
72-
filter: Document,
73-
#[serde(rename = "updateMods")]
74-
replacement: Document,
75-
collation: Option<Document>,
76-
hint: Option<Bson>,
77-
upsert: Option<bool>,
78-
},
79-
#[non_exhaustive]
80-
DeleteOne {
81-
#[serde(skip)]
82-
namespace: Namespace,
83-
filter: Document,
84-
collation: Option<Document>,
85-
hint: Option<Bson>,
86-
},
87-
#[non_exhaustive]
88-
DeleteMany {
89-
#[serde(skip)]
90-
namespace: Namespace,
39+
InsertOne(InsertOneModel),
40+
UpdateOne(UpdateOneModel),
41+
UpdateMany(UpdateManyModel),
42+
ReplaceOne(ReplaceOneModel),
43+
DeleteOne(DeleteOneModel),
44+
DeleteMany(DeleteManyModel),
45+
}
46+
47+
#[skip_serializing_none]
48+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
49+
#[cfg_attr(test, derive(Deserialize))]
50+
#[serde(rename_all = "camelCase")]
51+
#[builder(field_defaults(default, setter(into)))]
52+
#[non_exhaustive]
53+
pub struct InsertOneModel {
54+
#[serde(skip_serializing)]
55+
#[builder(!default)]
56+
pub namespace: Namespace,
57+
58+
#[builder(!default)]
59+
pub document: Document,
60+
}
61+
62+
impl From<InsertOneModel> for WriteModel {
63+
fn from(model: InsertOneModel) -> Self {
64+
Self::InsertOne(model)
65+
}
66+
}
67+
68+
#[skip_serializing_none]
69+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
70+
#[cfg_attr(test, derive(Deserialize))]
71+
#[serde(rename_all = "camelCase")]
72+
#[builder(field_defaults(default, setter(into)))]
73+
#[non_exhaustive]
74+
pub struct UpdateOneModel {
75+
#[serde(skip_serializing)]
76+
#[builder(!default)]
77+
pub namespace: Namespace,
78+
79+
#[builder(!default)]
80+
pub filter: Document,
81+
82+
#[serde(rename(serialize = "updateMods"))]
83+
#[builder(!default)]
84+
pub update: UpdateModifications,
85+
86+
pub array_filters: Option<Array>,
87+
88+
pub collation: Option<Document>,
89+
90+
pub hint: Option<Bson>,
91+
92+
pub upsert: Option<bool>,
93+
}
94+
95+
impl From<UpdateOneModel> for WriteModel {
96+
fn from(model: UpdateOneModel) -> Self {
97+
Self::UpdateOne(model)
98+
}
99+
}
100+
101+
#[skip_serializing_none]
102+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
103+
#[cfg_attr(test, derive(Deserialize))]
104+
#[serde(rename_all = "camelCase")]
105+
#[builder(field_defaults(default, setter(into)))]
106+
#[non_exhaustive]
107+
pub struct UpdateManyModel {
108+
#[serde(skip_serializing)]
109+
#[builder(!default)]
110+
pub namespace: Namespace,
111+
112+
#[builder(!default)]
113+
pub filter: Document,
114+
115+
#[serde(rename(serialize = "updateMods"))]
116+
#[builder(!default)]
117+
pub update: UpdateModifications,
118+
119+
pub array_filters: Option<Array>,
120+
121+
pub collation: Option<Document>,
122+
123+
pub hint: Option<Bson>,
124+
125+
pub upsert: Option<bool>,
126+
}
127+
128+
impl From<UpdateManyModel> for WriteModel {
129+
fn from(model: UpdateManyModel) -> Self {
130+
Self::UpdateMany(model)
131+
}
132+
}
133+
134+
#[skip_serializing_none]
135+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
136+
#[cfg_attr(test, derive(Deserialize))]
137+
#[serde(rename_all = "camelCase")]
138+
#[builder(field_defaults(default, setter(into)))]
139+
#[non_exhaustive]
140+
pub struct ReplaceOneModel {
141+
#[serde(skip_serializing)]
142+
#[builder(!default)]
143+
pub namespace: Namespace,
144+
145+
#[builder(!default)]
146+
pub filter: Document,
147+
148+
#[serde(rename(serialize = "updateMods"))]
149+
#[builder(!default)]
150+
pub replacement: Document,
151+
152+
pub collation: Option<Document>,
153+
154+
pub hint: Option<Bson>,
155+
156+
pub upsert: Option<bool>,
157+
}
158+
159+
impl From<ReplaceOneModel> for WriteModel {
160+
fn from(model: ReplaceOneModel) -> Self {
161+
Self::ReplaceOne(model)
162+
}
163+
}
164+
165+
#[skip_serializing_none]
166+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
167+
#[cfg_attr(test, derive(Deserialize))]
168+
#[serde(rename_all = "camelCase")]
169+
#[builder(field_defaults(default, setter(into)))]
170+
#[non_exhaustive]
171+
pub struct DeleteOneModel {
172+
#[serde(skip_serializing)]
173+
#[builder(!default)]
174+
pub namespace: Namespace,
175+
176+
#[builder(!default)]
177+
pub filter: Document,
178+
179+
pub collation: Option<Document>,
180+
181+
pub hint: Option<Bson>,
182+
}
183+
184+
impl From<DeleteOneModel> for WriteModel {
185+
fn from(model: DeleteOneModel) -> Self {
186+
Self::DeleteOne(model)
187+
}
188+
}
189+
190+
#[skip_serializing_none]
191+
#[derive(Clone, Debug, Serialize, TypedBuilder)]
192+
#[cfg_attr(test, derive(Deserialize))]
193+
#[serde(rename_all = "camelCase")]
194+
#[builder(field_defaults(default, setter(into)))]
195+
#[non_exhaustive]
196+
pub struct DeleteManyModel {
197+
#[serde(skip_serializing)]
198+
#[builder(!default)]
199+
pub namespace: Namespace,
200+
201+
pub filter: Document,
202+
203+
pub collation: Option<Document>,
204+
205+
pub hint: Option<Bson>,
206+
}
207+
208+
impl From<DeleteManyModel> for WriteModel {
209+
fn from(model: DeleteManyModel) -> Self {
210+
Self::DeleteMany(model)
211+
}
212+
}
213+
214+
impl<T> Collection<T>
215+
where
216+
T: Send + Sync + Serialize,
217+
{
218+
pub fn insert_one_model(&self, document: impl Borrow<T>) -> Result<InsertOneModel> {
219+
let document = bson::to_document(document.borrow())?;
220+
Ok(InsertOneModel::builder()
221+
.namespace(self.namespace())
222+
.document(document)
223+
.build())
224+
}
225+
226+
pub fn replace_one_model(
227+
&self,
91228
filter: Document,
92-
collation: Option<Document>,
93-
hint: Option<Bson>,
94-
},
229+
replacement: impl Borrow<T>,
230+
) -> Result<ReplaceOneModel> {
231+
let replacement = bson::to_document(replacement.borrow())?;
232+
Ok(ReplaceOneModel::builder()
233+
.namespace(self.namespace())
234+
.filter(filter)
235+
.replacement(replacement)
236+
.build())
237+
}
95238
}
96239

97240
pub(crate) enum OperationType {
@@ -103,34 +246,30 @@ pub(crate) enum OperationType {
103246
impl WriteModel {
104247
pub(crate) fn namespace(&self) -> &Namespace {
105248
match self {
106-
Self::InsertOne { namespace, .. } => namespace,
107-
Self::UpdateOne { namespace, .. } => namespace,
108-
Self::UpdateMany { namespace, .. } => namespace,
109-
Self::ReplaceOne { namespace, .. } => namespace,
110-
Self::DeleteOne { namespace, .. } => namespace,
111-
Self::DeleteMany { namespace, .. } => namespace,
249+
Self::InsertOne(model) => &model.namespace,
250+
Self::UpdateOne(model) => &model.namespace,
251+
Self::UpdateMany(model) => &model.namespace,
252+
Self::ReplaceOne(model) => &model.namespace,
253+
Self::DeleteOne(model) => &model.namespace,
254+
Self::DeleteMany(model) => &model.namespace,
112255
}
113256
}
114257

115258
pub(crate) fn operation_type(&self) -> OperationType {
116259
match self {
117-
Self::InsertOne { .. } => OperationType::Insert,
118-
Self::UpdateOne { .. } | Self::UpdateMany { .. } | Self::ReplaceOne { .. } => {
119-
OperationType::Update
120-
}
121-
Self::DeleteOne { .. } | Self::DeleteMany { .. } => OperationType::Delete,
260+
Self::InsertOne(_) => OperationType::Insert,
261+
Self::UpdateOne(_) | Self::UpdateMany(_) | Self::ReplaceOne(_) => OperationType::Update,
262+
Self::DeleteOne(_) | Self::DeleteMany(_) => OperationType::Delete,
122263
}
123264
}
124265

125266
/// Whether this operation should apply to all documents that match the filter. Returns None if
126267
/// the operation does not use a filter.
127268
pub(crate) fn multi(&self) -> Option<bool> {
128269
match self {
129-
Self::UpdateMany { .. } | Self::DeleteMany { .. } => Some(true),
130-
Self::UpdateOne { .. } | Self::ReplaceOne { .. } | Self::DeleteOne { .. } => {
131-
Some(false)
132-
}
133-
Self::InsertOne { .. } => None,
270+
Self::UpdateMany(_) | Self::DeleteMany(_) => Some(true),
271+
Self::UpdateOne(_) | Self::ReplaceOne(_) | Self::DeleteOne(_) => Some(false),
272+
Self::InsertOne(_) => None,
134273
}
135274
}
136275

@@ -145,17 +284,19 @@ impl WriteModel {
145284
/// Returns the operation-specific fields that should be included in this model's entry in the
146285
/// ops array. Also returns an inserted ID if this is an insert operation.
147286
pub(crate) fn get_ops_document_contents(&self) -> Result<(RawDocumentBuf, Option<Bson>)> {
148-
if let Self::UpdateOne { update, .. } | Self::UpdateMany { update, .. } = self {
287+
if let Self::UpdateOne(UpdateOneModel { update, .. })
288+
| Self::UpdateMany(UpdateManyModel { update, .. }) = self
289+
{
149290
if let UpdateModifications::Document(update_document) = update {
150291
update_document_check(update_document)?;
151292
}
152-
} else if let Self::ReplaceOne { replacement, .. } = self {
293+
} else if let Self::ReplaceOne(ReplaceOneModel { replacement, .. }) = self {
153294
replacement_document_check(replacement)?;
154295
}
155296

156297
let (mut model_document, inserted_id) = match self {
157-
Self::InsertOne { document, .. } => {
158-
let mut insert_document = RawDocumentBuf::from_document(document)?;
298+
Self::InsertOne(model) => {
299+
let mut insert_document = RawDocumentBuf::from_document(&model.document)?;
159300
let inserted_id = get_or_prepend_id_field(&mut insert_document)?;
160301
(rawdoc! { "document": insert_document }, Some(inserted_id))
161302
}

0 commit comments

Comments
 (0)