1
use std::collections::BTreeMap;
2

            
3
use arc_bytes::serde::Bytes;
4
use async_trait::async_trait;
5

            
6
use super::GroupedReductions;
7
use crate::{
8
    connection::{AccessPolicy, HasSession, QueryKey, Range, Sort, ViewMappings},
9
    document::{
10
        CollectionDocument, CollectionHeader, Document, DocumentId, HasHeader, Header,
11
        OwnedDocument,
12
    },
13
    key::{self, Key, KeyEncoding},
14
    schema::{
15
        self,
16
        view::{
17
            self,
18
            map::{MappedDocuments, MappedSerializedValue},
19
        },
20
        CollectionName, Map, MappedValue, Schematic, SerializedCollection, ViewName,
21
    },
22
    transaction::{OperationResult, Transaction},
23
    Error,
24
};
25

            
26
/// The low-level interface to a database's [`schema::Schema`], giving access to
27
/// [`Collection`s](crate::schema::Collection) and
28
/// [`Views`s](crate::schema::View). This trait is not safe to use within async
29
/// contexts and will block the current thread. For async access, use
30
/// [`AsyncLowLevelConnection`].
31
///
32
/// This trait's methods are not designed for ergonomics. See
33
/// [`Connection`](super::Connection) for a higher-level interface.
34
pub trait LowLevelConnection: HasSession {
35
    /// Returns the schema for the database.
36
    fn schematic(&self) -> &Schematic;
37

            
38
    /// Inserts a newly created document into the connected [`schema::Schema`]
39
    /// for the [`Collection`](schema::Collection) `C`. If `id` is `None` a unique id will be
40
    /// generated. If an id is provided and a document already exists with that
41
    /// id, a conflict error will be returned.
42
    ///
43
    /// This is the lower-level API. For better ergonomics, consider using
44
    /// one of:
45
    ///
46
    /// - [`SerializedCollection::push_into()`]
47
    /// - [`SerializedCollection::insert_into()`]
48
    /// - [`self.collection::<Collection>().insert()`](super::Collection::insert)
49
    /// - [`self.collection::<Collection>().push()`](super::Collection::push)
50
29607
    fn insert<C: schema::Collection, PrimaryKey: Send, B: Into<Bytes> + Send>(
51
29607
        &self,
52
29607
        id: Option<PrimaryKey>,
53
29607
        contents: B,
54
29607
    ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
55
29607
    where
56
29607
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
57
29607
    {
58
29607
        let contents = contents.into();
59
29561
        let results = self.apply_transaction(Transaction::insert(
60
29607
            C::collection_name(),
61
29607
            id.map(|id| DocumentId::new(id)).transpose()?,
62
29607
            contents,
63
46
        ))?;
64
29561
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
65
29561
            CollectionHeader::try_from(header)
66
        } else {
67
            unreachable!(
68
                "apply_transaction on a single insert should yield a single DocumentUpdated entry"
69
            )
70
        }
71
29607
    }
72

            
73
    /// Updates an existing document in the connected [`schema::Schema`] for the
74
    /// [`Collection`](schema::Collection) `C`. Upon success, `doc.revision` will be updated with
75
    /// the new revision.
76
    ///
77
    /// This is the lower-level API. For better ergonomics, consider using
78
    /// one of:
79
    ///
80
    /// - [`CollectionDocument::update()`]
81
    /// - [`self.collection::<Collection>().update()`](super::Collection::update)
82
76
    fn update<C: schema::Collection, D: Document<C> + Send + Sync>(
83
76
        &self,
84
76
        doc: &mut D,
85
76
    ) -> Result<(), Error> {
86
64
        let results = self.apply_transaction(Transaction::update(
87
76
            C::collection_name(),
88
76
            doc.header().into_header()?,
89
76
            doc.bytes()?,
90
12
        ))?;
91
64
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
92
64
            doc.set_header(header)?;
93
64
            Ok(())
94
        } else {
95
            unreachable!(
96
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
97
            )
98
        }
99
76
    }
100

            
101
    /// Overwrites an existing document, or inserts a new document. Upon success,
102
    /// `doc.revision` will be updated with the new revision information.
103
    ///
104
    /// This is the lower-level API. For better ergonomics, consider using
105
    /// one of:
106
    ///
107
    /// - [`SerializedCollection::overwrite()`]
108
    /// - [`SerializedCollection::overwrite_into()`]
109
    /// - [`self.collection::<Collection>().overwrite()`](super::Collection::overwrite)
110
6
    fn overwrite<C, PrimaryKey>(
111
6
        &self,
112
6
        id: PrimaryKey,
113
6
        contents: Vec<u8>,
114
6
    ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
115
6
    where
116
6
        C: schema::Collection,
117
6
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
118
6
    {
119
6
        let results = self.apply_transaction(Transaction::overwrite(
120
6
            C::collection_name(),
121
6
            DocumentId::new(id)?,
122
6
            contents,
123
        ))?;
124
6
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
125
6
            CollectionHeader::try_from(header)
126
        } else {
127
            unreachable!(
128
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
129
            )
130
        }
131
6
    }
132

            
133
    /// Retrieves a stored document from [`Collection`](schema::Collection) `C` identified by `id`.
134
    ///
135
    /// This is a lower-level API. For better ergonomics, consider using one of:
136
    ///
137
    /// - [`SerializedCollection::get()`]
138
    /// - [`self.collection::<Collection>().get()`](super::Collection::get)
139
    fn get<C, PrimaryKey>(&self, id: PrimaryKey) -> Result<Option<OwnedDocument>, Error>
140
    where
141
        C: schema::Collection,
142
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
143
    {
144
1097
        self.get_from_collection(DocumentId::new(id)?, &C::collection_name())
145
1097
    }
146

            
147
    /// Retrieves all documents matching `ids`. Documents that are not found are
148
    /// not returned, but no error will be generated.
149
    ///
150
    /// This is a lower-level API. For better ergonomics, consider using one of:
151
    ///
152
    /// - [`SerializedCollection::get_multiple()`]
153
    /// - [`self.collection::<Collection>().get_multiple()`](super::Collection::get_multiple)
154
746
    fn get_multiple<C, PrimaryKey, DocumentIds, I>(
155
746
        &self,
156
746
        ids: DocumentIds,
157
746
    ) -> Result<Vec<OwnedDocument>, Error>
158
746
    where
159
746
        C: schema::Collection,
160
746
        DocumentIds: IntoIterator<Item = PrimaryKey, IntoIter = I> + Send + Sync,
