agdb/query/
insert_values_query.rs

1use crate::DbElement;
2use crate::DbError;
3use crate::DbId;
4use crate::DbImpl;
5use crate::DbKeyValue;
6use crate::QueryId;
7use crate::QueryIds;
8use crate::QueryMut;
9use crate::QueryResult;
10use crate::SearchQuery;
11use crate::StorageData;
12use crate::query::query_values::QueryValues;
13use crate::query_builder::search::SearchQueryBuilder;
14
15/// Query to insert or update key-value pairs (properties)
16/// to existing elements in the database. All `ids` must exist
17/// in the database. If `values` is set to `Single` the properties
18/// will be inserted uniformly to all `ids` otherwise there must be
19/// enough `values` for all `ids`.
20///
21/// The result will be number of inserted/updated values and inserted new
22/// elements (nodes).
23///
24/// NOTE: The result is NOT number of affected elements but individual properties.
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
27#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
28#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
29#[derive(Clone, Debug, PartialEq)]
30pub struct InsertValuesQuery {
31    /// Ids whose properties should be updated
32    pub ids: QueryIds,
33
34    /// Key value pairs to be inserted to the existing elements.
35    pub values: QueryValues,
36}
37
38impl QueryMut for InsertValuesQuery {
39    fn process<Store: StorageData>(&self, db: &mut DbImpl<Store>) -> Result<QueryResult, DbError> {
40        let mut result = QueryResult::default();
41
42        match &self.ids {
43            QueryIds::Ids(ids) => match &self.values {
44                QueryValues::Single(values) => {
45                    for id in ids {
46                        insert_values(db, id, values, &mut result)?;
47                    }
48                }
49                QueryValues::Multi(values) => {
50                    if ids.len() != values.len() {
51                        return Err(DbError::from("Ids and values length do not match"));
52                    }
53
54                    for (id, values) in ids.iter().zip(values) {
55                        insert_values(db, id, values, &mut result)?;
56                    }
57                }
58            },
59            QueryIds::Search(search_query) => {
60                let db_ids = search_query.search(db)?;
61
62                match &self.values {
63                    QueryValues::Single(values) => {
64                        for db_id in db_ids {
65                            insert_values_id(db, db_id, values, &mut result)?;
66                        }
67                    }
68                    QueryValues::Multi(values) => {
69                        if db_ids.len() != values.len() {
70                            return Err(DbError::from("Ids and values length do not match"));
71                        }
72
73                        for (db_id, values) in db_ids.iter().zip(values) {
74                            insert_values_id(db, *db_id, values, &mut result)?;
75                        }
76                    }
77                }
78            }
79        }
80
81        Ok(result)
82    }
83}
84
85impl QueryMut for &InsertValuesQuery {
86    fn process<Store: StorageData>(&self, db: &mut DbImpl<Store>) -> Result<QueryResult, DbError> {
87        (*self).process(db)
88    }
89}
90
91fn insert_values<Store: StorageData>(
92    db: &mut DbImpl<Store>,
93    id: &QueryId,
94    values: &[DbKeyValue],
95    result: &mut QueryResult,
96) -> Result<(), DbError> {
97    match db.db_id(id) {
98        Ok(db_id) => insert_values_id(db, db_id, values, result),
99        Err(e) => match id {
100            QueryId::Id(id) => {
101                if id.0 == 0 {
102                    insert_values_new(db, None, values, result)
103                } else {
104                    Err(e)
105                }
106            }
107            QueryId::Alias(alias) => insert_values_new(db, Some(alias), values, result),
108        },
109    }
110}
111
112fn insert_values_new<Store: StorageData>(
113    db: &mut DbImpl<Store>,
114    alias: Option<&String>,
115    values: &[DbKeyValue],
116    result: &mut QueryResult,
117) -> Result<(), DbError> {
118    let db_id = db.insert_node()?;
119
120    if let Some(alias) = alias {
121        db.insert_new_alias(db_id, alias)?;
122    }
123
124    for key_value in values {
125        db.insert_key_value(db_id, key_value)?;
126    }
127
128    result.result += values.len() as i64;
129    result.elements.push(DbElement {
130        id: db_id,
131        from: None,
132        to: None,
133        values: vec![],
134    });
135
136    Ok(())
137}
138
139fn insert_values_id<Store: StorageData>(
140    db: &mut DbImpl<Store>,
141    db_id: DbId,
142    values: &[DbKeyValue],
143    result: &mut QueryResult,
144) -> Result<(), DbError> {
145    for key_value in values {
146        db.insert_or_replace_key_value(db_id, key_value)?;
147        result.result += 1;
148    }
149    Ok(())
150}
151
152impl SearchQueryBuilder for InsertValuesQuery {
153    fn search_mut(&mut self) -> &mut SearchQuery {
154        if let QueryIds::Search(search) = &mut self.ids {
155            search
156        } else {
157            panic!("Expected search query");
158        }
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    #[should_panic]
168    fn missing_search() {
169        InsertValuesQuery {
170            values: QueryValues::Single(vec![]),
171            ids: QueryIds::Ids(vec![]),
172        }
173        .search_mut();
174    }
175}