|
|
|
@ -2,7 +2,7 @@ |
|
|
|
|
#[macro_use] extern crate log; |
|
|
|
|
|
|
|
|
|
use std::borrow::Cow; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
use std::collections::{HashMap, HashSet}; |
|
|
|
|
|
|
|
|
|
use itertools::Itertools; |
|
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
@ -18,11 +18,13 @@ use crate::model::{PropertyModel, RelationModel}; |
|
|
|
|
|
|
|
|
|
pub use data::{TypedValue}; |
|
|
|
|
pub use model::{DataType}; |
|
|
|
|
use crate::data::Object; |
|
|
|
|
use crate::data::{Object, Value}; |
|
|
|
|
use crate::update::{UpdateObj, UpsertValue}; |
|
|
|
|
|
|
|
|
|
pub mod model; |
|
|
|
|
pub mod data; |
|
|
|
|
pub mod insert; |
|
|
|
|
pub mod update; |
|
|
|
|
pub mod id; |
|
|
|
|
mod cool; |
|
|
|
|
|
|
|
|
@ -45,7 +47,7 @@ pub struct Storage { |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
relations: HashMap<ID, data::Relation>, |
|
|
|
|
#[serde(with = "serde_map_as_list")] |
|
|
|
|
properties: HashMap<ID, data::Value>, |
|
|
|
|
values: HashMap<ID, data::Value>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -162,7 +164,7 @@ impl Storage { |
|
|
|
|
let _ = map_drain_filter(&mut self.objects, |_k, v| v.model == id); |
|
|
|
|
|
|
|
|
|
// Remove property values
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_ids.contains(&v.model)); |
|
|
|
|
let _ = map_drain_filter(&mut self.values, |_k, v| removed_prop_ids.contains(&v.model)); |
|
|
|
|
|
|
|
|
|
// Remove relations
|
|
|
|
|
let _ = map_drain_filter(&mut self.relations, |_k, v| removed_relation_ids.contains(&v.model)); |
|
|
|
@ -186,7 +188,7 @@ impl Storage { |
|
|
|
|
// Remove related property templates
|
|
|
|
|
let removed_prop_tpl_ids = map_drain_filter(&mut self.prop_models, |_k, v| v.object == id).keys(); |
|
|
|
|
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| removed_prop_tpl_ids.contains(&v.model)); |
|
|
|
|
let _ = map_drain_filter(&mut self.values, |_k, v| removed_prop_tpl_ids.contains(&v.model)); |
|
|
|
|
|
|
|
|
|
// Related object remain untouched, so there can be a problem with orphans. This is up to the application to deal with.
|
|
|
|
|
|
|
|
|
@ -202,7 +204,7 @@ impl Storage { |
|
|
|
|
debug!("Undefine property model \"{}\"", t.name); |
|
|
|
|
|
|
|
|
|
// Remove relations
|
|
|
|
|
let _ = map_drain_filter(&mut self.properties, |_k, v| v.model == id); |
|
|
|
|
let _ = map_drain_filter(&mut self.values, |_k, v| v.model == id); |
|
|
|
|
Ok(t) |
|
|
|
|
} else { |
|
|
|
|
Err(StorageError::NotExist(format!("property model {}", id).into())) |
|
|
|
@ -237,7 +239,7 @@ impl Storage { |
|
|
|
|
|
|
|
|
|
/// Insert object with relations, validating the data model constraints
|
|
|
|
|
pub fn insert_object(&mut self, insobj: InsertObj) -> Result<ID, StorageError> { |
|
|
|
|
let obj_model_id = insobj.model_id; |
|
|
|
|
let obj_model_id = insobj.model; |
|
|
|
|
debug!("Insert object {:?}", insobj); |
|
|
|
|
|
|
|
|
|
let obj_model = match self.obj_models.get(&obj_model_id) { |
|
|
|
@ -259,7 +261,7 @@ impl Storage { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let find_values_to_insert = |values: Vec<InsertValue>, parent_id : ID, parent_model_id: ID| -> Result<Vec<data::Value>, StorageError> { |
|
|
|
|
let mut values_by_id = values.into_iter().into_group_map_by(|iv| iv.model_id); |
|
|
|
|
let mut values_by_id = values.into_iter().into_group_map_by(|iv| iv.model); |
|
|
|
|
let mut values_to_insert = vec![]; |
|
|
|
|
|
|
|
|
|
for (id, prop) in self.prop_models.iter().filter(|(_id, p)| p.object == parent_model_id) { |
|
|
|
@ -294,7 +296,7 @@ impl Storage { |
|
|
|
|
let mut values_to_insert = find_values_to_insert(insobj.values, object_id, obj_model_id)?; |
|
|
|
|
|
|
|
|
|
// And now ..... relations!
|
|
|
|
|
let mut relations_by_id = insobj.relations.into_iter().into_group_map_by(|ir| ir.model_id); |
|
|
|
|
let mut relations_by_id = insobj.relations.into_iter().into_group_map_by(|ir| ir.model); |
|
|
|
|
let mut relations_to_insert = vec![]; |
|
|
|
|
|
|
|
|
|
for (relation_model_id, relation_model) in self.rel_models.iter().filter(|(_id, r)| r.object == obj_model_id) { |
|
|
|
@ -304,7 +306,7 @@ impl Storage { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for rel_instance in instances { |
|
|
|
|
if let Some(related) = self.objects.get(&rel_instance.related_id) { |
|
|
|
|
if let Some(related) = self.objects.get(&rel_instance.related) { |
|
|
|
|
if related.model != relation_model.related { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("{} of {} requires object of type {}, got {}", |
|
|
|
@ -322,8 +324,8 @@ impl Storage { |
|
|
|
|
relations_to_insert.push(data::Relation { |
|
|
|
|
id: relation_id, |
|
|
|
|
object: object_id, |
|
|
|
|
model: rel_instance.model_id, |
|
|
|
|
related: rel_instance.related_id, |
|
|
|
|
model: rel_instance.model, |
|
|
|
|
related: rel_instance.related, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
@ -340,7 +342,7 @@ impl Storage { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for value in values_to_insert { |
|
|
|
|
self.properties.insert(value.id, value); |
|
|
|
|
self.values.insert(value.id, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(object_id) |
|
|
|
@ -379,13 +381,18 @@ impl Storage { |
|
|
|
|
.filter(move |rel| rel.object == object_id) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn get_reciprocal_relations_for_object(&self, object_id: ID) -> impl Iterator<Item=&data::Relation> { |
|
|
|
|
self.relations.values() |
|
|
|
|
.filter(move |rel| rel.related == object_id) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn get_values_for_object(&self, object_id: ID) -> impl Iterator<Item=&data::Value> { |
|
|
|
|
self.properties.values() |
|
|
|
|
self.values.values() |
|
|
|
|
.filter(move |prop| prop.object == object_id) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn get_grouped_values_for_objects(&self, parents: Vec<ID>) -> HashMap<ID, Vec<&data::Value>> { |
|
|
|
|
self.properties.values() |
|
|
|
|
self.values.values() |
|
|
|
|
.filter(move |prop| parents.contains(&prop.object)) |
|
|
|
|
.into_group_map_by(|model| model.object) |
|
|
|
|
} |
|
|
|
@ -425,7 +432,146 @@ impl Storage { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Updates
|
|
|
|
|
pub fn update_object(&mut self, model : ObjectModel) -> Result<(), StorageError> { |
|
|
|
|
pub fn update_object(&mut self, updobj: UpdateObj) -> Result<(), StorageError> { |
|
|
|
|
let old_object = self.objects.get(&updobj.id) |
|
|
|
|
.ok_or_else(|| StorageError::ConstraintViolation(format!("Object does not exist").into()))?; |
|
|
|
|
|
|
|
|
|
let updated_object_id = old_object.id; |
|
|
|
|
let updated_object_model_id = old_object.model; |
|
|
|
|
debug!("Update object {:?}", updobj); |
|
|
|
|
|
|
|
|
|
let obj_model = match self.obj_models.get(&updated_object_model_id) { |
|
|
|
|
Some(m) => m, |
|
|
|
|
None => return Err(StorageError::NotExist(format!("object model {}", updated_object_model_id).into())) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// validate unique name
|
|
|
|
|
if self.objects.iter().find(|(_, o)| o.model == updated_object_model_id && o.name == updobj.name && o.id != updated_object_id).is_some() { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("{} named \"{}\" already exists", self.get_model_name(updated_object_model_id), updobj.name).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update the object after everything else is checked
|
|
|
|
|
|
|
|
|
|
let find_values_to_change = |values: Vec<UpsertValue>, parent_id : ID, parent_model_id: ID| -> Result<( |
|
|
|
|
// Insert (can overwrite existing, the ID will not change)
|
|
|
|
|
Vec<data::Value>, |
|
|
|
|
// Delete
|
|
|
|
|
Vec<ID> |
|
|
|
|
), StorageError> { |
|
|
|
|
let mut values_by_model = values.into_iter().into_group_map_by(|iv| iv.model); |
|
|
|
|
let mut values_to_insert = vec![]; |
|
|
|
|
let mut ids_to_delete = vec![]; |
|
|
|
|
|
|
|
|
|
let mut existing_values_by_id = self.values.values() |
|
|
|
|
.filter(|v| v.object == parent_id) |
|
|
|
|
.into_group_map_by(|v| v.model); |
|
|
|
|
|
|
|
|
|
for (prop_model_id, prop) in self.prop_models.iter().filter(|(_id, p)| p.object == parent_model_id) { |
|
|
|
|
if let Some(values) = values_by_model.remove(prop_model_id) { |
|
|
|
|
if values.len() > 1 && !prop.multiple { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} of {} cannot have multiple values", prop, self.describe_model(parent_model_id)).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let updated_ids = values.iter().filter_map(|v| v.id).collect_vec(); |
|
|
|
|
|
|
|
|
|
ids_to_delete.extend(existing_values_by_id.remove(&prop.id).unwrap_or_default().into_iter() |
|
|
|
|
.filter(|v| !updated_ids.contains(&v.id)).map(|v| v.id)); |
|
|
|
|
|
|
|
|
|
for val_instance in values { |
|
|
|
|
values_to_insert.push(data::Value { |
|
|
|
|
id: val_instance.id.unwrap_or_else(|| next_id()), |
|
|
|
|
object: parent_id, |
|
|
|
|
model: prop.id, |
|
|
|
|
value: val_instance.value |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if !prop.optional { |
|
|
|
|
warn!("Attempt to remove non-optional prop, do nothing"); |
|
|
|
|
} else { |
|
|
|
|
if let Some(existing) = existing_values_by_id.remove(&prop.id) { |
|
|
|
|
ids_to_delete.extend(existing.into_iter().map(|v| v.id)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok((values_to_insert, ids_to_delete)) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let (mut values_to_insert, mut value_ids_to_delete) = find_values_to_change(updobj.values, updated_object_id, updated_object_model_id)?; |
|
|
|
|
|
|
|
|
|
// And now ..... relations!
|
|
|
|
|
let mut relations_by_model = updobj.relations.into_iter().into_group_map_by(|ir| ir.model); |
|
|
|
|
let mut relations_to_insert = vec![]; |
|
|
|
|
let mut relations_to_delete = vec![]; |
|
|
|
|
|
|
|
|
|
let existing_relations_by_id = self.relations.values() |
|
|
|
|
.filter(|v| v.object == updated_object_id) |
|
|
|
|
.into_group_map_by(|v| v.model); |
|
|
|
|
|
|
|
|
|
for (relation_model_id, relation_model) in self.rel_models.iter().filter(|(_id, r)| r.object == updated_object_model_id) { |
|
|
|
|
if let Some(instances) = relations_by_model.remove(relation_model_id) { |
|
|
|
|
if instances.len() > 1 && !relation_model.multiple { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} of {} cannot be set multiply", relation_model, obj_model).into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for rel_instance in instances { |
|
|
|
|
if let Some(related) = self.objects.get(&rel_instance.related) { |
|
|
|
|
if related.model != relation_model.related { |
|
|
|
|
return Err(StorageError::ConstraintViolation( |
|
|
|
|
format!("{} of {} requires object of type {}, got {}", |
|
|
|
|
relation_model, obj_model, |
|
|
|
|
self.describe_model(relation_model.related), |
|
|
|
|
self.describe_model(related.model)).into())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let relation_id = rel_instance.id.unwrap_or_else(|| next_id()); |
|
|
|
|
|
|
|
|
|
// Relations can have properties
|
|
|
|
|
let (ins, del) = find_values_to_change(rel_instance.values, relation_id, *relation_model_id)?; |
|
|
|
|
values_to_insert.extend(ins); |
|
|
|
|
value_ids_to_delete.extend(del); |
|
|
|
|
|
|
|
|
|
relations_to_insert.push(data::Relation { |
|
|
|
|
id: relation_id, |
|
|
|
|
object: updated_object_id, |
|
|
|
|
model: rel_instance.model, |
|
|
|
|
related: rel_instance.related, |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if !relation_model.optional { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("{} is required for {}", relation_model, obj_model).into())); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let obj_mut = self.objects.get_mut(&updated_object_id).unwrap(); |
|
|
|
|
obj_mut.name = updobj.name; |
|
|
|
|
|
|
|
|
|
for rel in relations_to_insert { |
|
|
|
|
self.relations.insert(rel.id, rel); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for value in values_to_insert { |
|
|
|
|
self.values.insert(value.id, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for id in value_ids_to_delete { |
|
|
|
|
self.values.remove(&id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for id in relations_to_delete { |
|
|
|
|
self.relations.remove(&id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update_object_model(&mut self, model : ObjectModel) -> Result<(), StorageError> { |
|
|
|
|
if model.name.is_empty() { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("Model name must not be empty.").into())); |
|
|
|
|
} |
|
|
|
@ -442,7 +588,7 @@ impl Storage { |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update_relation(&mut self, mut rel : RelationModel) -> Result<(), StorageError> { |
|
|
|
|
pub fn update_relation_model(&mut self, mut rel : RelationModel) -> Result<(), StorageError> { |
|
|
|
|
if rel.name.is_empty() || rel.reciprocal_name.is_empty() { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("Relation names must not be empty.").into())); |
|
|
|
|
} |
|
|
|
@ -475,7 +621,7 @@ impl Storage { |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update_property(&mut self, mut prop: PropertyModel) -> Result<(), StorageError> { |
|
|
|
|
pub fn update_property_model(&mut self, mut prop: PropertyModel) -> Result<(), StorageError> { |
|
|
|
|
if prop.name.is_empty() { |
|
|
|
|
return Err(StorageError::ConstraintViolation(format!("Property name must not be empty.").into())); |
|
|
|
|
} |
|
|
|
|