semilattice_database/search/
join.rs1use std::{
2 num::{NonZeroI32, NonZeroU32},
3 sync::Arc,
4};
5
6use async_recursion::async_recursion;
7use futures::{future, FutureExt};
8use hashbrown::HashMap;
9use versatile_data::RowSet;
10
11use crate::{CollectionRow, Condition, Database, Search};
12
13use super::SearchResult;
14
15#[derive(Debug, Clone, PartialEq)]
16pub struct SearchJoin {
17 collection_id: NonZeroI32,
18 relation_key: Option<Arc<String>>,
19 conditions: Vec<Condition>,
20 join: HashMap<Arc<String>, SearchJoin>,
21}
22
23impl SearchJoin {
24 pub fn new(
25 collection_id: NonZeroI32,
26 conditions: Vec<Condition>,
27 relation_key: Option<Arc<String>>,
28 join: HashMap<Arc<String>, SearchJoin>,
29 ) -> Self {
30 Self {
31 collection_id,
32 conditions,
33 relation_key,
34 join,
35 }
36 }
37
38 #[async_recursion(?Send)]
39 async fn join_result_row(
40 &self,
41 database: &Database,
42 parent_collection_id: NonZeroI32,
43 parent_row: NonZeroU32,
44 ) -> SearchResult {
45 let mut futs = vec![];
46 if let Some(key) = &self.relation_key {
47 futs.push(
48 async {
49 database
50 .relation
51 .pends(
52 Some(Arc::clone(key)),
53 &CollectionRow::new(parent_collection_id, parent_row),
54 Some(self.collection_id),
55 )
56 .into_iter()
57 .map(|r| r.row())
58 .collect()
59 }
60 .boxed_local(),
61 );
62 }
63 if self.conditions.len() > 0 {
64 if let Some(collection) = database.collection(self.collection_id) {
65 futs.push(
66 async {
67 Search::result_conditions(collection, &self.conditions, &database.relation)
68 .await
69 }
70 .boxed_local(),
71 );
72 }
73 }
74
75 let (mut rows, _index, futs) = future::select_all(futs).await;
76 for r in future::join_all(futs).await.into_iter() {
77 rows.retain(|v| r.contains(v));
78 }
79
80 let join_nest = future::join_all(self.join.iter().map(|(key, join)| async {
81 (
82 Arc::clone(key),
83 join.join_result(database, self.collection_id, &rows).await,
84 )
85 }))
86 .await
87 .into_iter()
88 .collect();
89
90 SearchResult::new(None, rows, join_nest)
91 }
92
93 pub async fn join_result(
94 &self,
95 database: &Database,
96 parent_collection_id: NonZeroI32,
97 parent_rows: &RowSet,
98 ) -> HashMap<NonZeroU32, SearchResult> {
99 future::join_all(parent_rows.into_iter().map(|parent_row| async {
100 (
101 *parent_row,
102 self.join_result_row(database, parent_collection_id, *parent_row)
103 .await,
104 )
105 }))
106 .await
107 .into_iter()
108 .collect()
109 }
110}