semilattice_database/search/
join.rs

1use 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}