1use bson::{doc, Bson, Document};
4use futures::TryStreamExt;
5use mongodb::options::IndexOptions;
6use mongodb::{Client, Database, IndexModel};
7use parse_rust_core::{ErrorCode, ParseError, ParseValue};
8use parse_rust_schema::storage_format::{
9 field_type_to_storage, storage_to_field_type, NON_FIELD_KEYS,
10};
11use parse_rust_storage::{
12 ClassSchema, Comparison, Constraint, QueryOptions, Row, SortDirection, StorageAdapter,
13 WriteResult,
14};
15
16use crate::transform::{
17 mongo_object_to_parse, parse_object_to_mongo_create, value_to_bson_for_query,
18};
19
20const SCHEMA_COLLECTION: &str = "_SCHEMA";
23
24pub struct MongoAdapter {
25 db: Database,
26}
27
28impl MongoAdapter {
29 pub async fn connect(uri: &str, database: &str) -> Result<Self, ParseError> {
30 let client = Client::with_uri_str(uri).await.map_err(mongo_err)?;
31 Ok(Self {
32 db: client.database(database),
33 })
34 }
35
36 fn build_filter(
41 schema: &ClassSchema,
42 constraints: &[Constraint],
43 ) -> Result<Document, ParseError> {
44 let mut filter = Document::new();
45 for c in constraints {
46 let key = crate::transform::storage_key(schema, &c.field);
47 let value = |v: &ParseValue| value_to_bson_for_query(schema, &c.field, v);
48
49 let entry: Bson = match &c.comparison {
50 Comparison::Equal(v) => value(v)?,
51 Comparison::NotEqual(v) => Bson::Document(doc! { "$ne": value(v)? }),
52 Comparison::GreaterThan(v) => Bson::Document(doc! { "$gt": value(v)? }),
53 Comparison::GreaterThanOrEqual(v) => Bson::Document(doc! { "$gte": value(v)? }),
54 Comparison::LessThan(v) => Bson::Document(doc! { "$lt": value(v)? }),
55 Comparison::LessThanOrEqual(v) => Bson::Document(doc! { "$lte": value(v)? }),
56 Comparison::In(items) => Bson::Document(doc! {
57 "$in": items.iter().map(value).collect::<Result<Vec<_>, _>>()?
58 }),
59 Comparison::NotIn(items) => Bson::Document(doc! {
60 "$nin": items.iter().map(value).collect::<Result<Vec<_>, _>>()?
61 }),
62 Comparison::Exists(b) => Bson::Document(doc! { "$exists": *b }),
63 };
64
65 merge_constraint(&mut filter, key, entry)?;
69 }
70 Ok(filter)
71 }
72}
73
74fn merge_constraint(filter: &mut Document, key: String, entry: Bson) -> Result<(), ParseError> {
76 match filter.remove(&key) {
77 None => {
78 filter.insert(key, entry);
79 }
80 Some(existing) => match (existing, entry) {
81 (Bson::Document(mut a), Bson::Document(b)) => {
83 for (k, v) in b {
84 a.insert(k, v);
85 }
86 filter.insert(key, Bson::Document(a));
87 }
88 _ => {
91 return Err(ParseError::invalid_query(format!(
92 "conflicting constraints on field {key}"
93 )))
94 }
95 },
96 }
97 Ok(())
98}
99
100fn mongo_err(e: mongodb::error::Error) -> ParseError {
101 if let mongodb::error::ErrorKind::Write(mongodb::error::WriteFailure::WriteError(we)) =
104 e.kind.as_ref()
105 {
106 if we.code == 11000 {
107 return ParseError::new(ErrorCode::DuplicateValue, we.message.clone());
108 }
109 }
110 ParseError::new(
111 ErrorCode::InternalServerError,
112 format!("storage error: {e}"),
113 )
114}
115
116impl StorageAdapter for MongoAdapter {
117 async fn all_schemas(&self) -> Result<Vec<ClassSchema>, ParseError> {
118 let mut cursor = self
119 .db
120 .collection::<Document>(SCHEMA_COLLECTION)
121 .find(doc! {})
122 .await
123 .map_err(mongo_err)?;
124
125 let mut out = Vec::new();
126 while let Some(doc) = cursor.try_next().await.map_err(mongo_err)? {
127 let Some(class_name) = doc.get_str("_id").ok() else {
128 continue;
129 };
130 let mut schema = parse_rust_schema::default_schema(class_name);
131 for (key, value) in &doc {
132 if NON_FIELD_KEYS.contains(&key.as_str()) {
133 continue;
134 }
135 let Bson::String(type_str) = value else {
136 continue;
137 };
138 if let Some(ty) = storage_to_field_type(type_str) {
141 schema.fields.insert(key.clone(), ty);
142 }
143 }
144 out.push(schema);
145 }
146 Ok(out)
147 }
148
149 async fn upsert_schema(&self, schema: &ClassSchema) -> Result<(), ParseError> {
150 let mut set = Document::new();
151 for (name, ty) in &schema.fields {
152 let s = field_type_to_storage(ty);
153 if s.is_empty() {
155 continue;
156 }
157 set.insert(name.clone(), s);
158 }
159 self.db
160 .collection::<Document>(SCHEMA_COLLECTION)
161 .update_one(doc! { "_id": &schema.class_name }, doc! { "$set": set })
162 .upsert(true)
163 .await
164 .map_err(mongo_err)?;
165 Ok(())
166 }
167
168 async fn create(&self, schema: &ClassSchema, row: &Row) -> Result<WriteResult, ParseError> {
169 let doc = parse_object_to_mongo_create(schema, row)?;
170 let object_id = doc
171 .get_str("_id")
172 .map_err(|_| ParseError::new(ErrorCode::MissingObjectId, "objectId is required"))?
173 .to_string();
174 self.db
175 .collection::<Document>(&schema.class_name)
176 .insert_one(doc)
177 .await
178 .map_err(mongo_err)?;
179 Ok(WriteResult { object_id })
180 }
181
182 async fn find(
183 &self,
184 schema: &ClassSchema,
185 constraints: &[Constraint],
186 options: &QueryOptions,
187 ) -> Result<Vec<Row>, ParseError> {
188 let filter = Self::build_filter(schema, constraints)?;
189 let collection = self.db.collection::<Document>(&schema.class_name);
192 let mut find = collection.find(filter);
193
194 if let Some(limit) = options.limit {
195 if limit == 0 {
198 return Ok(Vec::new());
199 }
200 find = find.limit(limit as i64);
201 }
202 if let Some(skip) = options.skip {
203 find = find.skip(skip as u64);
204 }
205 if !options.order.is_empty() {
206 let mut sort = Document::new();
207 for (key, dir) in &options.order {
208 let dir = match dir {
209 SortDirection::Ascending => 1,
210 SortDirection::Descending => -1,
211 };
212 sort.insert(crate::transform::storage_key(schema, key), dir);
213 }
214 find = find.sort(sort);
215 }
216 if let Some(keys) = &options.keys {
217 let mut projection = Document::new();
218 for k in keys {
219 projection.insert(crate::transform::storage_key(schema, k), 1);
220 }
221 for always in ["_rperm", "_wperm", "_created_at", "_updated_at"] {
226 projection.insert(always, 1);
227 }
228 find = find.projection(projection);
229 }
230
231 let mut cursor = find.await.map_err(mongo_err)?;
232 let mut out = Vec::new();
233 while let Some(doc) = cursor.try_next().await.map_err(mongo_err)? {
234 out.push(mongo_object_to_parse(&doc)?);
235 }
236 Ok(out)
237 }
238
239 async fn count(
240 &self,
241 schema: &ClassSchema,
242 constraints: &[Constraint],
243 ) -> Result<u64, ParseError> {
244 let filter = Self::build_filter(schema, constraints)?;
245 self.db
246 .collection::<Document>(&schema.class_name)
247 .count_documents(filter)
248 .await
249 .map_err(mongo_err)
250 }
251
252 async fn update(
253 &self,
254 schema: &ClassSchema,
255 constraints: &[Constraint],
256 values: &Row,
257 ) -> Result<u64, ParseError> {
258 let filter = Self::build_filter(schema, constraints)?;
259 let set = parse_object_to_mongo_create(schema, values)?;
260 if set.is_empty() {
261 return Ok(0);
262 }
263 let res = self
264 .db
265 .collection::<Document>(&schema.class_name)
266 .update_many(filter, doc! { "$set": set })
267 .await
268 .map_err(mongo_err)?;
269 Ok(res.matched_count)
270 }
271
272 async fn delete(
273 &self,
274 schema: &ClassSchema,
275 constraints: &[Constraint],
276 ) -> Result<u64, ParseError> {
277 let filter = Self::build_filter(schema, constraints)?;
278 let res = self
279 .db
280 .collection::<Document>(&schema.class_name)
281 .delete_many(filter)
282 .await
283 .map_err(mongo_err)?;
284 Ok(res.deleted_count)
285 }
286
287 async fn ensure_unique_index(
288 &self,
289 class_name: &str,
290 fields: &[&str],
291 name: Option<&str>,
292 ) -> Result<(), ParseError> {
293 let mut keys = Document::new();
294 for f in fields {
295 keys.insert(f.to_string(), 1);
296 }
297 let mut opts = IndexOptions::builder().unique(true).sparse(true).build();
301 opts.name = name.map(str::to_string);
302
303 self.db
304 .collection::<Document>(class_name)
305 .create_index(IndexModel::builder().keys(keys).options(opts).build())
306 .await
307 .map_err(mongo_err)?;
308 Ok(())
309 }
310}