161
746
        I: Iterator<Item = PrimaryKey> + Send + Sync,
162
746
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
163
746
    {
164
746
        let ids = ids
165
746
            .into_iter()
166
762
            .map(|id| DocumentId::new(id))
167
746
            .collect::<Result<Vec<_>, _>>()?;
168
746
        self.get_multiple_from_collection(&ids, &C::collection_name())
169
746
    }
170

            
171
    /// Retrieves all documents within the range of `ids`. To retrieve all
172
    /// documents, pass in `..` for `ids`.
173
    ///
174
    /// This is a lower-level API. For better ergonomics, consider using one of:
175
    ///
176
    /// - [`SerializedCollection::all()`]
177
    /// - [`self.collection::<Collection>().all()`](super::Collection::all)
178
    /// - [`SerializedCollection::list()`]
179
    /// - [`self.collection::<Collection>().list()`](super::Collection::list)
180
24
    fn list<C, R, PrimaryKey>(
181
24
        &self,
182
24
        ids: R,
183
24
        order: Sort,
184
24
        limit: Option<u32>,
185
24
    ) -> Result<Vec<OwnedDocument>, Error>
186
24
    where
187
24
        C: schema::Collection,
188
24
        R: Into<Range<PrimaryKey>> + Send,
189
24
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
190
24
    {
191
26
        let ids = ids.into().map_result(|id| DocumentId::new(id))?;
192
24
        self.list_from_collection(ids, order, limit, &C::collection_name())
193
24
    }
194

            
195
    /// Retrieves all documents within the range of `ids`. To retrieve all
196
    /// documents, pass in `..` for `ids`.
197
    ///
198
    /// This is the lower-level API. For better ergonomics, consider using one
199
    /// of:
200
    ///
201
    /// - [`SerializedCollection::all_async().headers()`](schema::List::headers)
202
    /// - [`self.collection::<Collection>().all().headers()`](super::List::headers)
203
    /// - [`SerializedCollection::list_async().headers()`](schema::List::headers)
204
    /// - [`self.collection::<Collection>().list().headers()`](super::List::headers)
205
3
    fn list_headers<C, R, PrimaryKey>(
206
3
        &self,
207
3
        ids: R,
208
3
        order: Sort,
209
3
        limit: Option<u32>,
210
3
    ) -> Result<Vec<Header>, Error>
211
3
    where
212
3
        C: schema::Collection,
213
3
        R: Into<Range<PrimaryKey>> + Send,
214
3
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
215
3
    {
216
6
        let ids = ids.into().map_result(|id| DocumentId::new(id))?;
217
3
        self.list_headers_from_collection(ids, order, limit, &C::collection_name())
218
3
    }
219

            
220
    /// Counts the number of documents within the range of `ids`.
221
    ///
222
    /// This is a lower-level API. For better ergonomics, consider using one of:
223
    ///
224
    /// - [`SerializedCollection::all().count()`](schema::List::count)
225
    /// - [`self.collection::<Collection>().all().count()`](super::List::count)
226
    /// - [`SerializedCollection::list().count()`](schema::List::count)
227
    /// - [`self.collection::<Collection>().list().count()`](super::List::count)
228
    fn count<C, R, PrimaryKey>(&self, ids: R) -> Result<u64, Error>
229
    where
230
        C: schema::Collection,
231
        R: Into<Range<PrimaryKey>> + Send,
232
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
233
    {
234
        self.count_from_collection(
235
6
            ids.into().map_result(DocumentId::new)?,
236
6
            &C::collection_name(),
237
        )
238
6
    }
239

            
240
    /// Removes a `Document` from the database.
241
    ///
242
    /// This is a lower-level API. For better ergonomics, consider using
243
    /// one of:
244
    ///
245
    /// - [`CollectionDocument::delete()`]
246
    /// - [`self.collection::<Collection>().delete()`](super::Collection::delete)
247
15138
    fn delete<C: schema::Collection, H: HasHeader + Send + Sync>(
248
15138
        &self,
249
15138
        doc: &H,
250
15138
    ) -> Result<(), Error> {
251
15138
        let results =
252
15138
            self.apply_transaction(Transaction::delete(C::collection_name(), doc.header()?))?;
253
15138
        if let OperationResult::DocumentDeleted { .. } = &results[0] {
254
15138
            Ok(())
255
        } else {
256
            unreachable!(
257
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
258
            )
259
        }
260
15138
    }
261

            
262
    /// Queries for view entries matching [`View`](schema::View).
263
    ///
264
    /// This is a lower-level API. For better ergonomics, consider querying the
265
    /// view using [`self.view::<View>().query()`](super::View::query) instead. The
266
    /// parameters for the query can be customized on the builder returned from
267
    /// [`Connection::view()`](super::Connection::view).
268
19357
    fn query<V: schema::SerializedView, Key>(
269
19357
        &self,
270
19357
        key: Option<QueryKey<Key>>,
271
19357
        order: Sort,
272
19357
        limit: Option<u32>,
273
19357
        access_policy: AccessPolicy,
274
19357
    ) -> Result<ViewMappings<V>, Error>
275
19357
    where
276
19357
        Key: for<'k> KeyEncoding<'k, V::Key>,
277
19357
    {
278
19357
        let view = self.schematic().view::<V>()?;
279
19357
        let mappings = self.query_by_name(
280
19357
            &view.view_name(),
281
19357
            key.map(|key| key.serialized()).transpose()?,
282
19357
            order,
283
19357
            limit,
284
19357
            access_policy,
285
        )?;
286
19357
        mappings
287
19357
            .into_iter()
288
19357
            .map(|mapping| {
289
16880
                Ok(Map {
290
16880
                    key: <V::Key as key::Key>::from_ord_bytes(&mapping.key)
291
16880
                        .map_err(view::Error::key_serialization)
292
16880
                        .map_err(Error::from)?,
293
16880
                    value: V::deserialize(&mapping.value)?,
294
16880
                    source: mapping.source,
295
                })
296
19357
            })
297
19357
            .collect::<Result<Vec<_>, Error>>()
298
19357
    }
299

            
300
    /// Queries for view entries matching [`View`](schema::View) with their source documents.
301
    ///
302
    /// This is a lower-level API. For better ergonomics, consider querying
303
    /// the view using [`self.view::<View>().query_with_docs()`](super::View::query_with_docs) instead.
304
    /// The parameters for the query can be customized on the builder returned
305
    /// from [`Connection::view()`](super::Connection::view).
