1use std::collections::BTreeMap;
14use std::sync::Arc;
15
16use uqa_core::{DocId, FieldName, PathSegment, Value};
17
18use crate::backend::{StorageBackendError, StorageBackendResult};
19
20pub type Document = BTreeMap<FieldName, Value>;
22
23const MISSING_SHARED_SLOT: usize = usize::MAX;
24static SHARED_NULL_VALUE: Value = Value::Null;
25
26#[derive(Debug, Clone, PartialEq)]
33pub struct SharedDocumentRow {
34 values: Arc<Vec<Value>>,
35 projection: Arc<[usize]>,
36}
37
38impl SharedDocumentRow {
39 pub(crate) fn new(values: Arc<Vec<Value>>, projection: Arc<[usize]>) -> Self {
40 debug_assert!(projection
41 .iter()
42 .all(|slot| *slot == MISSING_SHARED_SLOT || *slot < values.len()));
43 Self { values, projection }
44 }
45
46 pub fn project<'a>(&'a self, output: &mut Vec<&'a Value>) {
48 output.clear();
49 output.extend(self.projection.iter().map(|slot| {
50 if *slot == MISSING_SHARED_SLOT {
51 &SHARED_NULL_VALUE
52 } else {
53 &self.values[*slot]
54 }
55 }));
56 }
57
58 pub fn with_projected<R>(&self, visitor: impl FnOnce(&[&Value]) -> R) -> R {
62 const INLINE_FIELDS: usize = 32;
63 if self.projection.len() <= INLINE_FIELDS {
64 let mut projected = [&SHARED_NULL_VALUE; INLINE_FIELDS];
65 for (output, slot) in projected.iter_mut().zip(self.projection.iter()) {
66 if *slot != MISSING_SHARED_SLOT {
67 *output = &self.values[*slot];
68 }
69 }
70 visitor(&projected[..self.projection.len()])
71 } else {
72 let projected = self
73 .projection
74 .iter()
75 .map(|slot| {
76 if *slot == MISSING_SHARED_SLOT {
77 &SHARED_NULL_VALUE
78 } else {
79 &self.values[*slot]
80 }
81 })
82 .collect::<Vec<_>>();
83 visitor(&projected)
84 }
85 }
86
87 pub fn indexed_values(&self) -> (&[Value], &[usize]) {
89 (&self.values, &self.projection)
90 }
91
92 pub fn into_parts(self) -> (Arc<Vec<Value>>, Arc<[usize]>) {
95 (self.values, self.projection)
96 }
97}
98
99pub trait DocumentStore: Send + Sync {
105 fn put(&mut self, doc_id: DocId, document: Document) -> StorageBackendResult<()>;
106 fn get(&self, doc_id: DocId) -> StorageBackendResult<Option<Document>>;
107 fn contains_doc_id(&self, doc_id: DocId) -> StorageBackendResult<bool> {
108 Ok(self.get(doc_id)?.is_some())
109 }
110 fn delete(&mut self, doc_id: DocId) -> StorageBackendResult<()>;
111 fn clear(&mut self) -> StorageBackendResult<()>;
112
113 fn get_field(&self, doc_id: DocId, field: &str) -> StorageBackendResult<Option<Value>> {
117 Ok(self
118 .get(doc_id)?
119 .and_then(|document| document.get(field).cloned()))
120 }
121
122 fn find_doc_id_by_field(
126 &self,
127 field: &str,
128 value: &Value,
129 ) -> StorageBackendResult<Option<DocId>> {
130 for doc_id in self.doc_ids()? {
131 if self.get_field(doc_id, field)?.as_ref() == Some(value) {
132 return Ok(Some(doc_id));
133 }
134 }
135 Ok(None)
136 }
137
138 fn patch_fields(
143 &mut self,
144 doc_id: DocId,
145 updates: &BTreeMap<String, Value>,
146 ) -> StorageBackendResult<bool> {
147 let Some(mut document) = self.get(doc_id)? else {
148 return Ok(false);
149 };
150 for (field, value) in updates {
151 if matches!(value, Value::Null) {
152 document.remove(field);
153 } else {
154 document.insert(field.clone(), value.clone());
155 }
156 }
157 self.put(doc_id, document)?;
158 Ok(true)
159 }
160
161 fn get_many(&self, doc_ids: &[DocId]) -> StorageBackendResult<BTreeMap<DocId, Document>> {
166 let mut out = BTreeMap::new();
167 for doc_id in doc_ids {
168 if let Some(document) = self.get(*doc_id)? {
169 out.insert(*doc_id, document);
170 }
171 }
172 Ok(out)
173 }
174
175 fn get_fields_multi(
181 &self,
182 doc_ids: &[DocId],
183 fields: &[&str],
184 ) -> StorageBackendResult<BTreeMap<DocId, Vec<Value>>> {
185 let mut out = BTreeMap::new();
186 for doc_id in doc_ids {
187 let Some(document) = self.get(*doc_id)? else {
188 continue;
189 };
190 let values = fields
191 .iter()
192 .map(|field| document.get(*field).cloned().unwrap_or(Value::Null))
193 .collect();
194 out.insert(*doc_id, values);
195 }
196 Ok(out)
197 }
198
199 fn for_each_fields_multi(
205 &self,
206 doc_ids: &[DocId],
207 fields: &[&str],
208 visitor: &mut dyn FnMut(DocId, Vec<Value>) -> bool,
209 ) -> StorageBackendResult<()> {
210 let mut projected = self.get_fields_multi(doc_ids, fields)?;
211 for doc_id in doc_ids {
212 let values = projected
213 .remove(doc_id)
214 .unwrap_or_else(|| vec![Value::Null; fields.len()]);
215 if !visitor(*doc_id, values) {
216 break;
217 }
218 }
219 Ok(())
220 }
221
222 fn for_each_fields_multi_ref(
227 &self,
228 doc_ids: &[DocId],
229 fields: &[&str],
230 visitor: &mut dyn FnMut(DocId, &[&Value]) -> bool,
231 ) -> StorageBackendResult<()> {
232 self.for_each_fields_multi(doc_ids, fields, &mut |doc_id, values| {
233 let references: Vec<&Value> = values.iter().collect();
234 visitor(doc_id, &references)
235 })
236 }
237
238 fn for_each_fields_multi_ref_with_presence(
243 &self,
244 doc_ids: &[DocId],
245 fields: &[&str],
246 visitor: &mut dyn FnMut(DocId, bool, &[&Value]) -> bool,
247 ) -> StorageBackendResult<()> {
248 if fields.is_empty() {
249 for doc_id in doc_ids {
250 if !visitor(*doc_id, self.contains_doc_id(*doc_id)?, &[]) {
251 break;
252 }
253 }
254 return Ok(());
255 }
256
257 let projected = self.get_fields_multi(doc_ids, fields)?;
258 let null = Value::Null;
259 let missing = vec![&null; fields.len()];
260 for doc_id in doc_ids {
261 let Some(values) = projected.get(doc_id) else {
262 if !visitor(*doc_id, false, &missing) {
263 break;
264 }
265 continue;
266 };
267 let references = values.iter().collect::<Vec<_>>();
268 if !visitor(*doc_id, true, &references) {
269 break;
270 }
271 }
272 Ok(())
273 }
274
275 fn get_shared_fields(
280 &self,
281 _doc_ids: &[DocId],
282 _fields: &[&str],
283 ) -> StorageBackendResult<Option<Vec<Option<SharedDocumentRow>>>> {
284 Ok(None)
285 }
286
287 fn get_fields_bulk(
291 &self,
292 doc_ids: &[DocId],
293 field: &str,
294 ) -> StorageBackendResult<BTreeMap<DocId, Value>> {
295 let mut out = BTreeMap::new();
296 for doc_id in doc_ids {
297 out.insert(
298 *doc_id,
299 self.get_field(*doc_id, field)?.unwrap_or(Value::Null),
300 );
301 }
302 Ok(out)
303 }
304
305 fn has_value(&self, field: &str, value: &Value) -> StorageBackendResult<bool> {
307 for doc_id in self.doc_ids()? {
308 if self.get_field(doc_id, field)?.as_ref() == Some(value) {
309 return Ok(true);
310 }
311 }
312 Ok(false)
313 }
314
315 fn find_doc_id_by_fields(
318 &self,
319 fields: &[String],
320 values: &[Value],
321 ) -> StorageBackendResult<Option<DocId>> {
322 if fields.is_empty() || fields.len() != values.len() {
323 return Ok(None);
324 }
325 for doc_id in self.doc_ids()? {
326 let mut matches = true;
327 for (field, value) in fields.iter().zip(values) {
328 if self.get_field(doc_id, field)?.unwrap_or(Value::Null) != *value {
329 matches = false;
330 break;
331 }
332 }
333 if matches {
334 return Ok(Some(doc_id));
335 }
336 }
337 Ok(None)
338 }
339
340 fn eval_path(
342 &self,
343 doc_id: DocId,
344 path: &[PathSegment],
345 ) -> StorageBackendResult<Option<Value>> {
346 let Some(document) = self.get(doc_id)? else {
347 return Ok(None);
348 };
349 Ok(eval_path_in_document(&document, path))
350 }
351
352 fn doc_ids(&self) -> StorageBackendResult<Vec<DocId>>;
353
354 fn next_doc_id(&self, after: Option<DocId>) -> StorageBackendResult<Option<DocId>> {
359 Ok(self
360 .doc_ids()?
361 .into_iter()
362 .filter(|doc_id| after.is_none_or(|after| *doc_id > after))
363 .min())
364 }
365
366 fn next_doc_ids(&self, after: Option<DocId>, limit: usize) -> StorageBackendResult<Vec<DocId>> {
370 if limit == 0 {
371 return Ok(Vec::new());
372 }
373 let mut doc_ids = self.doc_ids()?;
374 doc_ids.sort_unstable();
375 Ok(doc_ids
376 .into_iter()
377 .filter(|doc_id| after.is_none_or(|after| *doc_id > after))
378 .take(limit)
379 .collect())
380 }
381
382 fn next_shared_fields(
386 &self,
387 _after: Option<DocId>,
388 _limit: usize,
389 _fields: &[&str],
390 ) -> StorageBackendResult<Option<Vec<(DocId, SharedDocumentRow)>>> {
391 Ok(None)
392 }
393
394 fn for_each_next_fields(
396 &self,
397 _after: Option<DocId>,
398 _limit: usize,
399 _fields: &[&str],
400 _visitor: &mut dyn FnMut(DocId, &[&Value]) -> bool,
401 ) -> StorageBackendResult<Option<usize>> {
402 Ok(None)
403 }
404
405 fn max_doc_id(&self) -> StorageBackendResult<DocId> {
406 Ok(self.doc_ids()?.into_iter().max().unwrap_or(0))
407 }
408
409 fn len(&self) -> StorageBackendResult<usize>;
410
411 fn is_empty(&self) -> StorageBackendResult<bool> {
412 Ok(self.len()? == 0)
413 }
414
415 fn iter_all(&self) -> StorageBackendResult<Box<dyn Iterator<Item = (DocId, Document)> + '_>> {
419 let mut ids = self.doc_ids()?;
420 ids.sort_unstable();
421 let snapshot = self.snapshot()?;
422 let mut rows = Vec::with_capacity(ids.len());
423 for doc_id in ids {
424 if let Some(document) = snapshot.get(doc_id)? {
425 rows.push((doc_id, document));
426 }
427 }
428 Ok(Box::new(rows.into_iter()))
429 }
430
431 fn snapshot(&self) -> StorageBackendResult<Arc<dyn DocumentStore>>;
435
436 fn writable_snapshot(&self) -> StorageBackendResult<Box<dyn DocumentStore>> {
440 Err(StorageBackendError::Other(
441 "writable document-store snapshots are not supported by this backend".into(),
442 ))
443 }
444}
445
446pub fn eval_path_in_document(doc: &Document, path: &[PathSegment]) -> Option<Value> {
450 let mut current: Value = match path.first()? {
451 PathSegment::Key(k) => doc.get(k)?.clone(),
452 PathSegment::Index(_) => return None,
453 };
454 for seg in path.iter().skip(1) {
455 current = match (current, seg) {
456 (Value::Map(m), PathSegment::Key(k)) => m.get(k)?.clone(),
457 (Value::List(items), PathSegment::Index(i)) => items.get(*i)?.clone(),
458 (Value::List(items), PathSegment::Key(k)) => {
459 let collected: Vec<Value> = items
460 .into_iter()
461 .filter_map(|v| match v {
462 Value::Map(m) => m.get(k).cloned(),
463 _ => None,
464 })
465 .collect();
466 Value::List(collected)
467 }
468 _ => return None,
469 };
470 }
471 Some(current)
472}
473
474mod memory;
475
476pub use memory::MemoryDocumentStore;