Skip to main content

drive/query/drive_document_count_query/executors/
total.rs

1//! Total-count executor for [`super::super::DocumentCountMode::Total`]
2//! dispatch — `prove = false` count queries without a range clause.
3
4use super::super::super::conditions::WhereClause;
5use super::super::{DriveDocumentCountQuery, SplitCountEntry};
6use crate::drive::Drive;
7use crate::error::query::QuerySyntaxError;
8use crate::error::Error;
9use dpp::data_contract::document_type::DocumentTypeRef;
10use dpp::version::PlatformVersion;
11use grovedb::TransactionArg;
12
13impl Drive {
14    /// Total count for the given where clauses against an exactly-
15    /// covering countable index, OR — when the where clauses are
16    /// empty and the document type has `documents_countable: true` —
17    /// the type's primary-key CountTree (O(1) read at the doctype
18    /// tree's root).
19    ///
20    /// Single summed entry with empty key.
21    pub fn execute_document_count_total_no_proof(
22        &self,
23        contract_id: [u8; 32],
24        document_type: DocumentTypeRef,
25        document_type_name: String,
26        where_clauses: Vec<WhereClause>,
27        transaction: TransactionArg,
28        platform_version: &PlatformVersion,
29    ) -> Result<Vec<SplitCountEntry>, Error> {
30        use dpp::data_contract::document_type::accessors::{
31            DocumentTypeV0Getters, DocumentTypeV2Getters,
32        };
33
34        // Fast path: unfiltered total count on a `documents_countable:
35        // true` document type reads the primary-key CountTree directly
36        // (O(1)). No index needed — the doctype tree itself carries
37        // the count.
38        if where_clauses.is_empty() && document_type.documents_countable() {
39            let count = self.read_primary_key_count_tree(
40                &contract_id,
41                &document_type_name,
42                transaction,
43                platform_version,
44            )?;
45            return Ok(vec![SplitCountEntry {
46                in_key: None,
47                key: vec![],
48                // `documents_countable` fast path: we read the
49                // CountTree directly and got an explicit count, so
50                // this is a verified `Some(_)` (possibly `Some(0)`
51                // for an empty doctype).
52                count: Some(count),
53            }]);
54        }
55
56        let index = DriveDocumentCountQuery::find_countable_index_for_where_clauses(
57            document_type.indexes(),
58            &where_clauses,
59        )
60        .ok_or_else(|| {
61            Error::Query(QuerySyntaxError::WhereClauseOnNonIndexedProperty(
62                "count query requires a `countable: true` index whose properties \
63                     exactly match the where clause fields, or `documentsCountable: \
64                     true` on the document type for unfiltered total counts"
65                    .to_string(),
66            ))
67        })?;
68        let count_query = DriveDocumentCountQuery {
69            document_type,
70            contract_id,
71            document_type_name,
72            index,
73            where_clauses,
74        };
75        count_query.execute_no_proof(self, transaction, platform_version)
76    }
77
78    /// Reads the document-type primary-key tree's `CountTree` element
79    /// (`[contract_doc, contract_id, [1], doctype, 0]`) and returns
80    /// `count_value_or_default()`. Used by the `documents_countable:
81    /// true` fast path on the total-count flow.
82    ///
83    /// Returns 0 when the element doesn't exist (e.g. fresh contract
84    /// with no documents inserted). Caller is responsible for ensuring
85    /// `documents_countable` is set on the document type before
86    /// calling — without it the element at `[..., doctype, 0]` is a
87    /// regular `NormalTree` and `count_value_or_default()` returns 0
88    /// regardless of how many documents the type actually has.
89    pub(super) fn read_primary_key_count_tree(
90        &self,
91        contract_id: &[u8; 32],
92        document_type_name: &str,
93        transaction: TransactionArg,
94        platform_version: &PlatformVersion,
95    ) -> Result<u64, Error> {
96        let drive_version = &platform_version.drive;
97        let path = [
98            &[crate::drive::RootTree::DataContractDocuments as u8] as &[u8],
99            contract_id,
100            &[1u8],
101            document_type_name.as_bytes(),
102        ];
103        let mut drive_operations = vec![];
104        let element = self.grove_get_raw_optional(
105            grovedb_path::SubtreePath::from(path.as_slice()),
106            &[0],
107            crate::util::grove_operations::DirectQueryType::StatefulDirectQuery,
108            transaction,
109            &mut drive_operations,
110            drive_version,
111        )?;
112        Ok(element.map_or(0, |e| e.count_value_or_default()))
113    }
114}