306
530
    fn query_with_docs<V: schema::SerializedView, Key>(
307
530
        &self,
308
530
        key: Option<QueryKey<Key>>,
309
530
        order: Sort,
310
530
        limit: Option<u32>,
311
530
        access_policy: AccessPolicy,
312
530
    ) -> Result<MappedDocuments<OwnedDocument, V>, Error>
313
530
    where
314
530
        Key: for<'k> KeyEncoding<'k, V::Key>,
315
530
    {
316
        // Query permission is checked by the query call
317
530
        let results = self.query::<V, Key>(key, order, limit, access_policy)?;
318

            
319
        // Verify that there is permission to fetch each document
320
530
        let mut ids = Vec::with_capacity(results.len());
321
545
        ids.extend(results.iter().map(|m| m.source.id));
322

            
323
530
        let documents = self
324
530
            .get_multiple::<V::Collection, _, _, _>(ids)?
325
530
            .into_iter()
326
545
            .map(|doc| (doc.header.id, doc))
327
530
            .collect::<BTreeMap<_, _>>();
328
530

            
329
530
        Ok(MappedDocuments {
330
530
            mappings: results,
331
530
            documents,
332
530
        })
333
530
    }
334

            
335
    /// Queries for view entries matching [`View`](schema::View) with their source documents, deserialized.
336
    ///
337
    /// This is a lower-level API. For better ergonomics, consider querying
338
    /// the view using [`self.view::<View>().query_with_collection_docs()`](super::View::query_with_collection_docs) instead.
339
    /// The parameters for the query can be customized on the builder returned
340
    /// from [`Connection::view()`](super::Connection::view).
