Skip to main content

agdb/query/
insert_edges_query.rs

1use crate::DbElement;
2use crate::DbError;
3use crate::DbId;
4use crate::DbImpl;
5use crate::DbKeyValue;
6use crate::QueryIds;
7use crate::QueryMut;
8use crate::QueryResult;
9use crate::StorageData;
10use crate::query::query_values::QueryValues;
11
12/// Query to inserts edges to the database. The `from`
13/// and `to` ids must exist in the database. There must be
14/// enough `values` for all new edges unless set to `Single`
15/// in which case they will be uniformly applied to all new
16/// edges. The `each` flag is only useful if `from and `to` are
17/// symmetric (same length) but you still want to connect every
18/// origin to every destination. By default it would connect only
19/// the pairs. For asymmetric inserts `each` is assumed.
20///
21/// If the `ids` member is empty the query will insert new edges
22/// otherwise it will update the existing edges. The rules for length
23/// of `values` still apply and the search yield or static list must
24/// have equal length to the `values` (or the `Single` variant must
25/// be used).
26///
27/// The result will contain number of edges inserted or udpated and elements
28/// with their ids, origin and destination, but no properties.
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
31#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
32#[cfg_attr(feature = "api", derive(agdb::TypeDefImpl))]
33#[derive(Clone, Debug, PartialEq)]
34pub struct InsertEdgesQuery {
35    /// Origins
36    pub from: QueryIds,
37
38    /// Destinations
39    pub to: QueryIds,
40
41    /// Optional ids of edges (optionally a search sub-query).
42    /// This can be empty.
43    pub ids: QueryIds,
44
45    /// Key value pairs to be associated with
46    /// the new edges.
47    pub values: QueryValues,
48
49    /// If `true` create an edge between each origin
50    /// and destination.
51    pub each: bool,
52}
53
54impl QueryMut for InsertEdgesQuery {
55    fn process<Store: StorageData>(&self, db: &mut DbImpl<Store>) -> Result<QueryResult, DbError> {
56        let mut result = QueryResult::default();
57
58        let query_ids = match &self.ids {
59            QueryIds::Ids(ids) => ids
60                .iter()
61                .map(|query_id| db.db_id(query_id))
62                .collect::<Result<Vec<DbId>, DbError>>()?,
63            QueryIds::Search(search_query) => search_query.search(db)?,
64        };
65
66        let ids = if !query_ids.is_empty() {
67            query_ids.iter().try_for_each(|db_id| {
68                if db_id.0 > 0 {
69                    Err(DbError::from(format!(
70                        "The ids for insert or update must all refer to edges - node id '{}' found",
71                        db_id.0
72                    )))
73                } else {
74                    Ok(())
75                }
76            })?;
77
78            let mut ids = vec![];
79
80            for (db_id, key_values) in query_ids.iter().zip(self.values(query_ids.len())?) {
81                db.reserve_key_value_capacity(*db_id, key_values.len() as u64)?;
82                for key_value in key_values {
83                    db.insert_or_replace_key_value(*db_id, key_value)?;
84                }
85
86                ids.push(*db_id);
87            }
88
89            ids
90        } else {
91            let from = Self::db_ids(&self.from, db)?;
92            let to: Vec<DbId> = Self::db_ids(&self.to, db)?;
93
94            if self.each || from.len() != to.len() {
95                self.many_to_many_each(db, &from, &to)?
96            } else {
97                self.many_to_many(db, &from, &to)?
98            }
99        };
100
101        result.result = ids.len() as i64;
102        result.elements = ids
103            .into_iter()
104            .map(|id| DbElement {
105                id,
106                from: db.from_id(id),
107                to: db.to_id(id),
108                values: vec![],
109            })
110            .collect();
111
112        Ok(result)
113    }
114}
115
116impl QueryMut for &InsertEdgesQuery {
117    fn process<Store: StorageData>(&self, db: &mut DbImpl<Store>) -> Result<QueryResult, DbError> {
118        (*self).process(db)
119    }
120}
121
122impl InsertEdgesQuery {
123    fn db_ids<Store: StorageData>(
124        query_ids: &QueryIds,
125        db: &DbImpl<Store>,
126    ) -> Result<Vec<DbId>, DbError> {
127        Ok(match &query_ids {
128            QueryIds::Ids(query_ids) => {
129                let mut ids = Vec::with_capacity(query_ids.len());
130
131                for query_id in query_ids {
132                    ids.push(db.db_id(query_id)?);
133                }
134
135                ids
136            }
137            QueryIds::Search(search_query) => search_query
138                .search(db)?
139                .into_iter()
140                .filter(|id| id.0 > 0)
141                .collect(),
142        })
143    }
144
145    fn many_to_many<Store: StorageData>(
146        &self,
147        db: &mut DbImpl<Store>,
148        from: &[DbId],
149        to: &[DbId],
150    ) -> Result<Vec<DbId>, DbError> {
151        let mut ids = Vec::with_capacity(from.len());
152        let values = self.values(from.len())?;
153
154        for ((from, to), key_values) in from.iter().zip(to).zip(values) {
155            let db_id = db.insert_edge(*from, *to)?;
156            ids.push(db_id);
157
158            db.reserve_key_value_capacity(db_id, key_values.len() as u64)?;
159
160            for key_value in key_values {
161                db.insert_key_value(db_id, key_value)?;
162            }
163        }
164
165        Ok(ids)
166    }
167
168    fn many_to_many_each<Store: StorageData>(
169        &self,
170        db: &mut DbImpl<Store>,
171        from: &[DbId],
172        to: &[DbId],
173    ) -> Result<Vec<DbId>, DbError> {
174        let count = from.len() * to.len();
175        let mut ids = Vec::with_capacity(count);
176        let values = self.values(count)?;
177        let mut index = 0;
178
179        for from in from {
180            for to in to {
181                let db_id = db.insert_edge(*from, *to)?;
182                ids.push(db_id);
183
184                db.reserve_key_value_capacity(db_id, values[index].len() as u64)?;
185
186                for key_value in values[index] {
187                    db.insert_key_value(db_id, key_value)?;
188                }
189
190                index += 1;
191            }
192        }
193
194        Ok(ids)
195    }
196
197    fn values(&self, count: usize) -> Result<Vec<&Vec<DbKeyValue>>, DbError> {
198        let values = match &self.values {
199            QueryValues::Single(v) => vec![v; std::cmp::max(1, count)],
200            QueryValues::Multi(v) => v.iter().collect(),
201        };
202
203        if values.len() != count {
204            return Err(DbError::from(format!(
205                "Values len '{}' do not match the insert count '{count}'",
206                values.len()
207            )));
208        }
209
210        Ok(values)
211    }
212}