Skip to main content

tauri_plugin_mongoose/db/
documents.rs

1use crate::db::state::{get_client, get_db_name};
2use futures::stream::TryStreamExt;
3use mongodb::{
4    bson::{oid::ObjectId, Document},
5    options::{FindOneOptions as MongoFindOneOptions, FindOptions as MongoFindOptions},
6};
7use serde::Deserialize;
8use serde_json::Value;
9
10#[derive(Debug, Deserialize)]
11pub struct SearchOptions {
12    pub skip: Option<u64>,
13    pub limit: Option<i64>,
14    pub page: Option<u64>,
15    pub sort: Option<Value>,
16}
17
18pub async fn create_document(collection_name: String, document: Value) -> Result<Value, String> {
19    let client = get_client().await?;
20    let db_name = get_db_name().await;
21
22    let db = client.database(&db_name);
23    let collection = db.collection::<Document>(&collection_name);
24
25    let mut bson_doc = mongodb::bson::to_document(&document).map_err(|e| e.to_string())?;
26
27    if !bson_doc.contains_key("_id") {
28        bson_doc.insert("_id", ObjectId::new());
29    }
30
31    collection
32        .insert_one(bson_doc.clone(), None)
33        .await
34        .map_err(|e| e.to_string())?;
35
36    let json_doc: Value = mongodb::bson::from_document(bson_doc).map_err(|e| e.to_string())?;
37    Ok(json_doc)
38}
39
40pub async fn get_document_by_id(
41    collection_name: String,
42    id: String,
43) -> Result<Option<Value>, String> {
44    let client = get_client().await?;
45    let db_name = get_db_name().await;
46
47    let db = client.database(&db_name);
48    let collection = db.collection::<Document>(&collection_name);
49
50    let oid = ObjectId::parse_str(&id).map_err(|e| format!("Invalid ID format: {}", e))?;
51    let filter = mongodb::bson::doc! { "_id": oid };
52
53    let result = collection
54        .find_one(filter, None)
55        .await
56        .map_err(|e| e.to_string())?;
57
58    match result {
59        Some(doc) => {
60            let json_doc: Value = mongodb::bson::from_document(doc).map_err(|e| e.to_string())?;
61            Ok(Some(json_doc))
62        }
63        None => Ok(None),
64    }
65}
66
67pub async fn find_documents(
68    collection_name: String,
69    filter: Option<Value>,
70    options: Option<SearchOptions>,
71) -> Result<Vec<Value>, String> {
72    let client = get_client().await?;
73    let db_name = get_db_name().await;
74
75    let db = client.database(&db_name);
76    let collection = db.collection::<Document>(&collection_name);
77
78    let mut find_options = MongoFindOptions::builder().build();
79
80    if let Some(opts) = options {
81        if let Some(limit) = opts.limit {
82            find_options.limit = Some(limit);
83        }
84
85        let mut skip = opts.skip.unwrap_or(0);
86        if let Some(page) = opts.page {
87            if page > 0 {
88                let limit = opts.limit.unwrap_or(10); // Default limit 10 if page is set
89                skip = (page as u64 - 1) * (limit as u64);
90                // Also ensure limit is set if it wasn't
91                if find_options.limit.is_none() {
92                    find_options.limit = Some(limit);
93                }
94            }
95        }
96        find_options.skip = Some(skip);
97
98        if let Some(sort) = opts.sort {
99            if let Ok(sort_doc) = mongodb::bson::to_document(&sort) {
100                find_options.sort = Some(sort_doc);
101            }
102        }
103    }
104
105    let query = if let Some(f) = filter {
106        mongodb::bson::to_document(&f).map_err(|e| e.to_string())?
107    } else {
108        mongodb::bson::doc! {}
109    };
110
111    let mut cursor = collection
112        .find(query, find_options)
113        .await
114        .map_err(|e| e.to_string())?;
115
116    let mut docs = Vec::new();
117    while let Some(result) = cursor.try_next().await.map_err(|e| e.to_string())? {
118        let json_doc: Value = mongodb::bson::from_document(result).map_err(|e| e.to_string())?;
119        docs.push(json_doc);
120    }
121
122    Ok(docs)
123}
124
125pub async fn find_one_document(
126    collection_name: String,
127    filter: Option<Value>,
128    options: Option<SearchOptions>,
129) -> Result<Option<Value>, String> {
130    let client = get_client().await?;
131    let db_name = get_db_name().await;
132
133    let db = client.database(&db_name);
134    let collection = db.collection::<Document>(&collection_name);
135
136    let mut find_options = MongoFindOneOptions::builder().build();
137
138    if let Some(opts) = options {
139        if let Some(skip) = opts.skip {
140            find_options.skip = Some(skip);
141        }
142
143        if let Some(sort) = opts.sort {
144            if let Ok(sort_doc) = mongodb::bson::to_document(&sort) {
145                find_options.sort = Some(sort_doc);
146            }
147        }
148        // Note: limit and page doesn't make much sense for findOne, usually.
149        // Start/skip/sort apply.
150    }
151
152    let query = if let Some(f) = filter {
153        mongodb::bson::to_document(&f).map_err(|e| e.to_string())?
154    } else {
155        mongodb::bson::doc! {}
156    };
157
158    let result = collection
159        .find_one(query, find_options)
160        .await
161        .map_err(|e| e.to_string())?;
162
163    match result {
164        Some(doc) => {
165            let json_doc: Value = mongodb::bson::from_document(doc).map_err(|e| e.to_string())?;
166            Ok(Some(json_doc))
167        }
168        None => Ok(None),
169    }
170}
171
172pub async fn update_one_document(
173    collection_name: String,
174    filter: Value,
175    update: Value,
176    options: Option<Value>,
177) -> Result<Value, String> {
178    let client = get_client().await?;
179    let db_name = get_db_name().await;
180
181    let db = client.database(&db_name);
182    let collection = db.collection::<Document>(&collection_name);
183
184    let filter_doc = mongodb::bson::to_document(&filter).map_err(|e| e.to_string())?;
185    let update_doc = mongodb::bson::to_document(&update).map_err(|e| e.to_string())?;
186
187    let mut update_options = mongodb::options::UpdateOptions::builder().build();
188
189    if let Some(opts) = options {
190        if let Some(upsert) = opts.get("upsert").and_then(|v| v.as_bool()) {
191            update_options.upsert = Some(upsert);
192        }
193    }
194
195    let result = collection
196        .update_one(filter_doc, update_doc, update_options)
197        .await
198        .map_err(|e| e.to_string())?;
199
200    let result_json = serde_json::to_value(&result).map_err(|e| e.to_string())?;
201    Ok(result_json)
202}