341
25
    fn query_with_collection_docs<V, Key>(
342
25
        &self,
343
25
        key: Option<QueryKey<Key>>,
344
25
        order: Sort,
345
25
        limit: Option<u32>,
346
25
        access_policy: AccessPolicy,
347
25
    ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
348
25
    where
349
25
        Key: for<'k> KeyEncoding<'k, V::Key>,
350
25
        V: schema::SerializedView,
351
25
        V::Collection: SerializedCollection,
352
25
        <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
353
25
    {
354
25
        let mapped_docs = self.query_with_docs::<V, Key>(key, order, limit, access_policy)?;
355
25
        let mut collection_docs = BTreeMap::new();
356
66
        for (id, doc) in mapped_docs.documents {
357
41
            collection_docs.insert(id, CollectionDocument::<V::Collection>::try_from(&doc)?);
358
        }
359
25
        Ok(MappedDocuments {
360
25
            mappings: mapped_docs.mappings,
361
25
            documents: collection_docs,
362
25
        })
363
25
    }
364

            
365
    /// Reduces the view entries matching [`View`](schema::View).
366
    ///
367
    /// This is a lower-level API. For better ergonomics, consider reducing
368
    /// the view using [`self.view::<View>().reduce()`](super::View::reduce) instead.
369
    /// The parameters for the query can be customized on the builder returned
370
    /// from [`Connection::view()`](super::Connection::view).
371
22
    fn reduce<V: schema::SerializedView, Key>(
372
22
        &self,
373
22
        key: Option<QueryKey<Key>>,
374
22
        access_policy: AccessPolicy,
375
22
    ) -> Result<V::Value, Error>
376
22
    where
377
22
        Key: for<'k> KeyEncoding<'k, V::Key>,
378
22
    {
379
22
        let view = self.schematic().view::<V>()?;
380
        self.reduce_by_name(
381
22
            &view.view_name(),
382
22
            key.map(|key| key.serialized()).transpose()?,
383
22
            access_policy,
384
22
        )
385
22
        .and_then(|value| V::deserialize(&value))
386
22
    }
387

            
388
    /// Reduces the view entries matching [`View`](schema::View), reducing the values by each
389
    /// unique key.
390
    ///
391
    /// This is a lower-level API. For better ergonomics, consider reducing
392
    /// the view using
393
    /// [`self.view::<View>().reduce_grouped()`](super::View::reduce_grouped) instead.
394
    /// The parameters for the query can be customized on the builder returned
395
    /// from [`Connection::view()`](super::Connection::view).
396
18
    fn reduce_grouped<V: schema::SerializedView, Key>(
397
18
        &self,
398
18
        key: Option<QueryKey<Key>>,
399
18
        access_policy: AccessPolicy,
400
18
    ) -> Result<GroupedReductions<V>, Error>
401
18
    where
402
18
        Key: for<'k> KeyEncoding<'k, V::Key>,
403
18
    {
404
18
        let view = self.schematic().view::<V>()?;
405
        self.reduce_grouped_by_name(
406
18
            &view.view_name(),
407
18
            key.map(|key| key.serialized()).transpose()?,
408
18
            access_policy,
409
        )?
410
18
        .into_iter()
411
45
        .map(|map| {
412
45
            Ok(MappedValue::new(
413
45
                V::Key::from_ord_bytes(&map.key).map_err(view::Error::key_serialization)?,
414
45
                V::deserialize(&map.value)?,
415
            ))
416
45
        })
417
18
        .collect::<Result<Vec<_>, Error>>()
418
18
    }
419

            
420
    /// Deletes all of the documents associated with this view.
421
    ///
422
    /// This is a lower-level API. For better ergonomics, consider querying the
423
    /// view using
424
    /// [`self.view::<View>().delete_docs()`](super::View::delete_docs())
425
    /// instead. The parameters for the query can be customized on the builder
426
    /// returned from [`Connection::view()`](super::Connection::view).
427
6
    fn delete_docs<V: schema::SerializedView, Key>(
428
6
        &self,
429
6
        key: Option<QueryKey<Key>>,
430
6
        access_policy: AccessPolicy,
431
6
    ) -> Result<u64, Error>
432
6
    where
433
6
        Key: for<'k> KeyEncoding<'k, V::Key>,
434
6
    {
435
6
        let view = self.schematic().view::<V>()?;
436
        self.delete_docs_by_name(
437
6
            &view.view_name(),
438
6
            key.map(|key| key.serialized()).transpose()?,
439
6
            access_policy,
440
        )
441
6
    }
442

            
443
    /// Applies a [`Transaction`] to the [`schema::Schema`]. If any operation in the
444
    /// [`Transaction`] fails, none of the operations will be applied to the
445
    /// [`schema::Schema`].
446
    fn apply_transaction(&self, transaction: Transaction) -> Result<Vec<OperationResult>, Error>;
447

            
448
    /// Retrieves the document with `id` stored within the named `collection`.
449
    ///
450
    /// This is a lower-level API. For better ergonomics, consider using
451
    /// one of:
452
    ///
453
    /// - [`SerializedCollection::get()`]
454
    /// - [`self.collection::<Collection>().get()`](super::Collection::get)
455
    fn get_from_collection(
456
        &self,
457
        id: DocumentId,
458
        collection: &CollectionName,
459
    ) -> Result<Option<OwnedDocument>, Error>;
460

            
461
    /// Retrieves all documents matching `ids` from the named `collection`.
462
    /// Documents that are not found are not returned, but no error will be
463
    /// generated.
464
    ///
465
    /// This is a lower-level API. For better ergonomics, consider using one of:
466
    ///
467
    /// - [`SerializedCollection::get_multiple()`]
468
    /// - [`self.collection::<Collection>().get_multiple()`](super::Collection::get_multiple)
469
    fn get_multiple_from_collection(
470
        &self,
471
        ids: &[DocumentId],
472
        collection: &CollectionName,
473
    ) -> Result<Vec<OwnedDocument>, Error>;
474

            
475
    /// Retrieves all documents within the range of `ids` from the named
476
    /// `collection`. To retrieve all documents, pass in `..` for `ids`.
477
    ///
478
    /// This is a lower-level API. For better ergonomics, consider using one of:
479
    ///
480
    /// - [`SerializedCollection::all()`]
481
    /// - [`self.collection::<Collection>().all()`](super::Collection::all)
482
    /// - [`SerializedCollection::list()`]
483
    /// - [`self.collection::<Collection>().list()`](super::Collection::list)
484
    fn list_from_collection(
485
        &self,
486
        ids: Range<DocumentId>,
487
        order: Sort,
488
        limit: Option<u32>,
489
        collection: &CollectionName,
490
    ) -> Result<Vec<OwnedDocument>, Error>;
491

            
492
    /// Retrieves all headers within the range of `ids` from the named
493
    /// `collection`. To retrieve all documents, pass in `..` for `ids`.
494
    ///
495
    /// This is a lower-level API. For better ergonomics, consider using one of:
496
    ///
497
    /// - [`SerializedCollection::all().headers()`](schema::List::headers)
498
    /// - [`self.collection::<Collection>().all().headers()`](super::AsyncCollection::all)
499
    /// - [`SerializedCollection::list().headers()`](schema::List::headers)
500
    /// - [`self.collection::<Collection>().list()`](super::AsyncCollection::list)
501
    fn list_headers_from_collection(
502
        &self,
503
        ids: Range<DocumentId>,
504
        order: Sort,
505
        limit: Option<u32>,
506
        collection: &CollectionName,
507
    ) -> Result<Vec<Header>, Error>;
508

            
509
    /// Counts the number of documents within the range of `ids` from the named
510
    /// `collection`.
511
    ///
512
    /// This is a lower-level API. For better ergonomics, consider using one of:
513
    ///
514
    /// - [`SerializedCollection::all().count()`](schema::List::count)
515
    /// - [`self.collection::<Collection>().all().count()`](super::List::count)
516
    /// - [`SerializedCollection::list().count()`](schema::List::count)
517
    /// - [`self.collection::<Collection>().list().count()`](super::List::count)
518
    fn count_from_collection(
519
        &self,
520
        ids: Range<DocumentId>,
521
        collection: &CollectionName,
522
    ) -> Result<u64, Error>;
523

            
524
    /// Compacts the collection to reclaim unused disk space.
525
    ///
526
    /// This process is done by writing data to a new file and swapping the file
527
    /// once the process completes. This ensures that if a hardware failure,
528
    /// power outage, or crash occurs that the original collection data is left
529
    /// untouched.
530
    ///
531
    /// ## Errors
532
    ///
533
    /// * [`Error::CollectionNotFound`]: database `name` does not exist.
534
    /// * [`Error::Io`]: an error occurred while compacting the database.
535
    fn compact_collection_by_name(&self, collection: CollectionName) -> Result<(), Error>;
536

            
537
    /// Queries for view entries from the named `view`.
538
    ///
539
    /// This is a lower-level API. For better ergonomics, consider querying the
540
    /// view using [`self.view::<View>().query()`](super::View::query) instead. The
541
    /// parameters for the query can be customized on the builder returned from
542
    /// [`Connection::view()`](super::Connection::view).
543
    fn query_by_name(
544
        &self,
545
        view: &ViewName,
546
        key: Option<QueryKey<Bytes>>,
547
        order: Sort,
548
        limit: Option<u32>,
549
        access_policy: AccessPolicy,
550
    ) -> Result<Vec<schema::view::map::Serialized>, Error>;
551

            
552
    /// Queries for view entries from the named `view` with their source
553
    /// documents.
554
    ///
555
    /// This is a lower-level API. For better ergonomics, consider querying the
556
    /// view using
557
    /// [`self.view::<View>().query_with_docs()`](super::View::query_with_docs)
558
    /// instead. The parameters for the query can be customized on the builder
559
    /// returned from [`Connection::view()`](super::Connection::view).
560
    fn query_by_name_with_docs(
561
        &self,
562
        view: &ViewName,
563
        key: Option<QueryKey<Bytes>>,
564
        order: Sort,
565
        limit: Option<u32>,
566
        access_policy: AccessPolicy,
567
    ) -> Result<schema::view::map::MappedSerializedDocuments, Error>;
568

            
569
    /// Reduces the view entries from the named `view`.
570
    ///
571
    /// This is a lower-level API. For better ergonomics, consider reducing the
572
    /// view using [`self.view::<View>().reduce()`](super::View::reduce)
573
    /// instead. The parameters for the query can be customized on the builder
574
    /// returned from [`Connection::view()`](super::Connection::view).
575
    fn reduce_by_name(
576
        &self,
577
        view: &ViewName,
578
        key: Option<QueryKey<Bytes>>,
579
        access_policy: AccessPolicy,
580
    ) -> Result<Vec<u8>, Error>;
581

            
582
    /// Reduces the view entries from the named `view`, reducing the values by each
583
    /// unique key.
584
    ///
585
    /// This is a lower-level API. For better ergonomics, consider reducing
586
    /// the view using
587
    /// [`self.view::<View>().reduce_grouped()`](super::View::reduce_grouped) instead.
588
    /// The parameters for the query can be customized on the builder returned
589
    /// from [`Connection::view()`](super::Connection::view).
590
    fn reduce_grouped_by_name(
591
        &self,
592
        view: &ViewName,
593
        key: Option<QueryKey<Bytes>>,
594
        access_policy: AccessPolicy,
595
    ) -> Result<Vec<MappedSerializedValue>, Error>;
596

            
597
    /// Deletes all source documents for entries that match within the named
598
    /// `view`.
599
    ///
600
    /// This is a lower-level API. For better ergonomics, consider querying the
601
    /// view using
602
    /// [`self.view::<View>().delete_docs()`](super::View::delete_docs())
603
    /// instead. The parameters for the query can be customized on the builder
604
    /// returned from [`Connection::view()`](super::Connection::view).
605
    fn delete_docs_by_name(
606
        &self,
607
        view: &ViewName,
608
        key: Option<QueryKey<Bytes>>,
609
        access_policy: AccessPolicy,
610
    ) -> Result<u64, Error>;
611
}
612

            
613
/// The low-level interface to a database's [`schema::Schema`], giving access to
614
/// [`Collection`s](crate::schema::Collection) and
615
/// [`Views`s](crate::schema::View). This trait is for use within async
616
/// contexts. For access outside of async contexts, use [`LowLevelConnection`].
617
///
618
/// This trait's methods are not designed for ergonomics. See
619
/// [`AsyncConnection`](super::AsyncConnection) for a higher-level interface.
620
#[async_trait]
621
pub trait AsyncLowLevelConnection: HasSession + Send + Sync {
622
    /// Returns the schema for the database.
623
    fn schematic(&self) -> &Schematic;
624

            
625
    /// Inserts a newly created document into the connected [`schema::Schema`]
626
    /// for the [`Collection`](schema::Collection) `C`. If `id` is `None` a unique id will be
627
    /// generated. If an id is provided and a document already exists with that
628
    /// id, a conflict error will be returned.
629
    ///
630
    /// This is the lower-level API. For better ergonomics, consider using
631
    /// one of:
632
    ///
633
    /// - [`SerializedCollection::push_into_async()`]
634
    /// - [`SerializedCollection::insert_into_async()`]
635
    /// - [`self.collection::<Collection>().insert()`](super::AsyncCollection::insert)
636
    /// - [`self.collection::<Collection>().push()`](super::AsyncCollection::push)
637
10588
    async fn insert<C: schema::Collection, PrimaryKey: Send, B: Into<Bytes> + Send>(
638
10588
        &self,
639
10588
        id: Option<PrimaryKey>,
640
10588
        contents: B,
641
10588
    ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
642
10588
    where
643
10588
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
644
10588
    {
645
10588
        let contents = contents.into();
646
10066
        let results = self
647
            .apply_transaction(Transaction::insert(
648
10588
                C::collection_name(),
649
10588
                id.map(|id| DocumentId::new(id)).transpose()?,
650
10588
                contents,
651
16423
            ))
652
16423
            .await?;
653
10066
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
654
10066
            CollectionHeader::try_from(header)
655
        } else {
656
            unreachable!(
657
                "apply_transaction on a single insert should yield a single DocumentUpdated entry"
658
            )
659
        }
660
21176
    }
661

            
662
    /// Updates an existing document in the connected [`schema::Schema`] for the
663
    /// [`Collection`](schema::Collection)(schema::Collection) `C`. Upon success, `doc.revision`
664
    /// will be updated with the new revision.
665
    ///
666
    /// This is the lower-level API. For better ergonomics, consider using one
667
    /// of:
668
    ///
669
    /// - [`CollectionDocument::update_async()`]
670
    /// - [`self.collection::<Collection>().update()`](super::AsyncCollection::update)
671
5584
    async fn update<C: schema::Collection, D: Document<C> + Send + Sync>(
672
5584
        &self,
673
5584
        doc: &mut D,
674
5584
    ) -> Result<(), Error> {
675
5564
        let results = self
676
            .apply_transaction(Transaction::update(
677
5584
                C::collection_name(),
678
5584
                doc.header().into_header()?,
679
5584
                doc.bytes()?,
680
7546
            ))
681
7546
            .await?;
682
5564
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
683
5564
            doc.set_header(header)?;
684
5564
            Ok(())
685
        } else {
686
            unreachable!(
687
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
688
            )
689
        }
690
11168
    }
