bonsaidb_core/connection/lowlevel.rs
1use std::borrow::Borrow;
2use std::collections::BTreeMap;
3
4use arc_bytes::serde::Bytes;
5use async_trait::async_trait;
6
7use super::GroupedReductions;
8use crate::connection::{
9 AccessPolicy, HasSession, QueryKey, Range, RangeRef, SerializedQueryKey, Sort,
10};
11use crate::document::{
12 CollectionDocument, CollectionHeader, Document, DocumentId, HasHeader, Header, OwnedDocument,
13};
14use crate::key::{self, ByteSource, Key, KeyEncoding};
15use crate::schema::view::map::{
16 CollectionMap, MappedDocuments, MappedSerializedValue, ViewMappings,
17};
18use crate::schema::view::{self};
19use crate::schema::{self, CollectionName, MappedValue, Schematic, SerializedCollection, ViewName};
20use crate::transaction::{OperationResult, Transaction};
21use crate::Error;
22
23/// The low-level interface to a database's [`schema::Schema`], giving access to
24/// [`Collection`s](crate::schema::Collection) and
25/// [`Views`s](crate::schema::View). This trait is not safe to use within async
26/// contexts and will block the current thread. For async access, use
27/// [`AsyncLowLevelConnection`].
28///
29/// This trait's methods are not designed for ergonomics. See
30/// [`Connection`](super::Connection) for a higher-level interface.
31pub trait LowLevelConnection: HasSchema + HasSession {
32 /// Inserts a newly created document into the connected [`schema::Schema`]
33 /// for the [`Collection`](schema::Collection) `C`. If `id` is `None` a unique id will be
34 /// generated. If an id is provided and a document already exists with that
35 /// id, a conflict error will be returned.
36 ///
37 /// This is the lower-level API. For better ergonomics, consider using
38 /// one of:
39 ///
40 /// - [`SerializedCollection::push_into()`]
41 /// - [`SerializedCollection::insert_into()`]
42 /// - [`self.collection::<Collection>().insert()`](super::Collection::insert)
43 /// - [`self.collection::<Collection>().push()`](super::Collection::push)
44 fn insert<C, PrimaryKey, B>(
45 &self,
46 id: Option<&PrimaryKey>,
47 contents: B,
48 ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
49 where
50 C: schema::Collection,
51 B: Into<Bytes> + Send,
52 PrimaryKey: KeyEncoding<C::PrimaryKey> + Send + ?Sized,
53 {
54 let contents = contents.into();
55 let results = self.apply_transaction(Transaction::insert(
56 C::collection_name(),
57 id.map(|id| DocumentId::new(id)).transpose()?,
58 contents,
59 ))?;
60 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
61 CollectionHeader::try_from(header)
62 } else {
63 unreachable!(
64 "apply_transaction on a single insert should yield a single DocumentUpdated entry"
65 )
66 }
67 }
68
69 /// Updates an existing document in the connected [`schema::Schema`] for the
70 /// [`Collection`](schema::Collection) `C`. Upon success, `doc.revision` will be updated with
71 /// the new revision.
72 ///
73 /// This is the lower-level API. For better ergonomics, consider using
74 /// one of:
75 ///
76 /// - [`CollectionDocument::update()`]
77 /// - [`self.collection::<Collection>().update()`](super::Collection::update)
78 fn update<C: schema::Collection, D: Document<C> + Send + Sync>(
79 &self,
80 doc: &mut D,
81 ) -> Result<(), Error> {
82 let results = self.apply_transaction(Transaction::update(
83 C::collection_name(),
84 doc.header().into_header()?,
85 doc.bytes()?,
86 ))?;
87 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
88 doc.set_header(header)?;
89 Ok(())
90 } else {
91 unreachable!(
92 "apply_transaction on a single update should yield a single DocumentUpdated entry"
93 )
94 }
95 }
96
97 /// Overwrites an existing document, or inserts a new document. Upon success,
98 /// `doc.revision` will be updated with the new revision information.
99 ///
100 /// This is the lower-level API. For better ergonomics, consider using
101 /// one of:
102 ///
103 /// - [`SerializedCollection::overwrite()`]
104 /// - [`SerializedCollection::overwrite_into()`]
105 /// - [`self.collection::<Collection>().overwrite()`](super::Collection::overwrite)
106 fn overwrite<C, PrimaryKey>(
107 &self,
108 id: &PrimaryKey,
109 contents: Vec<u8>,
110 ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
111 where
112 C: schema::Collection,
113 PrimaryKey: KeyEncoding<C::PrimaryKey>,
114 {
115 let results = self.apply_transaction(Transaction::overwrite(
116 C::collection_name(),
117 DocumentId::new(id)?,
118 contents,
119 ))?;
120 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
121 CollectionHeader::try_from(header)
122 } else {
123 unreachable!(
124 "apply_transaction on a single update should yield a single DocumentUpdated entry"
125 )
126 }
127 }
128
129 /// Retrieves a stored document from [`Collection`](schema::Collection) `C` identified by `id`.
130 ///
131 /// This is a lower-level API. For better ergonomics, consider using one of:
132 ///
133 /// - [`SerializedCollection::get()`]
134 /// - [`self.collection::<Collection>().get()`](super::Collection::get)
135 fn get<C, PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
136 where
137 C: schema::Collection,
138 PrimaryKey: KeyEncoding<C::PrimaryKey> + ?Sized,
139 {
140 self.get_from_collection(DocumentId::new(id)?, &C::collection_name())
141 }
142
143 /// Retrieves all documents matching `ids`. Documents that are not found are
144 /// not returned, but no error will be generated.
145 ///
146 /// This is a lower-level API. For better ergonomics, consider using one of:
147 ///
148 /// - [`SerializedCollection::get_multiple()`]
149 /// - [`self.collection::<Collection>().get_multiple()`](super::Collection::get_multiple)
150 fn get_multiple<'id, C, PrimaryKey, DocumentIds, I>(
151 &self,
152 ids: DocumentIds,
153 ) -> Result<Vec<OwnedDocument>, Error>
154 where
155 C: schema::Collection,
156 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
157 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
158 PrimaryKey: KeyEncoding<C::PrimaryKey> + 'id + ?Sized,
159 {
160 let ids = ids
161 .into_iter()
162 .map(|id| DocumentId::new(id))
163 .collect::<Result<Vec<_>, _>>()?;
164 self.get_multiple_from_collection(&ids, &C::collection_name())
165 }
166
167 /// Retrieves all documents within the range of `ids`. To retrieve all
168 /// documents, pass in `..` for `ids`.
169 ///
170 /// This is a lower-level API. For better ergonomics, consider using one of:
171 ///
172 /// - [`SerializedCollection::all()`]
173 /// - [`self.collection::<Collection>().all()`](super::Collection::all)
174 /// - [`SerializedCollection::list()`]
175 /// - [`self.collection::<Collection>().list()`](super::Collection::list)
176 fn list<'id, C, R, PrimaryKey>(
177 &self,
178 ids: R,
179 order: Sort,
180 limit: Option<u32>,
181 ) -> Result<Vec<OwnedDocument>, Error>
182 where
183 C: schema::Collection,
184 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
185 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
186 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
187 {
188 let ids = ids.into().map_result(|id| DocumentId::new(id))?;
189 self.list_from_collection(ids, order, limit, &C::collection_name())
190 }
191
192 /// Retrieves all documents within the range of `ids`. To retrieve all
193 /// documents, pass in `..` for `ids`.
194 ///
195 /// This is the lower-level API. For better ergonomics, consider using one
196 /// of:
197 ///
198 /// - [`SerializedCollection::all_async().headers()`](schema::List::headers)
199 /// - [`self.collection::<Collection>().all().headers()`](super::List::headers)
200 /// - [`SerializedCollection::list_async().headers()`](schema::List::headers)
201 /// - [`self.collection::<Collection>().list().headers()`](super::List::headers)
202 fn list_headers<'id, C, R, PrimaryKey>(
203 &self,
204 ids: R,
205 order: Sort,
206 limit: Option<u32>,
207 ) -> Result<Vec<Header>, Error>
208 where
209 C: schema::Collection,
210 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
211 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
212 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
213 {
214 let ids = ids.into().map_result(|id| DocumentId::new(id))?;
215 self.list_headers_from_collection(ids, order, limit, &C::collection_name())
216 }
217
218 /// Counts the number of documents within the range of `ids`.
219 ///
220 /// This is a lower-level API. For better ergonomics, consider using one of:
221 ///
222 /// - [`SerializedCollection::all().count()`](schema::List::count)
223 /// - [`self.collection::<Collection>().all().count()`](super::List::count)
224 /// - [`SerializedCollection::list().count()`](schema::List::count)
225 /// - [`self.collection::<Collection>().list().count()`](super::List::count)
226 fn count<'id, C, R, PrimaryKey>(&self, ids: R) -> Result<u64, Error>
227 where
228 C: schema::Collection,
229 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
230 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
231 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
232 {
233 self.count_from_collection(
234 ids.into().map_result(|id| DocumentId::new(id))?,
235 &C::collection_name(),
236 )
237 }
238
239 /// Removes a `Document` from the database.
240 ///
241 /// This is a lower-level API. For better ergonomics, consider using
242 /// one of:
243 ///
244 /// - [`CollectionDocument::delete()`]
245 /// - [`self.collection::<Collection>().delete()`](super::Collection::delete)
246 fn delete<C: schema::Collection, H: HasHeader + Send + Sync>(
247 &self,
248 doc: &H,
249 ) -> Result<(), Error> {
250 let results =
251 self.apply_transaction(Transaction::delete(C::collection_name(), doc.header()?))?;
252 if let OperationResult::DocumentDeleted { .. } = &results[0] {
253 Ok(())
254 } else {
255 unreachable!(
256 "apply_transaction on a single update should yield a single DocumentUpdated entry"
257 )
258 }
259 }
260
261 /// Queries for view entries matching [`View`](schema::View).
262 ///
263 /// This is a lower-level API. For better ergonomics, consider querying the
264 /// view using [`View::entries(self).query()`](super::View::query) instead. The
265 /// parameters for the query can be customized on the builder returned from
266 /// [`SerializedView::entries()`](schema::SerializedView::entries),
267 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
268 /// or [`Connection::view()`](super::Connection::view).
269 fn query<V: schema::SerializedView, Key>(
270 &self,
271 key: Option<QueryKey<'_, V::Key, Key>>,
272 order: Sort,
273 limit: Option<u32>,
274 access_policy: AccessPolicy,
275 ) -> Result<ViewMappings<V>, Error>
276 where
277 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
278 V::Key: Borrow<Key> + PartialEq<Key>,
279 {
280 let view = self.schematic().view::<V>()?;
281 let mappings = self.query_by_name(
282 &view.view_name(),
283 key.map(|key| key.serialized()).transpose()?,
284 order,
285 limit,
286 access_policy,
287 )?;
288 mappings
289 .into_iter()
290 .map(|mapping| {
291 Ok(CollectionMap {
292 key: <V::Key as key::Key>::from_ord_bytes(ByteSource::Borrowed(&mapping.key))
293 .map_err(view::Error::key_serialization)
294 .map_err(Error::from)?,
295 value: V::deserialize(&mapping.value)?,
296 source: mapping.source.try_into()?,
297 })
298 })
299 .collect::<Result<Vec<_>, Error>>()
300 }
301
302 /// Queries for view entries matching [`View`](schema::View) with their
303 /// source documents.
304 ///
305 /// This is a lower-level API. For better ergonomics, consider querying the
306 /// view using
307 /// [`View::entries(self).query_with_docs()`](super::View::query_with_docs)
308 /// instead. The parameters for the query can be customized on the builder
309 /// returned from
310 /// [`SerializedView::entries()`](schema::SerializedView::entries),
311 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
312 /// or [`Connection::view()`](super::Connection::view).
313 fn query_with_docs<V: schema::SerializedView, Key>(
314 &self,
315 key: Option<QueryKey<'_, V::Key, Key>>,
316 order: Sort,
317 limit: Option<u32>,
318 access_policy: AccessPolicy,
319 ) -> Result<MappedDocuments<OwnedDocument, V>, Error>
320 where
321 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
322 V::Key: Borrow<Key> + PartialEq<Key>,
323 {
324 // Query permission is checked by the query call
325 let results = self.query::<V, Key>(key, order, limit, access_policy)?;
326
327 // Verify that there is permission to fetch each document
328 let documents = self
329 .get_multiple::<V::Collection, _, _, _>(results.iter().map(|m| &m.source.id))?
330 .into_iter()
331 .map(|doc| {
332 let id = doc.header.id.deserialize()?;
333 Ok((id, doc))
334 })
335 .collect::<Result<BTreeMap<_, _>, Error>>()?;
336
337 Ok(MappedDocuments {
338 mappings: results,
339 documents,
340 })
341 }
342
343 /// Queries for view entries matching [`View`](schema::View) with their
344 /// source documents, deserialized.
345 ///
346 /// This is a lower-level API. For better ergonomics, consider querying the
347 /// view using
348 /// [`View::entries(self).query_with_collection_docs()`](super::View::query_with_collection_docs)
349 /// instead. The parameters for the query can be customized on the builder
350 /// returned from
351 /// [`SerializedView::entries()`](schema::SerializedView::entries),
352 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
353 /// or [`Connection::view()`](super::Connection::view).
354 fn query_with_collection_docs<V, Key>(
355 &self,
356 key: Option<QueryKey<'_, V::Key, Key>>,
357 order: Sort,
358 limit: Option<u32>,
359 access_policy: AccessPolicy,
360 ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
361 where
362 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
363 V::Key: Borrow<Key> + PartialEq<Key>,
364 V: schema::SerializedView,
365 V::Collection: SerializedCollection,
366 <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
367 {
368 let mapped_docs = self.query_with_docs::<V, Key>(key, order, limit, access_policy)?;
369 let mut collection_docs = BTreeMap::new();
370 for (id, doc) in mapped_docs.documents {
371 collection_docs.insert(id, CollectionDocument::<V::Collection>::try_from(&doc)?);
372 }
373 Ok(MappedDocuments {
374 mappings: mapped_docs.mappings,
375 documents: collection_docs,
376 })
377 }
378
379 /// Reduces the view entries matching [`View`](schema::View).
380 ///
381 /// This is a lower-level API. For better ergonomics, consider reducing the
382 /// view using [`View::entries(self).reduce()`](super::View::reduce)
383 /// instead. The parameters for the query can be customized on the builder
384 /// returned from
385 /// [`SerializedView::entries()`](schema::SerializedView::entries),
386 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
387 /// or [`Connection::view()`](super::Connection::view).
388 fn reduce<V: schema::SerializedView, Key>(
389 &self,
390 key: Option<QueryKey<'_, V::Key, Key>>,
391 access_policy: AccessPolicy,
392 ) -> Result<V::Value, Error>
393 where
394 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
395 V::Key: Borrow<Key> + PartialEq<Key>,
396 {
397 let view = self.schematic().view::<V>()?;
398 self.reduce_by_name(
399 &view.view_name(),
400 key.map(|key| key.serialized()).transpose()?,
401 access_policy,
402 )
403 .and_then(|value| V::deserialize(&value))
404 }
405
406 /// Reduces the view entries matching [`View`](schema::View), reducing the
407 /// values by each unique key.
408 ///
409 /// This is a lower-level API. For better ergonomics, consider reducing the
410 /// view using
411 /// [`View::entries(self).reduce_grouped()`](super::View::reduce_grouped)
412 /// instead. The parameters for the query can be customized on the builder
413 /// returned from
414 /// [`SerializedView::entries()`](schema::SerializedView::entries),
415 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
416 /// or [`Connection::view()`](super::Connection::view).
417 fn reduce_grouped<V: schema::SerializedView, Key>(
418 &self,
419 key: Option<QueryKey<'_, V::Key, Key>>,
420 access_policy: AccessPolicy,
421 ) -> Result<GroupedReductions<V>, Error>
422 where
423 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
424 V::Key: Borrow<Key> + PartialEq<Key>,
425 {
426 let view = self.schematic().view::<V>()?;
427 self.reduce_grouped_by_name(
428 &view.view_name(),
429 key.map(|key| key.serialized()).transpose()?,
430 access_policy,
431 )?
432 .into_iter()
433 .map(|map| {
434 Ok(MappedValue::new(
435 V::Key::from_ord_bytes(ByteSource::Borrowed(&map.key))
436 .map_err(view::Error::key_serialization)?,
437 V::deserialize(&map.value)?,
438 ))
439 })
440 .collect::<Result<Vec<_>, Error>>()
441 }
442
443 /// Deletes all of the documents associated with this view.
444 ///
445 /// This is a lower-level API. For better ergonomics, consider querying the
446 /// view using
447 /// [`View::entries(self).delete_docs()`](super::View::delete_docs())
448 /// instead. The parameters for the query can be customized on the builder
449 /// returned from
450 /// [`SerializedView::entries()`](schema::SerializedView::entries),
451 /// [`SerializedView::entries_async()`](schema::SerializedView::entries_async),
452 /// or [`Connection::view()`](super::Connection::view).
453 fn delete_docs<V: schema::SerializedView, Key>(
454 &self,
455 key: Option<QueryKey<'_, V::Key, Key>>,
456 access_policy: AccessPolicy,
457 ) -> Result<u64, Error>
458 where
459 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
460 V::Key: Borrow<Key> + PartialEq<Key>,
461 {
462 let view = self.schematic().view::<V>()?;
463 self.delete_docs_by_name(
464 &view.view_name(),
465 key.map(|key| key.serialized()).transpose()?,
466 access_policy,
467 )
468 }
469
470 /// Applies a [`Transaction`] to the [`schema::Schema`]. If any operation in the
471 /// [`Transaction`] fails, none of the operations will be applied to the
472 /// [`schema::Schema`].
473 fn apply_transaction(&self, transaction: Transaction) -> Result<Vec<OperationResult>, Error>;
474
475 /// Retrieves the document with `id` stored within the named `collection`.
476 ///
477 /// This is a lower-level API. For better ergonomics, consider using
478 /// one of:
479 ///
480 /// - [`SerializedCollection::get()`]
481 /// - [`self.collection::<Collection>().get()`](super::Collection::get)
482 fn get_from_collection(
483 &self,
484 id: DocumentId,
485 collection: &CollectionName,
486 ) -> Result<Option<OwnedDocument>, Error>;
487
488 /// Retrieves all documents matching `ids` from the named `collection`.
489 /// Documents that are not found are not returned, but no error will be
490 /// generated.
491 ///
492 /// This is a lower-level API. For better ergonomics, consider using one of:
493 ///
494 /// - [`SerializedCollection::get_multiple()`]
495 /// - [`self.collection::<Collection>().get_multiple()`](super::Collection::get_multiple)
496 fn get_multiple_from_collection(
497 &self,
498 ids: &[DocumentId],
499 collection: &CollectionName,
500 ) -> Result<Vec<OwnedDocument>, Error>;
501
502 /// Retrieves all documents within the range of `ids` from the named
503 /// `collection`. To retrieve all documents, pass in `..` for `ids`.
504 ///
505 /// This is a lower-level API. For better ergonomics, consider using one of:
506 ///
507 /// - [`SerializedCollection::all()`]
508 /// - [`self.collection::<Collection>().all()`](super::Collection::all)
509 /// - [`SerializedCollection::list()`]
510 /// - [`self.collection::<Collection>().list()`](super::Collection::list)
511 fn list_from_collection(
512 &self,
513 ids: Range<DocumentId>,
514 order: Sort,
515 limit: Option<u32>,
516 collection: &CollectionName,
517 ) -> Result<Vec<OwnedDocument>, Error>;
518
519 /// Retrieves all headers within the range of `ids` from the named
520 /// `collection`. To retrieve all documents, pass in `..` for `ids`.
521 ///
522 /// This is a lower-level API. For better ergonomics, consider using one of:
523 ///
524 /// - [`SerializedCollection::all().headers()`](schema::List::headers)
525 /// - [`self.collection::<Collection>().all().headers()`](super::AsyncCollection::all)
526 /// - [`SerializedCollection::list().headers()`](schema::List::headers)
527 /// - [`self.collection::<Collection>().list()`](super::AsyncCollection::list)
528 fn list_headers_from_collection(
529 &self,
530 ids: Range<DocumentId>,
531 order: Sort,
532 limit: Option<u32>,
533 collection: &CollectionName,
534 ) -> Result<Vec<Header>, Error>;
535
536 /// Counts the number of documents within the range of `ids` from the named
537 /// `collection`.
538 ///
539 /// This is a lower-level API. For better ergonomics, consider using one of:
540 ///
541 /// - [`SerializedCollection::all().count()`](schema::List::count)
542 /// - [`self.collection::<Collection>().all().count()`](super::List::count)
543 /// - [`SerializedCollection::list().count()`](schema::List::count)
544 /// - [`self.collection::<Collection>().list().count()`](super::List::count)
545 fn count_from_collection(
546 &self,
547 ids: Range<DocumentId>,
548 collection: &CollectionName,
549 ) -> Result<u64, Error>;
550
551 /// Compacts the collection to reclaim unused disk space.
552 ///
553 /// This process is done by writing data to a new file and swapping the file
554 /// once the process completes. This ensures that if a hardware failure,
555 /// power outage, or crash occurs that the original collection data is left
556 /// untouched.
557 ///
558 /// ## Errors
559 ///
560 /// * [`Error::CollectionNotFound`]: database `name` does not exist.
561 /// * [`Error::Other`]: an error occurred while compacting the database.
562 fn compact_collection_by_name(&self, collection: CollectionName) -> Result<(), Error>;
563
564 /// Queries for view entries from the named `view`.
565 ///
566 /// This is a lower-level API. For better ergonomics, consider querying the
567 /// view using [`View::entries(self).query()`](super::View::query) instead. The
568 /// parameters for the query can be customized on the builder returned from
569 /// [`Connection::view()`](super::Connection::view).
570 fn query_by_name(
571 &self,
572 view: &ViewName,
573 key: Option<SerializedQueryKey>,
574 order: Sort,
575 limit: Option<u32>,
576 access_policy: AccessPolicy,
577 ) -> Result<Vec<schema::view::map::Serialized>, Error>;
578
579 /// Queries for view entries from the named `view` with their source
580 /// documents.
581 ///
582 /// This is a lower-level API. For better ergonomics, consider querying the
583 /// view using
584 /// [`View::entries(self).query_with_docs()`](super::View::query_with_docs)
585 /// instead. The parameters for the query can be customized on the builder
586 /// returned from [`Connection::view()`](super::Connection::view).
587 fn query_by_name_with_docs(
588 &self,
589 view: &ViewName,
590 key: Option<SerializedQueryKey>,
591 order: Sort,
592 limit: Option<u32>,
593 access_policy: AccessPolicy,
594 ) -> Result<schema::view::map::MappedSerializedDocuments, Error>;
595
596 /// Reduces the view entries from the named `view`.
597 ///
598 /// This is a lower-level API. For better ergonomics, consider reducing the
599 /// view using [`View::entries(self).reduce()`](super::View::reduce)
600 /// instead. The parameters for the query can be customized on the builder
601 /// returned from [`Connection::view()`](super::Connection::view).
602 fn reduce_by_name(
603 &self,
604 view: &ViewName,
605 key: Option<SerializedQueryKey>,
606 access_policy: AccessPolicy,
607 ) -> Result<Vec<u8>, Error>;
608
609 /// Reduces the view entries from the named `view`, reducing the values by each
610 /// unique key.
611 ///
612 /// This is a lower-level API. For better ergonomics, consider reducing
613 /// the view using
614 /// [`View::entries(self).reduce_grouped()`](super::View::reduce_grouped) instead.
615 /// The parameters for the query can be customized on the builder returned
616 /// from [`Connection::view()`](super::Connection::view).
617 fn reduce_grouped_by_name(
618 &self,
619 view: &ViewName,
620 key: Option<SerializedQueryKey>,
621 access_policy: AccessPolicy,
622 ) -> Result<Vec<MappedSerializedValue>, Error>;
623
624 /// Deletes all source documents for entries that match within the named
625 /// `view`.
626 ///
627 /// This is a lower-level API. For better ergonomics, consider querying the
628 /// view using
629 /// [`View::entries(self).delete_docs()`](super::View::delete_docs())
630 /// instead. The parameters for the query can be customized on the builder
631 /// returned from [`Connection::view()`](super::Connection::view).
632 fn delete_docs_by_name(
633 &self,
634 view: &ViewName,
635 key: Option<SerializedQueryKey>,
636 access_policy: AccessPolicy,
637 ) -> Result<u64, Error>;
638}
639
640/// The low-level interface to a database's [`schema::Schema`], giving access to
641/// [`Collection`s](crate::schema::Collection) and
642/// [`Views`s](crate::schema::View). This trait is for use within async
643/// contexts. For access outside of async contexts, use [`LowLevelConnection`].
644///
645/// This trait's methods are not designed for ergonomics. See
646/// [`AsyncConnection`](super::AsyncConnection) for a higher-level interface.
647#[async_trait]
648pub trait AsyncLowLevelConnection: HasSchema + HasSession + Send + Sync {
649 /// Inserts a newly created document into the connected [`schema::Schema`]
650 /// for the [`Collection`](schema::Collection) `C`. If `id` is `None` a unique id will be
651 /// generated. If an id is provided and a document already exists with that
652 /// id, a conflict error will be returned.
653 ///
654 /// This is the lower-level API. For better ergonomics, consider using
655 /// one of:
656 ///
657 /// - [`SerializedCollection::push_into_async()`]
658 /// - [`SerializedCollection::insert_into_async()`]
659 /// - [`self.collection::<Collection>().insert()`](super::AsyncCollection::insert)
660 /// - [`self.collection::<Collection>().push()`](super::AsyncCollection::push)
661 async fn insert<C: schema::Collection, PrimaryKey: Send, B: Into<Bytes> + Send>(
662 &self,
663 id: Option<&PrimaryKey>,
664 contents: B,
665 ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
666 where
667 PrimaryKey: KeyEncoding<C::PrimaryKey> + ?Sized,
668 {
669 let contents = contents.into();
670 let results = self
671 .apply_transaction(Transaction::insert(
672 C::collection_name(),
673 id.map(|id| DocumentId::new(id)).transpose()?,
674 contents,
675 ))
676 .await?;
677 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
678 CollectionHeader::try_from(header)
679 } else {
680 unreachable!(
681 "apply_transaction on a single insert should yield a single DocumentUpdated entry"
682 )
683 }
684 }
685
686 /// Updates an existing document in the connected [`schema::Schema`] for the
687 /// [`Collection`](schema::Collection)(schema::Collection) `C`. Upon success, `doc.revision`
688 /// will be updated with the new revision.
689 ///
690 /// This is the lower-level API. For better ergonomics, consider using one
691 /// of:
692 ///
693 /// - [`CollectionDocument::update_async()`]
694 /// - [`self.collection::<Collection>().update()`](super::AsyncCollection::update)
695 async fn update<C: schema::Collection, D: Document<C> + Send + Sync>(
696 &self,
697 doc: &mut D,
698 ) -> Result<(), Error> {
699 let results = self
700 .apply_transaction(Transaction::update(
701 C::collection_name(),
702 doc.header().into_header()?,
703 doc.bytes()?,
704 ))
705 .await?;
706 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
707 doc.set_header(header)?;
708 Ok(())
709 } else {
710 unreachable!(
711 "apply_transaction on a single update should yield a single DocumentUpdated entry"
712 )
713 }
714 }
715
716 /// Overwrites an existing document, or inserts a new document. Upon success,
717 /// `doc.revision` will be updated with the new revision information.
718 ///
719 /// This is the lower-level API. For better ergonomics, consider using
720 /// one of:
721 ///
722 /// - [`SerializedCollection::overwrite_async()`]
723 /// - [`SerializedCollection::overwrite_into_async()`]
724 /// - [`self.collection::<Collection>().overwrite()`](super::AsyncCollection::overwrite)
725 async fn overwrite<'a, C, PrimaryKey>(
726 &self,
727 id: &PrimaryKey,
728 contents: Vec<u8>,
729 ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
730 where
731 C: schema::Collection,
732 PrimaryKey: KeyEncoding<C::PrimaryKey>,
733 {
734 let results = self
735 .apply_transaction(Transaction::overwrite(
736 C::collection_name(),
737 DocumentId::new(id)?,
738 contents,
739 ))
740 .await?;
741 if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
742 CollectionHeader::try_from(header)
743 } else {
744 unreachable!(
745 "apply_transaction on a single update should yield a single DocumentUpdated entry"
746 )
747 }
748 }
749
750 /// Retrieves a stored document from [`Collection`](schema::Collection) `C` identified by `id`.
751 ///
752 /// This is the lower-level API. For better ergonomics, consider using
753 /// one of:
754 ///
755 /// - [`SerializedCollection::get_async()`]
756 /// - [`self.collection::<Collection>().get()`](super::AsyncCollection::get)
757 async fn get<C, PrimaryKey>(&self, id: &PrimaryKey) -> Result<Option<OwnedDocument>, Error>
758 where
759 C: schema::Collection,
760 PrimaryKey: KeyEncoding<C::PrimaryKey> + ?Sized,
761 {
762 self.get_from_collection(DocumentId::new(id)?, &C::collection_name())
763 .await
764 }
765
766 /// Retrieves all documents matching `ids`. Documents that are not found
767 /// are not returned, but no error will be generated.
768 ///
769 /// This is the lower-level API. For better ergonomics, consider using
770 /// one of:
771 ///
772 /// - [`SerializedCollection::get_multiple_async()`]
773 /// - [`self.collection::<Collection>().get_multiple()`](super::AsyncCollection::get_multiple)
774 async fn get_multiple<'id, C, PrimaryKey, DocumentIds, I>(
775 &self,
776 ids: DocumentIds,
777 ) -> Result<Vec<OwnedDocument>, Error>
778 where
779 C: schema::Collection,
780 DocumentIds: IntoIterator<Item = &'id PrimaryKey, IntoIter = I> + Send + Sync,
781 I: Iterator<Item = &'id PrimaryKey> + Send + Sync,
782 PrimaryKey: KeyEncoding<C::PrimaryKey> + 'id + ?Sized,
783 {
784 let ids = ids
785 .into_iter()
786 .map(DocumentId::new)
787 .collect::<Result<Vec<_>, _>>()?;
788 self.get_multiple_from_collection(&ids, &C::collection_name())
789 .await
790 }
791
792 /// Retrieves all documents within the range of `ids`. To retrieve all
793 /// documents, pass in `..` for `ids`.
794 ///
795 /// This is the lower-level API. For better ergonomics, consider using one
796 /// of:
797 ///
798 /// - [`SerializedCollection::all_async()`]
799 /// - [`self.collection::<Collection>().all()`](super::AsyncCollection::all)
800 /// - [`SerializedCollection::list_async()`]
801 /// - [`self.collection::<Collection>().list()`](super::AsyncCollection::list)
802 async fn list<'id, C, R, PrimaryKey>(
803 &self,
804 ids: R,
805 order: Sort,
806 limit: Option<u32>,
807 ) -> Result<Vec<OwnedDocument>, Error>
808 where
809 C: schema::Collection,
810 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
811 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
812 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
813 {
814 let ids = ids.into().map_result(|id| DocumentId::new(id))?;
815 self.list_from_collection(ids, order, limit, &C::collection_name())
816 .await
817 }
818
819 /// Retrieves all documents within the range of `ids`. To retrieve all
820 /// documents, pass in `..` for `ids`.
821 ///
822 /// This is the lower-level API. For better ergonomics, consider using one
823 /// of:
824 ///
825 /// - [`SerializedCollection::all_async().headers()`](schema::AsyncList::headers)
826 /// - [`self.collection::<Collection>().all()`](super::AsyncList::headers)
827 /// - [`SerializedCollection::list_async().headers()`](schema::AsyncList::headers)
828 /// - [`self.collection::<Collection>().list().headers()`](super::AsyncList::headers)
829 async fn list_headers<'id, C, R, PrimaryKey>(
830 &self,
831 ids: R,
832 order: Sort,
833 limit: Option<u32>,
834 ) -> Result<Vec<Header>, Error>
835 where
836 C: schema::Collection,
837 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
838 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
839 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
840 {
841 let ids = ids.into().map_result(|id| DocumentId::new(id))?;
842 self.list_headers_from_collection(ids, order, limit, &C::collection_name())
843 .await
844 }
845
846 /// Counts the number of documents within the range of `ids`.
847 ///
848 /// This is the lower-level API. For better ergonomics, consider using
849 /// one of:
850 ///
851 /// - [`SerializedCollection::all_async().count()`](schema::AsyncList::count)
852 /// - [`self.collection::<Collection>().all().count()`](super::AsyncList::count)
853 /// - [`SerializedCollection::list_async().count()`](schema::AsyncList::count)
854 /// - [`self.collection::<Collection>().list().count()`](super::AsyncList::count)
855 async fn count<'id, C, R, PrimaryKey>(&self, ids: R) -> Result<u64, Error>
856 where
857 C: schema::Collection,
858 R: Into<RangeRef<'id, C::PrimaryKey, PrimaryKey>> + Send,
859 PrimaryKey: KeyEncoding<C::PrimaryKey> + PartialEq + 'id + ?Sized,
860 C::PrimaryKey: Borrow<PrimaryKey> + PartialEq<PrimaryKey>,
861 {
862 self.count_from_collection(
863 ids.into().map_result(|id| DocumentId::new(id))?,
864 &C::collection_name(),
865 )
866 .await
867 }
868
869 /// Removes a `Document` from the database.
870 ///
871 /// This is the lower-level API. For better ergonomics, consider using
872 /// one of:
873 ///
874 /// - [`CollectionDocument::delete_async()`]
875 /// - [`self.collection::<Collection>().delete()`](super::AsyncCollection::delete)
876 async fn delete<C: schema::Collection, H: HasHeader + Send + Sync>(
877 &self,
878 doc: &H,
879 ) -> Result<(), Error> {
880 let results = self
881 .apply_transaction(Transaction::delete(C::collection_name(), doc.header()?))
882 .await?;
883 if let OperationResult::DocumentDeleted { .. } = &results[0] {
884 Ok(())
885 } else {
886 unreachable!(
887 "apply_transaction on a single update should yield a single DocumentUpdated entry"
888 )
889 }
890 }
891 /// Queries for view entries matching [`View`](schema::View)(super::AsyncView).
892 ///
893 /// This is the lower-level API. For better ergonomics, consider querying
894 /// the view using [`View::entries(self).query()`](super::AsyncView::query)
895 /// instead. The parameters for the query can be customized on the builder
896 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
897 async fn query<V: schema::SerializedView, Key>(
898 &self,
899 key: Option<QueryKey<'_, V::Key, Key>>,
900 order: Sort,
901 limit: Option<u32>,
902 access_policy: AccessPolicy,
903 ) -> Result<ViewMappings<V>, Error>
904 where
905 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
906 V::Key: Borrow<Key> + PartialEq<Key>,
907 {
908 let view = self.schematic().view::<V>()?;
909 let mappings = self
910 .query_by_name(
911 &view.view_name(),
912 key.map(|key| key.serialized()).transpose()?,
913 order,
914 limit,
915 access_policy,
916 )
917 .await?;
918 mappings
919 .into_iter()
920 .map(|mapping| {
921 Ok(CollectionMap {
922 key: <V::Key as key::Key>::from_ord_bytes(ByteSource::Borrowed(&mapping.key))
923 .map_err(view::Error::key_serialization)
924 .map_err(Error::from)?,
925 value: V::deserialize(&mapping.value)?,
926 source: mapping.source.try_into()?,
927 })
928 })
929 .collect::<Result<Vec<_>, Error>>()
930 }
931
932 /// Queries for view entries matching [`View`](schema::View) with their source documents.
933 ///
934 /// This is the lower-level API. For better ergonomics, consider querying
935 /// the view using [`View::entries(self).query_with_docs()`](super::AsyncView::query_with_docs) instead.
936 /// The parameters for the query can be customized on the builder returned
937 /// from [`AsyncConnection::view()`](super::AsyncConnection::view).
938 #[must_use]
939 async fn query_with_docs<V: schema::SerializedView, Key>(
940 &self,
941 key: Option<QueryKey<'_, V::Key, Key>>,
942 order: Sort,
943 limit: Option<u32>,
944 access_policy: AccessPolicy,
945 ) -> Result<MappedDocuments<OwnedDocument, V>, Error>
946 where
947 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
948 V::Key: Borrow<Key> + PartialEq<Key>,
949 {
950 // Query permission is checked by the query call
951 let results = self
952 .query::<V, Key>(key, order, limit, access_policy)
953 .await?;
954
955 // Verify that there is permission to fetch each document
956 let documents = self
957 .get_multiple::<V::Collection, _, _, _>(results.iter().map(|m| &m.source.id))
958 .await?
959 .into_iter()
960 .map(|doc| {
961 let id = doc.header.id.deserialize()?;
962 Ok((id, doc))
963 })
964 .collect::<Result<BTreeMap<_, _>, Error>>()?;
965
966 Ok(MappedDocuments {
967 mappings: results,
968 documents,
969 })
970 }
971
972 /// Queries for view entries matching [`View`](schema::View) with their source documents,
973 /// deserialized.
974 ///
975 /// This is the lower-level API. For better ergonomics, consider querying
976 /// the view using
977 /// [`View::entries(self).query_with_collection_docs()`](super::AsyncView::query_with_collection_docs)
978 /// instead. The parameters for the query can be customized on the builder
979 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
980 #[must_use]
981 async fn query_with_collection_docs<V, Key>(
982 &self,
983 key: Option<QueryKey<'_, V::Key, Key>>,
984 order: Sort,
985 limit: Option<u32>,
986 access_policy: AccessPolicy,
987 ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
988 where
989 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
990 V::Key: Borrow<Key> + PartialEq<Key>,
991 V: schema::SerializedView,
992 V::Collection: SerializedCollection,
993 <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
994 {
995 let mapped_docs = self
996 .query_with_docs::<V, Key>(key, order, limit, access_policy)
997 .await?;
998 let mut collection_docs = BTreeMap::new();
999 for (id, doc) in mapped_docs.documents {
1000 collection_docs.insert(id, CollectionDocument::<V::Collection>::try_from(&doc)?);
1001 }
1002 Ok(MappedDocuments {
1003 mappings: mapped_docs.mappings,
1004 documents: collection_docs,
1005 })
1006 }
1007
1008 /// Reduces the view entries matching [`View`](schema::View).
1009 ///
1010 /// This is the lower-level API. For better ergonomics, consider querying
1011 /// the view using
1012 /// [`View::entries(self).reduce()`](super::AsyncView::reduce)
1013 /// instead. The parameters for the query can be customized on the builder
1014 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1015 #[must_use]
1016 async fn reduce<V: schema::SerializedView, Key>(
1017 &self,
1018 key: Option<QueryKey<'_, V::Key, Key>>,
1019 access_policy: AccessPolicy,
1020 ) -> Result<V::Value, Error>
1021 where
1022 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
1023 V::Key: Borrow<Key> + PartialEq<Key>,
1024 {
1025 let view = self.schematic().view::<V>()?;
1026 self.reduce_by_name(
1027 &view.view_name(),
1028 key.map(|key| key.serialized()).transpose()?,
1029 access_policy,
1030 )
1031 .await
1032 .and_then(|value| V::deserialize(&value))
1033 }
1034
1035 /// Reduces the view entries matching [`View`](schema::View), reducing the values by each
1036 /// unique key.
1037 ///
1038 /// This is the lower-level API. For better ergonomics, consider querying
1039 /// the view using
1040 /// [`View::entries(self).reduce_grouped()`](super::AsyncView::reduce_grouped)
1041 /// instead. The parameters for the query can be customized on the builder
1042 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1043 #[must_use]
1044 async fn reduce_grouped<V: schema::SerializedView, Key>(
1045 &self,
1046 key: Option<QueryKey<'_, V::Key, Key>>,
1047 access_policy: AccessPolicy,
1048 ) -> Result<GroupedReductions<V>, Error>
1049 where
1050 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
1051 V::Key: Borrow<Key> + PartialEq<Key>,
1052 {
1053 let view = self.schematic().view::<V>()?;
1054 self.reduce_grouped_by_name(
1055 &view.view_name(),
1056 key.map(|key| key.serialized()).transpose()?,
1057 access_policy,
1058 )
1059 .await?
1060 .into_iter()
1061 .map(|map| {
1062 Ok(MappedValue::new(
1063 V::Key::from_ord_bytes(ByteSource::Borrowed(&map.key))
1064 .map_err(view::Error::key_serialization)?,
1065 V::deserialize(&map.value)?,
1066 ))
1067 })
1068 .collect::<Result<Vec<_>, Error>>()
1069 }
1070
1071 /// Deletes all of the documents associated with this view.
1072 ///
1073 /// This is the lower-level API. For better ergonomics, consider querying
1074 /// the view using
1075 /// [`View::entries(self).delete_docs()`](super::AsyncView::delete_docs)
1076 /// instead. The parameters for the query can be customized on the builder
1077 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1078 #[must_use]
1079 async fn delete_docs<V: schema::SerializedView, Key>(
1080 &self,
1081 key: Option<QueryKey<'_, V::Key, Key>>,
1082 access_policy: AccessPolicy,
1083 ) -> Result<u64, Error>
1084 where
1085 Key: KeyEncoding<V::Key> + PartialEq + ?Sized,
1086 V::Key: Borrow<Key> + PartialEq<Key>,
1087 {
1088 let view = self.schematic().view::<V>()?;
1089 self.delete_docs_by_name(
1090 &view.view_name(),
1091 key.map(|key| key.serialized()).transpose()?,
1092 access_policy,
1093 )
1094 .await
1095 }
1096
1097 /// Applies a [`Transaction`] to the [`Schema`](schema::Schema). If any
1098 /// operation in the [`Transaction`] fails, none of the operations will be
1099 /// applied to the [`Schema`](schema::Schema).
1100 async fn apply_transaction(
1101 &self,
1102 transaction: Transaction,
1103 ) -> Result<Vec<OperationResult>, Error>;
1104
1105 /// Retrieves the document with `id` stored within the named `collection`.
1106 ///
1107 /// This is a lower-level API. For better ergonomics, consider using one of:
1108 ///
1109 /// - [`SerializedCollection::get_async()`]
1110 /// - [`self.collection::<Collection>().get()`](super::AsyncCollection::get)
1111 async fn get_from_collection(
1112 &self,
1113 id: DocumentId,
1114 collection: &CollectionName,
1115 ) -> Result<Option<OwnedDocument>, Error>;
1116
1117 /// Retrieves all documents matching `ids` from the named `collection`.
1118 /// Documents that are not found are not returned, but no error will be
1119 /// generated.
1120 ///
1121 /// This is a lower-level API. For better ergonomics, consider using one of:
1122 ///
1123 /// - [`SerializedCollection::get_multiple_async()`]
1124 /// - [`self.collection::<Collection>().get_multiple()`](super::AsyncCollection::get_multiple)
1125 async fn get_multiple_from_collection(
1126 &self,
1127 ids: &[DocumentId],
1128 collection: &CollectionName,
1129 ) -> Result<Vec<OwnedDocument>, Error>;
1130
1131 /// Retrieves all documents within the range of `ids` from the named
1132 /// `collection`. To retrieve all documents, pass in `..` for `ids`.
1133 ///
1134 /// This is a lower-level API. For better ergonomics, consider using one of:
1135 ///
1136 /// - [`SerializedCollection::all().headers()`](schema::List::headers)
1137 /// - [`self.collection::<Collection>().all().headers()`](super::List::headers)
1138 /// - [`SerializedCollection::list().headers()`](schema::List::headers)
1139 /// - [`self.collection::<Collection>().list().headers()`](super::List::headers)
1140 async fn list_from_collection(
1141 &self,
1142 ids: Range<DocumentId>,
1143 order: Sort,
1144 limit: Option<u32>,
1145 collection: &CollectionName,
1146 ) -> Result<Vec<OwnedDocument>, Error>;
1147
1148 /// Retrieves all headers within the range of `ids` from the named
1149 /// `collection`. To retrieve all documents, pass in `..` for `ids`.
1150 ///
1151 /// This is a lower-level API. For better ergonomics, consider using one of:
1152 ///
1153 /// - [`SerializedCollection::all().headers()`](schema::AsyncList::headers)
1154 /// - [`self.collection::<Collection>().all().headers()`](super::AsyncList::headers)
1155 /// - [`SerializedCollection::list().headers()`](schema::AsyncList::headers)
1156 /// - [`self.collection::<Collection>().list().headers()`](super::AsyncList::headers)
1157 async fn list_headers_from_collection(
1158 &self,
1159 ids: Range<DocumentId>,
1160 order: Sort,
1161 limit: Option<u32>,
1162 collection: &CollectionName,
1163 ) -> Result<Vec<Header>, Error>;
1164
1165 /// Counts the number of documents within the range of `ids` from the named
1166 /// `collection`.
1167 ///
1168 /// This is a lower-level API. For better ergonomics, consider using one of:
1169 ///
1170 /// - [`SerializedCollection::all_async().count()`](schema::AsyncList::count)
1171 /// - [`self.collection::<Collection>().all().count()`](super::AsyncList::count)
1172 /// - [`SerializedCollection::list_async().count()`](schema::AsyncList::count)
1173 /// - [`self.collection::<Collection>().list().count()`](super::AsyncList::count)
1174 async fn count_from_collection(
1175 &self,
1176 ids: Range<DocumentId>,
1177 collection: &CollectionName,
1178 ) -> Result<u64, Error>;
1179
1180 /// Compacts the collection to reclaim unused disk space.
1181 ///
1182 /// This process is done by writing data to a new file and swapping the file
1183 /// once the process completes. This ensures that if a hardware failure,
1184 /// power outage, or crash occurs that the original collection data is left
1185 /// untouched.
1186 ///
1187 /// ## Errors
1188 ///
1189 /// * [`Error::CollectionNotFound`]: database `name` does not exist.
1190 /// * [`Error::Other`]: an error occurred while compacting the database.
1191 async fn compact_collection_by_name(&self, collection: CollectionName) -> Result<(), Error>;
1192
1193 /// Queries for view entries from the named `view`.
1194 ///
1195 /// This is the lower-level API. For better ergonomics, consider querying
1196 /// the view using [`View::entries(self).query()`](super::AsyncView::query)
1197 /// instead. The parameters for the query can be customized on the builder
1198 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1199 async fn query_by_name(
1200 &self,
1201 view: &ViewName,
1202 key: Option<SerializedQueryKey>,
1203 order: Sort,
1204 limit: Option<u32>,
1205 access_policy: AccessPolicy,
1206 ) -> Result<Vec<schema::view::map::Serialized>, Error>;
1207
1208 /// Queries for view entries from the named `view` with their source
1209 /// documents.
1210 ///
1211 /// This is the lower-level API. For better ergonomics, consider querying
1212 /// the view using [`View::entries(self).query_with_docs()`](super::AsyncView::query_with_docs) instead.
1213 /// The parameters for the query can be customized on the builder returned
1214 /// from [`AsyncConnection::view()`](super::AsyncConnection::view).
1215 async fn query_by_name_with_docs(
1216 &self,
1217 view: &ViewName,
1218 key: Option<SerializedQueryKey>,
1219 order: Sort,
1220 limit: Option<u32>,
1221 access_policy: AccessPolicy,
1222 ) -> Result<schema::view::map::MappedSerializedDocuments, Error>;
1223
1224 /// Reduces the view entries from the named `view`.
1225 ///
1226 /// This is the lower-level API. For better ergonomics, consider querying
1227 /// the view using
1228 /// [`View::entries(self).reduce()`](super::AsyncView::reduce)
1229 /// instead. The parameters for the query can be customized on the builder
1230 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1231 async fn reduce_by_name(
1232 &self,
1233 view: &ViewName,
1234 key: Option<SerializedQueryKey>,
1235 access_policy: AccessPolicy,
1236 ) -> Result<Vec<u8>, Error>;
1237
1238 /// Reduces the view entries from the named `view`, reducing the values by each
1239 /// unique key.
1240 ///
1241 /// This is the lower-level API. For better ergonomics, consider querying
1242 /// the view using
1243 /// [`View::entries(self).reduce_grouped()`](super::AsyncView::reduce_grouped)
1244 /// instead. The parameters for the query can be customized on the builder
1245 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1246 async fn reduce_grouped_by_name(
1247 &self,
1248 view: &ViewName,
1249 key: Option<SerializedQueryKey>,
1250 access_policy: AccessPolicy,
1251 ) -> Result<Vec<MappedSerializedValue>, Error>;
1252
1253 /// Deletes all source documents for entries that match within the named
1254 /// `view`.
1255 ///
1256 /// This is the lower-level API. For better ergonomics, consider querying
1257 /// the view using
1258 /// [`View::entries(self).delete_docs()`](super::AsyncView::delete_docs)
1259 /// instead. The parameters for the query can be customized on the builder
1260 /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
1261 async fn delete_docs_by_name(
1262 &self,
1263 view: &ViewName,
1264 key: Option<SerializedQueryKey>,
1265 access_policy: AccessPolicy,
1266 ) -> Result<u64, Error>;
1267}
1268
1269/// Access to a connection's schema.
1270pub trait HasSchema {
1271 /// Returns the schema for the database.
1272 fn schematic(&self) -> &Schematic;
1273}