semilattice_database_session/session/
search.rs1use std::{
2 collections::BTreeSet,
3 num::{NonZeroI32, NonZeroI64, NonZeroU32},
4 sync::Arc,
5};
6
7use async_recursion::async_recursion;
8use hashbrown::HashMap;
9use semilattice_database::{
10 idx_binary::AvltrieeSearch, search, Collection, Condition, Depend, FieldName, SearchResult,
11};
12
13use crate::{Session, SessionDatabase};
14
15use super::{SessionOperation, TemporaryDataEntity};
16
17#[derive(Debug, Clone, PartialEq)]
18pub struct SessionSearchResult {
19 collection_id: i32,
20 rows: BTreeSet<NonZeroI64>,
21 join: HashMap<Arc<String>, HashMap<NonZeroI64, SessionSearchResult>>,
22}
23
24impl SessionSearchResult {
25 pub fn rows(&self) -> &BTreeSet<NonZeroI64> {
26 &self.rows
27 }
28
29 pub fn join(&self) -> &HashMap<Arc<String>, HashMap<NonZeroI64, SessionSearchResult>> {
30 &self.join
31 }
32}
33
34impl Session {
35 fn temporary_data_match(
36 row: NonZeroI64,
37 ent: &TemporaryDataEntity,
38 condition: &Condition,
39 ) -> bool {
40 match condition {
41 Condition::Row(cond) => match cond {
42 search::Number::In(c) => c.contains(&(row.get() as isize)),
43 search::Number::Max(c) => row.get() <= *c as i64,
44 search::Number::Min(c) => row.get() >= *c as i64,
45 search::Number::Range(c) => c.contains(&(row.get() as isize)),
46 },
47 Condition::Uuid(uuid) => uuid.contains(&ent.uuid),
48 Condition::Activity(activity) => ent.activity == *activity,
49 Condition::Term(cond) => match cond {
50 search::Term::In(c) => {
51 ent.term_begin < *c && (ent.term_end == 0 || ent.term_end > *c)
52 }
53 search::Term::Past(c) => ent.term_end >= *c,
54 search::Term::Future(c) => ent.term_begin >= *c,
55 },
56 Condition::Field(field_id, cond) => {
57 ent.fields.get(field_id).map_or(false, |f| match cond {
58 search::Field::Match(v) => f == v,
59 search::Field::Range(min, max) => min <= f && max >= f,
60 search::Field::Min(min) => min <= f,
61 search::Field::Max(max) => max >= f,
62 search::Field::Forward(v) => {
63 unsafe { std::str::from_utf8_unchecked(f) }.starts_with(v.as_ref())
64 }
65 search::Field::Partial(v) => {
66 unsafe { std::str::from_utf8_unchecked(f) }.contains(v.as_ref())
67 }
68 search::Field::Backward(v) => {
69 unsafe { std::str::from_utf8_unchecked(f) }.ends_with(v.as_ref())
70 }
71 search::Field::ValueForward(v) => {
72 v.starts_with(unsafe { std::str::from_utf8_unchecked(f) })
73 }
74 search::Field::ValueBackward(v) => {
75 v.ends_with(unsafe { std::str::from_utf8_unchecked(f) })
76 }
77 search::Field::ValuePartial(v) => {
78 v.contains(unsafe { std::str::from_utf8_unchecked(f) })
79 }
80 })
81 }
82 Condition::Narrow(conditions) => {
83 let mut is_match = true;
84 for c in conditions.into_iter() {
85 is_match &= Self::temporary_data_match(row, ent, c);
86 if !is_match {
87 break;
88 }
89 }
90 is_match
91 }
92 Condition::Wide(conditions) => {
93 let mut is_match = false;
94 for c in conditions.into_iter() {
95 is_match |= Self::temporary_data_match(row, ent, c);
96 if is_match {
97 break;
98 }
99 }
100 is_match
101 }
102 Condition::Depend(key, collection_row) => {
103 let mut is_match = true;
104 for depend in &ent.depends {
105 is_match = key.as_ref().map_or(true, |key| key == depend.key())
106 && collection_row == &**depend;
107 if is_match {
108 break;
109 }
110 }
111 is_match
112 }
113 Condition::LastUpdated(_) => true,
114 }
115 }
116
117 fn temprary_data_match_conditions(
118 conditions: &Vec<Condition>,
119 row: NonZeroI64,
120 ent: &TemporaryDataEntity,
121 ) -> bool {
122 for c in conditions.into_iter() {
123 if !Self::temporary_data_match(row, ent, c) {
124 return false;
125 }
126 }
127 true
128 }
129
130 #[async_recursion(?Send)]
131 async fn join(
132 &self,
133 join_result: &HashMap<Arc<String>, HashMap<NonZeroU32, SearchResult>>,
134 ) -> HashMap<Arc<String>, HashMap<NonZeroI64, SessionSearchResult>> {
135 let mut map = HashMap::new();
136 for (name, row_join) in join_result {
137 let mut map_inner = HashMap::new();
138 for (row, result) in row_join {
139 let result = self.result_with(result).await;
140 map_inner.insert((*row).into(), result);
141 }
142 map.insert(name.to_owned(), map_inner);
143 }
144 map
145 }
146
147 pub async fn result_with(&self, search_result: &SearchResult) -> SessionSearchResult {
148 let (collection_id, rows) = if let Some(search) = search_result.search() {
149 let collection_id = search.collection_id();
150 (
151 collection_id.get(),
152 if let Some(tmp) = self.temporary_data.get(&collection_id) {
153 let mut rows: BTreeSet<NonZeroI64> = BTreeSet::new();
154 for row in search_result.rows().into_iter() {
155 let row = NonZeroI64::from(*row);
156 if let Some(ent) = tmp.get(&row) {
157 if ent.operation != SessionOperation::Delete {
158 if Self::temprary_data_match_conditions(
159 search.conditions(),
160 row,
161 ent,
162 ) {
163 rows.insert(row);
164 }
165 }
166 } else {
167 rows.insert(row);
168 }
169 }
170 for (row, _) in tmp.into_iter() {
171 if row.get() < 0 {
172 if let Some(ent) = tmp.get(row) {
173 if ent.operation != SessionOperation::Delete {
174 if Self::temprary_data_match_conditions(
175 search.conditions(),
176 *row,
177 ent,
178 ) {
179 rows.insert(*row);
180 }
181 }
182 }
183 }
184 }
185 rows
186 } else {
187 search_result
188 .rows()
189 .into_iter()
190 .map(|x| NonZeroI64::from(*x))
191 .collect()
192 },
193 )
194 } else {
195 (0, BTreeSet::new())
196 };
197 let join = self.join(search_result.join()).await;
198
199 SessionSearchResult {
200 collection_id,
201 rows,
202 join,
203 }
204 }
205
206 pub fn field_bytes<'a>(
207 &'a self,
208 database: &'a SessionDatabase,
209 collection_id: NonZeroI32,
210 row: NonZeroI64,
211 field_name: &FieldName,
212 ) -> &[u8] {
213 if let Some(temporary_collection) = self.temporary_data.get(&collection_id) {
214 if let Some(tmp_row) = temporary_collection.get(&row) {
215 if let Some(val) = tmp_row.fields.get(field_name) {
216 return val;
217 }
218 }
219 }
220 if row.get() > 0 {
221 if let Some(collection) = database.collection(collection_id) {
222 return collection.field_bytes(row.try_into().unwrap(), field_name);
223 }
224 }
225 b""
226 }
227
228 pub fn collection_field_bytes<'a>(
229 &'a self,
230 collection: &'a Collection,
231 row: NonZeroI64,
232 field_name: &FieldName,
233 ) -> &[u8] {
234 if let Some(temprary_collection) = self.temporary_data.get(&collection.id()) {
235 if let Some(temprary_row) = temprary_collection.get(&row) {
236 if let Some(val) = temprary_row.fields.get(field_name) {
237 return val;
238 }
239 }
240 }
241 if row.get() > 0 {
242 collection.field_bytes(row.try_into().unwrap(), field_name)
243 } else {
244 b""
245 }
246 }
247
248 pub fn temporary_collection(
249 &self,
250 collection_id: NonZeroI32,
251 ) -> Option<&HashMap<NonZeroI64, TemporaryDataEntity>> {
252 self.temporary_data.get(&collection_id)
253 }
254
255 pub fn depends(&self, key: Option<Arc<String>>, pend_row: NonZeroU32) -> Option<Vec<Depend>> {
256 self.session_data.as_ref().and_then(move |session_data| {
257 key.map_or_else(
258 || {
259 Some(
260 session_data
261 .relation
262 .rows
263 .session_row
264 .iter_by(&pend_row.get())
265 .filter_map(|relation_row| {
266 if let (Some(key), Some(depend)) = (
267 session_data.relation.rows.key.value(relation_row),
268 session_data.relation.rows.depend.value(relation_row),
269 ) {
270 return Some(Depend::new(
271 Arc::new(
272 unsafe {
273 std::str::from_utf8_unchecked(
274 session_data
275 .relation
276 .key_names
277 .value(NonZeroU32::new(*key).unwrap())
278 .unwrap(),
279 )
280 }
281 .into(),
282 ),
283 depend.clone(),
284 ));
285 }
286 None
287 })
288 .collect(),
289 )
290 },
291 |key_name| {
292 session_data
293 .relation
294 .key_names
295 .row(key_name.as_bytes())
296 .map(|key_id| {
297 session_data
298 .relation
299 .rows
300 .session_row
301 .iter_by(&pend_row.get())
302 .filter_map(|relation_row| {
303 if let (Some(key), Some(depend)) = (
304 session_data.relation.rows.key.value(relation_row),
305 session_data.relation.rows.depend.value(relation_row),
306 ) {
307 if *key == key_id.get() {
308 return Some(Depend::new(
309 Arc::clone(&key_name),
310 depend.clone(),
311 ));
312 }
313 }
314 None
315 })
316 .collect()
317 })
318 },
319 )
320 })
321 }
322}