691

            
692
    /// Overwrites an existing document, or inserts a new document. Upon success,
693
    /// `doc.revision` will be updated with the new revision information.
694
    ///
695
    /// This is the lower-level API. For better ergonomics, consider using
696
    /// one of:
697
    ///
698
    /// - [`SerializedCollection::overwrite_async()`]
699
    /// - [`SerializedCollection::overwrite_into_async()`]
700
    /// - [`self.collection::<Collection>().overwrite()`](super::AsyncCollection::overwrite)
701
511
    async fn overwrite<'a, C, PrimaryKey>(
702
511
        &self,
703
511
        id: PrimaryKey,
704
511
        contents: Vec<u8>,
705
511
    ) -> Result<CollectionHeader<C::PrimaryKey>, Error>
706
511
    where
707
511
        C: schema::Collection,
708
511
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
709
511
    {
710
511
        let results = self
711
            .apply_transaction(Transaction::overwrite(
712
511
                C::collection_name(),
713
511
                DocumentId::new(id)?,
714
511
                contents,
715
1670
            ))
716
1670
            .await?;
717
511
        if let Some(OperationResult::DocumentUpdated { header, .. }) = results.into_iter().next() {
718
511
            CollectionHeader::try_from(header)
719
        } else {
720
            unreachable!(
721
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
722
            )
723
        }
724
1022
    }
725

            
726
    /// Retrieves a stored document from [`Collection`](schema::Collection) `C` identified by `id`.
727
    ///
728
    /// This is the lower-level API. For better ergonomics, consider using
729
    /// one of:
730
    ///
731
    /// - [`SerializedCollection::get_async()`]
732
    /// - [`self.collection::<Collection>().get()`](super::AsyncCollection::get)
733
15795
    async fn get<C, PrimaryKey>(&self, id: PrimaryKey) -> Result<Option<OwnedDocument>, Error>
734
15795
    where
735
15795
        C: schema::Collection,
736
15795
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
737
15795
    {
738
19570
        self.get_from_collection(DocumentId::new(id)?, &C::collection_name())
739
18298
            .await
740
31590
    }
741

            
742
    /// Retrieves all documents matching `ids`. Documents that are not found
743
    /// are not returned, but no error will be generated.
744
    ///
745
    /// This is the lower-level API. For better ergonomics, consider using
746
    /// one of:
747
    ///
748
    /// - [`SerializedCollection::get_multiple_async()`]
