wread_data_mongodb/
crud_repository.rs

1use futures::stream::StreamExt;
2use log::trace;
3use mongodb::bson;
4use mongodb::bson::oid::ObjectId;
5use mongodb::bson::{doc, Bson::Document as BsonDocument, Document};
6use mongodb::error::Error;
7use mongodb::options::FindOptions;
8use mongodb::Database;
9use serde::{Deserialize, Serialize};
10
11use mongodb::{
12    options::{
13        DeleteOptions, FindOneAndReplaceOptions, FindOneAndUpdateOptions, ReplaceOptions,
14        UpdateModifications, UpdateOptions,
15    },
16    results::{DeleteResult, InsertOneResult, UpdateResult},
17};
18use std::fmt::Debug;
19
20pub async fn find_one<T>(
21    filter_document: Document,
22    collection_name: &str,
23    db: &Database,
24) -> Result<Option<T>, Error>
25where
26    for<'a> T: Serialize + Deserialize<'a>,
27{
28    trace!("find_one");
29    let coll = db.collection(collection_name);
30    let result = coll.find_one(filter_document, None).await?;
31    if let Some(document) = result {
32        let t = bson::from_bson::<T>(BsonDocument(document))?;
33        return Ok(Some(t));
34    } else {
35        return Ok(None);
36    }
37}
38
39pub async fn find_by_id<T>(
40    id: &ObjectId,
41    collection_name: &str,
42    db: &Database,
43) -> Result<Option<T>, Error>
44where
45    for<'a> T: Serialize + Deserialize<'a>,
46{
47    trace!("find_by_id");
48    let filter_document = doc! {"_id":  id};
49    find_one(filter_document, &collection_name, &db).await
50}
51
52pub async fn find_by_string_id<T>(
53    id: &str,
54    collection_name: &str,
55    db: &Database,
56) -> Result<Option<T>, Error>
57where
58    for<'a> T: Serialize + Deserialize<'a>,
59{
60    trace!("find_by_string_id");
61    let filter_document = doc! {"_id": id};
62    find_one(filter_document, &collection_name, &db).await
63}
64
65pub async fn find_one_by_string_field<T>(
66    name: &str,
67    value: &str,
68    collection_name: &str,
69    db: &Database,
70) -> Result<Option<T>, Error>
71where
72    for<'a> T: Serialize + Deserialize<'a>,
73{
74    trace!("find_by_string_id");
75    let filter_document = doc! {name: value};
76    find_one(filter_document, &collection_name, &db).await
77}
78
79pub async fn find_by_string_field<T>(
80    name: &str,
81    value: &str,
82    collection_name: &str,
83    db: &Database,
84) -> Result<Vec<T>, Error>
85where
86    for<'a> T: Serialize + Deserialize<'a>,
87{
88    trace!("find_by_string_field");
89    let filter_document = doc! {name: value};
90    find(filter_document, &collection_name, &db).await
91}
92
93pub async fn find_all<T>(collection_name: &str, db: &Database) -> Result<Vec<T>, Error>
94where
95    for<'a> T: Serialize + Deserialize<'a>,
96{
97    trace!("find_all");
98    find_generic(None, None, collection_name, db).await
99}
100
101pub async fn find<T>(
102    filter_document: Document,
103    collection_name: &str,
104    db: &Database,
105) -> Result<Vec<T>, Error>
106where
107    for<'a> T: Serialize + Deserialize<'a>,
108{
109    trace!("find");
110    find_generic(filter_document, None, collection_name, db).await
111}
112
113pub async fn find_with_sort<T>(
114    filter_document: Document,
115    sort_document: Document,
116    collection_name: &str,
117    db: &Database,
118) -> Result<Vec<T>, Error>
119where
120    for<'a> T: Serialize + Deserialize<'a>,
121{
122    trace!("find_with_sort");
123    find_generic(filter_document, Some(sort_document), collection_name, db).await
124}
125
126async fn find_generic<T>(
127    filter_document: impl Into<Option<Document>>,
128    sort_document_option: Option<Document>,
129    collection_name: &str,
130    db: &Database,
131) -> Result<Vec<T>, Error>
132where
133    for<'a> T: Serialize + Deserialize<'a>,
134{
135    trace!("find_generic");
136    let coll = db.collection(collection_name);
137    let find_options = get_sort_find_option(sort_document_option);
138    let mut cursor = coll.find(filter_document, find_options).await?;
139    let mut items = Vec::<T>::new();
140    while let Some(result) = cursor.next().await {
141        match result {
142            Ok(document) => {
143                let item = bson::from_bson::<T>(BsonDocument(document))?;
144                items.push(item);
145            }
146            Err(err) => {
147                return Err(err);
148            }
149        }
150    }
151
152    Ok(items)
153}
154
155fn get_sort_find_option(sort_document_option: Option<Document>) -> Option<FindOptions> {
156    if let Some(sort_document) = sort_document_option {
157        Some(FindOptions::builder().sort(sort_document).build())
158    } else {
159        None
160    }
161}
162
163pub async fn _find_one_by_field<T>(
164    field_name: String,
165    value: String,
166    collection_name: &str,
167    db: &Database,
168) -> Result<Option<T>, Error>
169where
170    for<'a> T: Serialize + Deserialize<'a>,
171{
172    self::find_one(doc! {field_name: value}, collection_name, db).await
173}
174
175pub async fn add<T>(t: &T, collection_name: &str, db: &Database) -> Result<InsertOneResult, Error>
176where
177    for<'a> T: Debug + Serialize + Deserialize<'a>,
178{
179    let serialized_item = bson::to_bson(&t)?;
180
181    if let BsonDocument(document) = serialized_item {
182        let coll = db.collection(collection_name);
183        coll.insert_one(document, None).await
184    } else {
185        panic!("Error converting the BSON object into a MongoDB document");
186    }
187}
188
189pub async fn update_one(
190    query: Document,
191    update: impl Into<UpdateModifications>,
192    options: impl Into<Option<UpdateOptions>>,
193    collection_name: &str,
194    db: &Database,
195) -> Result<UpdateResult, Error> {
196    let coll = db.collection(collection_name);
197    coll.update_one(query, update, options).await
198}
199
200pub async fn find_one_and_update<T>(
201    filter: Document,
202    update: impl Into<UpdateModifications>,
203    options: impl Into<Option<FindOneAndUpdateOptions>>,
204    collection_name: &str,
205    db: &Database,
206) -> Result<Option<T>, Error>
207where
208    for<'a> T: Serialize + Deserialize<'a>,
209{
210    let coll = db.collection(collection_name);
211    let option = coll.find_one_and_update(filter, update, options).await?;
212    if let Some(document) = option {
213        let t = bson::from_bson::<T>(BsonDocument(document))?;
214        return Ok(Some(t));
215    } else {
216        return Ok(None);
217    }
218}
219
220pub async fn find_one_and_replace<T>(
221    filter: Document,
222    replacement: &T,
223    options: impl Into<Option<FindOneAndReplaceOptions>>,
224    collection_name: &str,
225    db: &Database,
226) -> Result<Option<T>, Error>
227where
228    for<'a> T: Serialize + Deserialize<'a>,
229{
230    let coll = db.collection(collection_name);
231    let serialized_item = bson::to_bson(&replacement)?;
232
233    if let BsonDocument(document) = serialized_item {
234        let option = coll.find_one_and_replace(filter, document, options).await?;
235        if let Some(document) = option {
236            let t = bson::from_bson::<T>(BsonDocument(document))?;
237            return Ok(Some(t));
238        } else {
239            return Ok(None);
240        }
241    } else {
242        panic!("Error converting the BSON object into a MongoDB document");
243    }
244}
245
246pub async fn replace_one<T>(
247    query: Document,
248    replacement: &T,
249    options: impl Into<Option<ReplaceOptions>>,
250    collection_name: &str,
251    db: &Database,
252) -> Result<UpdateResult, Error>
253where
254    for<'a> T: Serialize + Deserialize<'a>,
255{
256    let coll = db.collection(collection_name);
257    let serialized_item = bson::to_bson(&replacement)?;
258
259    if let BsonDocument(document) = serialized_item {
260        coll.replace_one(query, document, options).await
261    } else {
262        panic!("Error converting the BSON object into a MongoDB document");
263    }
264}
265
266pub async fn delete_one(
267    query: Document,
268    options: impl Into<Option<DeleteOptions>>,
269    collection_name: &str,
270    db: &Database,
271) -> Result<DeleteResult, Error> {
272    let coll = db.collection(collection_name);
273    coll.delete_one(query, options).await
274}