drive/query/drive_document_sum_query/
index_picker.rs1use crate::query::drive_document_sum_query::{is_indexable_for_sum, is_range_operator};
18use crate::query::{WhereClause, WhereOperator};
19use dpp::data_contract::document_type::Index;
20use std::collections::{BTreeMap, BTreeSet};
21
22pub fn find_summable_index_for_where_clauses<'b>(
30 indexes: &'b BTreeMap<String, Index>,
31 where_clauses: &[WhereClause],
32 sum_property: &str,
33) -> Option<&'b Index> {
34 if where_clauses
37 .iter()
38 .any(|wc| !is_indexable_for_sum(wc.operator))
39 {
40 return None;
41 }
42
43 let indexable_fields: BTreeSet<&str> = where_clauses
44 .iter()
45 .filter(|wc| matches!(wc.operator, WhereOperator::Equal | WhereOperator::In))
46 .map(|wc| wc.field.as_str())
47 .collect();
48
49 if indexable_fields.is_empty() {
50 return None;
51 }
52
53 for index in indexes.values() {
54 match &index.summable {
56 Some(prop) if prop == sum_property => {}
57 _ => continue,
58 }
59 if index.properties.len() != indexable_fields.len() {
60 continue;
61 }
62 let all_covered = index
63 .properties
64 .iter()
65 .all(|prop| indexable_fields.contains(prop.name.as_str()));
66 if all_covered {
67 return Some(index);
68 }
69 }
70
71 None
72}
73
74pub fn find_range_summable_index_for_where_clauses<'b>(
81 indexes: &'b BTreeMap<String, Index>,
82 where_clauses: &[WhereClause],
83 sum_property: &str,
84) -> Option<&'b Index> {
85 let range_clauses: Vec<&WhereClause> = where_clauses
86 .iter()
87 .filter(|wc| is_range_operator(wc.operator))
88 .collect();
89 let (outer_range_field, terminator_range_clause) = match range_clauses.len() {
90 1 => (None, range_clauses[0]),
91 2 => {
92 if range_clauses[0].field == range_clauses[1].field {
95 return None;
96 }
97 (
98 Some((
99 range_clauses[0].field.as_str(),
100 range_clauses[1].field.as_str(),
101 )),
102 range_clauses[0],
103 )
104 }
105 _ => return None,
106 };
107
108 if where_clauses
111 .iter()
112 .any(|wc| !is_indexable_for_sum(wc.operator) && !is_range_operator(wc.operator))
113 {
114 return None;
115 }
116
117 let prefix_fields: BTreeSet<&str> = where_clauses
118 .iter()
119 .filter(|wc| matches!(wc.operator, WhereOperator::Equal | WhereOperator::In))
120 .map(|wc| wc.field.as_str())
121 .collect();
122
123 for index in indexes.values() {
124 if !index.range_summable {
125 continue;
126 }
127 match &index.summable {
130 Some(prop) if prop == sum_property => {}
131 _ => continue,
132 }
133
134 if let Some((field_a, field_b)) = outer_range_field {
135 let terminator = index.properties.last()?;
136 let first = index.properties.first()?;
137 let (outer_field, _terminator_field) = if terminator.name == field_a {
138 (field_b, field_a)
139 } else if terminator.name == field_b {
140 (field_a, field_b)
141 } else {
142 continue;
143 };
144 if first.name != outer_field {
145 continue;
146 }
147 let intermediate_props = &index.properties[1..index.properties.len() - 1];
148 let mut intermediate_props_ok = true;
149 for prop in intermediate_props {
150 if !prefix_fields.contains(prop.name.as_str()) {
151 intermediate_props_ok = false;
152 break;
153 }
154 }
155 if intermediate_props_ok && intermediate_props.len() == prefix_fields.len() {
162 return Some(index);
163 }
164 continue;
165 }
166
167 let mut prefix_len = 0usize;
169 for prop in &index.properties {
170 if prefix_fields.contains(prop.name.as_str()) {
171 prefix_len += 1;
172 } else {
173 break;
174 }
175 }
176 if prefix_len < prefix_fields.len() {
177 continue;
178 }
179 if prefix_len + 1 != index.properties.len() {
180 continue;
181 }
182 let range_prop = &index.properties[prefix_len];
183 if range_prop.name == terminator_range_clause.field {
184 return Some(index);
185 }
186 }
187
188 None
189}