749
    /// - [`self.collection::<Collection>().get_multiple()`](super::AsyncCollection::get_multiple)
750
9084
    async fn get_multiple<C, PrimaryKey, DocumentIds, I>(
751
9084
        &self,
752
9084
        ids: DocumentIds,
753
9084
    ) -> Result<Vec<OwnedDocument>, Error>
754
9084
    where
755
9084
        C: schema::Collection,
756
9084
        DocumentIds: IntoIterator<Item = PrimaryKey, IntoIter = I> + Send + Sync,
757
9084
        I: Iterator<Item = PrimaryKey> + Send + Sync,
758
9084
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
759
9084
    {
760
9084
        let ids = ids
761
9084
            .into_iter()
762
9084
            .map(DocumentId::new)
763
9084
            .collect::<Result<Vec<_>, _>>()?;
764
9084
        self.get_multiple_from_collection(&ids, &C::collection_name())
765
8379
            .await
766
18168
    }
767

            
768
    /// Retrieves all documents within the range of `ids`. To retrieve all
769
    /// documents, pass in `..` for `ids`.
770
    ///
771
    /// This is the lower-level API. For better ergonomics, consider using one
772
    /// of:
773
    ///
774
    /// - [`SerializedCollection::all_async()`]
775
    /// - [`self.collection::<Collection>().all()`](super::AsyncCollection::all)
776
    /// - [`SerializedCollection::list_async()`]
777
    /// - [`self.collection::<Collection>().list()`](super::AsyncCollection::list)
778
20
    async fn list<C, R, PrimaryKey>(
779
20
        &self,
780
20
        ids: R,
781
20
        order: Sort,
782
20
        limit: Option<u32>,
783
20
    ) -> Result<Vec<OwnedDocument>, Error>
784
20
    where
785
20
        C: schema::Collection,
786
20
        R: Into<Range<PrimaryKey>> + Send,
787
20
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
788
20
    {
789
20
        let ids = ids.into().map_result(DocumentId::new)?;
790
20
        self.list_from_collection(ids, order, limit, &C::collection_name())
791
20
            .await
792
40
    }
793

            
794
    /// Retrieves all documents within the range of `ids`. To retrieve all
795
    /// documents, pass in `..` for `ids`.
796
    ///
797
    /// This is the lower-level API. For better ergonomics, consider using one
798
    /// of:
799
    ///
800
    /// - [`SerializedCollection::all_async().headers()`](schema::AsyncList::headers)
801
    /// - [`self.collection::<Collection>().all()`](super::AsyncList::headers)
802
    /// - [`SerializedCollection::list_async().headers()`](schema::AsyncList::headers)
803
    /// - [`self.collection::<Collection>().list().headers()`](super::AsyncList::headers)
804
5
    async fn list_headers<C, R, PrimaryKey>(
805
5
        &self,
806
5
        ids: R,
807
5
        order: Sort,
808
5
        limit: Option<u32>,
809
5
    ) -> Result<Vec<Header>, Error>
810
5
    where
811
5
        C: schema::Collection,
812
5
        R: Into<Range<PrimaryKey>> + Send,
813
5
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
814
5
    {
815
5
        let ids = ids.into().map_result(DocumentId::new)?;
816
5
        self.list_headers_from_collection(ids, order, limit, &C::collection_name())
817
5
            .await
818
10
    }
819

            
820
    /// Counts the number of documents within the range of `ids`.
821
    ///
822
    /// This is the lower-level API. For better ergonomics, consider using
823
    /// one of:
824
    ///
825
    /// - [`SerializedCollection::all_async().count()`](schema::AsyncList::count)
826
    /// - [`self.collection::<Collection>().all().count()`](super::AsyncList::count)
827
    /// - [`SerializedCollection::list_async().count()`](schema::AsyncList::count)
828
    /// - [`self.collection::<Collection>().list().count()`](super::AsyncList::count)
829
10
    async fn count<C, R, PrimaryKey>(&self, ids: R) -> Result<u64, Error>
830
10
    where
831
10
        C: schema::Collection,
832
10
        R: Into<Range<PrimaryKey>> + Send,
833
10
        PrimaryKey: for<'k> KeyEncoding<'k, C::PrimaryKey>,
834
10
    {
835
        self.count_from_collection(
836
10
            ids.into().map_result(DocumentId::new)?,
837
10
            &C::collection_name(),
838
10
        )
839
10
        .await
840
20
    }
841

            
842
    /// Removes a `Document` from the database.
843
    ///
844
    /// This is the lower-level API. For better ergonomics, consider using
845
    /// one of:
846
    ///
847
    /// - [`CollectionDocument::delete_async()`]
848
    /// - [`self.collection::<Collection>().delete()`](super::AsyncCollection::delete)
849
963
    async fn delete<C: schema::Collection, H: HasHeader + Send + Sync>(
850
963
        &self,
851
963
        doc: &H,
852
963
    ) -> Result<(), Error> {
853
961
        let results = self
854
2109
            .apply_transaction(Transaction::delete(C::collection_name(), doc.header()?))
855
2066
            .await?;
856
961
        if let OperationResult::DocumentDeleted { .. } = &results[0] {
857
961
            Ok(())
858
        } else {
859
            unreachable!(
860
                "apply_transaction on a single update should yield a single DocumentUpdated entry"
861
            )
862
        }
863
1926
    }
864
    /// Queries for view entries matching [`View`](schema::View)(super::AsyncView).
865
    ///
866
    /// This is the lower-level API. For better ergonomics, consider querying
867
    /// the view using [`self.view::<View>().query()`](super::AsyncView::query)
868
    /// instead. The parameters for the query can be customized on the builder
869
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
870
9317
    async fn query<V: schema::SerializedView>(
871
9317
        &self,
872
9317
        key: Option<QueryKey<V::Key>>,
873
9317
        order: Sort,
874
9317
        limit: Option<u32>,
875
9317
        access_policy: AccessPolicy,
876
9317
    ) -> Result<ViewMappings<V>, Error> {
877
9318
        let view = self.schematic().view::<V>()?;
878
9318
        let mappings = self
879
            .query_by_name(
880
9318
                &view.view_name(),
881
9318
                key.map(|key| key.serialized()).transpose()?,
882
9318
                order,
883
9318
                limit,
884
9318
                access_policy,
885
8712
            )
886
8712
            .await?;
887
9318
        mappings
888
9318
            .into_iter()
889
9350
            .map(|mapping| {
890
9209
                Ok(Map {
891
9209
                    key: <V::Key as Key>::from_ord_bytes(&mapping.key)
892
9209
                        .map_err(view::Error::key_serialization)
893
9209
                        .map_err(Error::from)?,
894
9209
                    value: V::deserialize(&mapping.value)?,
895
9209
                    source: mapping.source,
896
                })
897
9350
            })
898
9318
            .collect::<Result<Vec<_>, Error>>()
899
18635
    }
900

            
901
    /// Queries for view entries matching [`View`](schema::View) with their source documents.
902
    ///
903
    /// This is the lower-level API. For better ergonomics, consider querying
904
    /// the view using [`self.view::<View>().query_with_docs()`](super::AsyncView::query_with_docs) instead.
905
    /// The parameters for the query can be customized on the builder returned
906
    /// from [`AsyncConnection::view()`](super::AsyncConnection::view).
907
    #[must_use]
908
9074
    async fn query_with_docs<V: schema::SerializedView>(
909
9074
        &self,
910
9074
        key: Option<QueryKey<V::Key>>,
911
9074
        order: Sort,
912
9074
        limit: Option<u32>,
913
9074
        access_policy: AccessPolicy,
914
9074
    ) -> Result<MappedDocuments<OwnedDocument, V>, Error> {
915
        // Query permission is checked by the query call
916
9074
        let results = self.query::<V>(key, order, limit, access_policy).await?;
917

            
918
        // Verify that there is permission to fetch each document
919
9074
        let mut ids = Vec::with_capacity(results.len());
920
9076
        ids.extend(results.iter().map(|m| m.source.id));
921

            
922
9074
        let documents = self
923
9074
            .get_multiple::<V::Collection, _, _, _>(ids)
924
8369
            .await?
925
9074
            .into_iter()
926
9076
            .map(|doc| (doc.header.id, doc))
927
9074
            .collect::<BTreeMap<_, _>>();
928
9074

            
929
9074
        Ok(MappedDocuments {
930
9074
            mappings: results,
931
9074
            documents,
932
9074
        })
933
18148
    }
934

            
935
    /// Queries for view entries matching [`View`](schema::View) with their source documents,
936
    /// deserialized.
937
    ///
938
    /// This is the lower-level API. For better ergonomics, consider querying
939
    /// the view using
940
    /// [`self.view::<View>().query_with_collection_docs()`](super::AsyncView::query_with_collection_docs)
941
    /// instead. The parameters for the query can be customized on the builder
942
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
943
    #[must_use]
944
105
    async fn query_with_collection_docs<V>(
945
105
        &self,
946
105
        key: Option<QueryKey<V::Key>>,
947
105
        order: Sort,
948
105
        limit: Option<u32>,
949
105
        access_policy: AccessPolicy,
950
105
    ) -> Result<MappedDocuments<CollectionDocument<V::Collection>, V>, Error>
951
105
    where
952
105
        V: schema::SerializedView,
953
105
        V::Collection: SerializedCollection,
954
105
        <V::Collection as SerializedCollection>::Contents: std::fmt::Debug,
955
105
    {
956
105
        let mapped_docs = self
957
210
            .query_with_docs::<V>(key, order, limit, access_policy)
958
210
            .await?;
959
105
        let mut collection_docs = BTreeMap::new();
960
212
        for (id, doc) in mapped_docs.documents {
961
107
            collection_docs.insert(id, CollectionDocument::<V::Collection>::try_from(&doc)?);
962
        }
963
105
        Ok(MappedDocuments {
964
105
            mappings: mapped_docs.mappings,
965
105
            documents: collection_docs,
966
105
        })
967
210
    }
968

            
969
    /// Reduces the view entries matching [`View`](schema::View).
970
    ///
971
    /// This is the lower-level API. For better ergonomics, consider querying
972
    /// the view using
973
    /// [`self.view::<View>().reduce()`](super::AsyncView::reduce)
974
    /// instead. The parameters for the query can be customized on the builder
975
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
976
    #[must_use]
977
17985
    async fn reduce<V: schema::SerializedView>(
978
17985
        &self,
979
17985
        key: Option<QueryKey<V::Key>>,
980
17985
        access_policy: AccessPolicy,
981
17985
    ) -> Result<V::Value, Error> {
982
17985
        let view = self.schematic().view::<V>()?;
983
        self.reduce_by_name(
984
17985
            &view.view_name(),
985
17985
            key.map(|key| key.serialized()).transpose()?,
986
17985
            access_policy,
987
16662
        )
988
16662
        .await
989
17985
        .and_then(|value| V::deserialize(&value))
990
35970
    }
991

            
992
    /// Reduces the view entries matching [`View`](schema::View), reducing the values by each
993
    /// unique key.
994
    ///
995
    /// This is the lower-level API. For better ergonomics, consider querying
996
    /// the view using
997
    /// [`self.view::<View>().reduce_grouped()`](super::AsyncView::reduce_grouped)
998
    /// instead. The parameters for the query can be customized on the builder
999
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    #[must_use]
12
    async fn reduce_grouped<V: schema::SerializedView>(
12
        &self,
12
        key: Option<QueryKey<V::Key>>,
12
        access_policy: AccessPolicy,
12
    ) -> Result<GroupedReductions<V>, Error> {
12
        let view = self.schematic().view::<V>()?;
        self.reduce_grouped_by_name(
12
            &view.view_name(),
12
            key.map(|key| key.serialized()).transpose()?,
12
            access_policy,
14
        )
14
        .await?
12
        .into_iter()
31
        .map(|map| {
31
            Ok(MappedValue::new(
31
                V::Key::from_ord_bytes(&map.key).map_err(view::Error::key_serialization)?,
31
                V::deserialize(&map.value)?,
            ))
31
        })
12
        .collect::<Result<Vec<_>, Error>>()
24
    }

            
    /// Deletes all of the documents associated with this view.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using
    /// [`self.view::<View>().delete_docs()`](super::AsyncView::delete_docs)
    /// instead. The parameters for the query can be customized on the builder
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    #[must_use]
10
    async fn delete_docs<V: schema::SerializedView>(
10
        &self,
10
        key: Option<QueryKey<V::Key>>,
10
        access_policy: AccessPolicy,
10
    ) -> Result<u64, Error> {
10
        let view = self.schematic().view::<V>()?;
        self.delete_docs_by_name(
10
            &view.view_name(),
10
            key.map(|key| key.serialized()).transpose()?,
10
            access_policy,
10
        )
10
        .await
20
    }

            
    /// Applies a [`Transaction`] to the [`Schema`](schema::Schema). If any
    /// operation in the [`Transaction`] fails, none of the operations will be
    /// applied to the [`Schema`](schema::Schema).
    async fn apply_transaction(
        &self,
        transaction: Transaction,
    ) -> Result<Vec<OperationResult>, Error>;

            
    /// Retrieves the document with `id` stored within the named `collection`.
    ///
    /// This is a lower-level API. For better ergonomics, consider using one of:
    ///
    /// - [`SerializedCollection::get_async()`]
    /// - [`self.collection::<Collection>().get()`](super::AsyncCollection::get)
    async fn get_from_collection(
        &self,
        id: DocumentId,
        collection: &CollectionName,
    ) -> Result<Option<OwnedDocument>, Error>;

            
    /// Retrieves all documents matching `ids` from the named `collection`.
    /// Documents that are not found are not returned, but no error will be
    /// generated.
    ///
    /// This is a lower-level API. For better ergonomics, consider using one of:
    ///
    /// - [`SerializedCollection::get_multiple_async()`]
    /// - [`self.collection::<Collection>().get_multiple()`](super::AsyncCollection::get_multiple)
    async fn get_multiple_from_collection(
        &self,
        ids: &[DocumentId],
        collection: &CollectionName,
    ) -> Result<Vec<OwnedDocument>, Error>;

            
    /// Retrieves all documents within the range of `ids` from the named
    /// `collection`. To retrieve all documents, pass in `..` for `ids`.
    ///
    /// This is a lower-level API. For better ergonomics, consider using one of:
    ///
    /// - [`SerializedCollection::all().headers()`](schema::List::headers)
    /// - [`self.collection::<Collection>().all().headers()`](super::List::headers)
    /// - [`SerializedCollection::list().headers()`](schema::List::headers)
    /// - [`self.collection::<Collection>().list().headers()`](super::List::headers)
    async fn list_from_collection(
        &self,
        ids: Range<DocumentId>,
        order: Sort,
        limit: Option<u32>,
        collection: &CollectionName,
    ) -> Result<Vec<OwnedDocument>, Error>;

            
    /// Retrieves all headers within the range of `ids` from the named
    /// `collection`. To retrieve all documents, pass in `..` for `ids`.
    ///
    /// This is a lower-level API. For better ergonomics, consider using one of:
    ///
    /// - [`SerializedCollection::all().headers()`](schema::AsyncList::headers)
    /// - [`self.collection::<Collection>().all().headers()`](super::AsyncList::headers)
    /// - [`SerializedCollection::list().headers()`](schema::AsyncList::headers)
    /// - [`self.collection::<Collection>().list().headers()`](super::AsyncList::headers)
    async fn list_headers_from_collection(
        &self,
        ids: Range<DocumentId>,
        order: Sort,
        limit: Option<u32>,
        collection: &CollectionName,
    ) -> Result<Vec<Header>, Error>;

            
    /// Counts the number of documents within the range of `ids` from the named
    /// `collection`.
    ///
    /// This is a lower-level API. For better ergonomics, consider using one of:
    ///
    /// - [`SerializedCollection::all_async().count()`](schema::AsyncList::count)
    /// - [`self.collection::<Collection>().all().count()`](super::AsyncList::count)
    /// - [`SerializedCollection::list_async().count()`](schema::AsyncList::count)
    /// - [`self.collection::<Collection>().list().count()`](super::AsyncList::count)
    async fn count_from_collection(
        &self,
        ids: Range<DocumentId>,
        collection: &CollectionName,
    ) -> Result<u64, Error>;

            
    /// Compacts the collection to reclaim unused disk space.
    ///
    /// This process is done by writing data to a new file and swapping the file
    /// once the process completes. This ensures that if a hardware failure,
    /// power outage, or crash occurs that the original collection data is left
    /// untouched.
    ///
    /// ## Errors
    ///
    /// * [`Error::CollectionNotFound`]: database `name` does not exist.
    /// * [`Error::Io`]: an error occurred while compacting the database.
    async fn compact_collection_by_name(&self, collection: CollectionName) -> Result<(), Error>;

            
    /// Queries for view entries from the named `view`.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using [`self.view::<View>().query()`](super::AsyncView::query)
    /// instead. The parameters for the query can be customized on the builder
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    async fn query_by_name(
        &self,
        view: &ViewName,
        key: Option<QueryKey<Bytes>>,
        order: Sort,
        limit: Option<u32>,
        access_policy: AccessPolicy,
    ) -> Result<Vec<schema::view::map::Serialized>, Error>;

            
    /// Queries for view entries from the named `view` with their source
    /// documents.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using [`self.view::<View>().query_with_docs()`](super::AsyncView::query_with_docs) instead.
    /// The parameters for the query can be customized on the builder returned
    /// from [`AsyncConnection::view()`](super::AsyncConnection::view).
    async fn query_by_name_with_docs(
        &self,
        view: &ViewName,
        key: Option<QueryKey<Bytes>>,
        order: Sort,
        limit: Option<u32>,
        access_policy: AccessPolicy,
    ) -> Result<schema::view::map::MappedSerializedDocuments, Error>;

            
    /// Reduces the view entries from the named `view`.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using
    /// [`self.view::<View>().reduce()`](super::AsyncView::reduce)
    /// instead. The parameters for the query can be customized on the builder
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    async fn reduce_by_name(
        &self,
        view: &ViewName,
        key: Option<QueryKey<Bytes>>,
        access_policy: AccessPolicy,
    ) -> Result<Vec<u8>, Error>;

            
    /// Reduces the view entries from the named `view`, reducing the values by each
    /// unique key.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using
    /// [`self.view::<View>().reduce_grouped()`](super::AsyncView::reduce_grouped)
    /// instead. The parameters for the query can be customized on the builder
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    async fn reduce_grouped_by_name(
        &self,
        view: &ViewName,
        key: Option<QueryKey<Bytes>>,
        access_policy: AccessPolicy,
    ) -> Result<Vec<MappedSerializedValue>, Error>;

            
    /// Deletes all source documents for entries that match within the named
    /// `view`.
    ///
    /// This is the lower-level API. For better ergonomics, consider querying
    /// the view using
    /// [`self.view::<View>().delete_docs()`](super::AsyncView::delete_docs)
    /// instead. The parameters for the query can be customized on the builder
    /// returned from [`AsyncConnection::view()`](super::AsyncConnection::view).
    async fn delete_docs_by_name(
        &self,
        view: &ViewName,
        key: Option<QueryKey<Bytes>>,
        access_policy: AccessPolicy,
    ) -> Result<u64, Error>;
}