use crate::Bound;
use crate::pldag::Node;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
pub trait KeyValueStore: Send + Sync {
fn get_all(&self) -> HashMap<String, Value>;
fn get(&self, key: &str) -> Option<Value>;
fn set(&self, key: &str, value: Value);
fn mset(&self, kv_pairs: &[(String, Value)]);
fn exists(&self, key: &str) -> bool;
fn keys(&self) -> Vec<String>;
fn mget(&self, keys: &[String]) -> HashMap<String, Value>;
fn delete(&self, key: &str);
fn get_prefix(&self, prefix: &str) -> HashMap<String, Value>;
}
pub trait NodeStoreTrait: Send + Sync {
fn get_all_nodes(&self) -> HashMap<String, Node>;
fn get_nodes(&self, ids: &[String]) -> HashMap<String, Node>;
fn set_node(&self, id: &str, node: Node);
fn set_primitives(&self, primitives: &[(&str, &Bound)]);
fn node_exists(&self, id: &str) -> bool;
fn node_ids(&self) -> Vec<String>;
fn delete(&self, id: &str);
fn get_parent_ids(&self, ids: &[String]) -> HashMap<String, Vec<String>>;
fn get_children_ids(&self, ids: &[String]) -> HashMap<String, Vec<String>>;
fn get_kv_store(&self) -> &dyn KeyValueStore;
}
pub struct NodeStore {
data: Arc<dyn KeyValueStore>,
}
impl NodeStore {
pub fn new(store: Arc<dyn KeyValueStore>) -> Self {
Self { data: store }
}
pub fn store(&self) -> &dyn KeyValueStore {
&*self.data
}
}
impl NodeStoreTrait for NodeStore {
fn get_all_nodes(&self) -> HashMap<String, Node> {
self.data
.get_all()
.into_iter()
.filter_map(|(id, value)| {
serde_json::from_value::<Node>(value)
.ok()
.map(|node| (id, node))
})
.collect()
}
fn get_nodes(&self, ids: &[String]) -> HashMap<String, Node> {
self.data
.mget(&ids.iter().map(|s| s.to_string()).collect::<Vec<String>>())
.into_iter()
.filter_map(|(id, value)| {
serde_json::from_value::<Node>(value)
.ok()
.map(|node| (id.clone(), node))
})
.collect()
}
fn set_node(&self, id: &str, node: Node) {
match node {
Node::Primitive(p) => {
let value = serde_json::to_value(Node::Primitive(p)).unwrap();
self.data.set(id, value);
}
Node::Composite(c) => {
let value = serde_json::to_value(&Node::Composite(c.clone())).unwrap();
self.data.set(id, value);
let coef_ids: Vec<String> = c
.coefficients
.iter()
.map(|(coef_id, _)| coef_id.to_string())
.collect();
let mut coefficient_current_references = self.get_parent_ids(&coef_ids);
for (coef_id, current_references) in coefficient_current_references.iter_mut() {
if !current_references.contains(&id.to_string()) {
current_references.push(id.to_string());
self.data.set(
&format!("__outgoing__{}", coef_id),
serde_json::to_value(current_references).unwrap(),
);
}
}
}
}
}
fn set_primitives(&self, primitives: &[(&str, &Bound)]) {
let kv_pairs = primitives
.into_iter()
.map(|(id, &bound)| {
(
id.to_string(),
serde_json::to_value(Node::Primitive(bound.clone())).unwrap(),
)
})
.collect::<Vec<(String, serde_json::Value)>>();
self.data.mset(&kv_pairs);
}
fn node_exists(&self, id: &str) -> bool {
self.data.exists(id)
}
fn node_ids(&self) -> Vec<String> {
self.data
.keys()
.iter()
.filter(|key| !key.starts_with("__outgoing__"))
.cloned()
.collect()
}
fn delete(&self, id: &str) {
if let Some(node_value) = self.data.get(id) {
if let Ok(node) = serde_json::from_value::<Node>(node_value) {
if let Node::Composite(c) = node {
for (coef_id, _) in c.coefficients {
let mut current_references = self
.get_parent_ids(&[coef_id.clone()])
.get(&coef_id)
.cloned()
.unwrap_or_else(Vec::new);
current_references.retain(|ref_id| ref_id != id);
self.data.set(
&format!("__outgoing__{}", coef_id),
serde_json::to_value(current_references).unwrap(),
);
}
}
}
self.data.delete(id);
}
}
fn get_parent_ids(&self, ids: &[String]) -> HashMap<String, Vec<String>> {
let mut result = self
.data
.mget(
&ids.iter()
.map(|id| format!("__outgoing__{}", id))
.collect::<Vec<String>>(),
)
.into_iter()
.map(|(id, refs)| {
(
id["__outgoing__".len()..].to_string(),
serde_json::from_value(refs).unwrap_or_else(|_| Vec::new()),
)
})
.collect::<HashMap<String, Vec<String>>>();
ids.iter().for_each(|id| {
if !result.contains_key(id) {
result.insert(id.clone(), Vec::new());
}
});
result
}
fn get_children_ids(&self, ids: &[String]) -> HashMap<String, Vec<String>> {
self.data
.mget(ids)
.into_iter()
.map(|(id, val)| {
(
id,
match serde_json::from_value::<Node>(val) {
Ok(Node::Composite(c)) => c
.coefficients
.iter()
.map(|(child_id, _)| child_id.clone())
.collect(),
_ => Vec::new(),
},
)
})
.collect::<HashMap<String, Vec<String>>>()
}
fn get_kv_store(&self) -> &dyn KeyValueStore {
&*self.data
}
}
pub struct InMemoryStore {
data: RwLock<HashMap<String, Value>>,
}
impl InMemoryStore {
pub fn new() -> Self {
Self {
data: RwLock::new(HashMap::new()),
}
}
}
impl KeyValueStore for InMemoryStore {
fn get_all(&self) -> HashMap<String, Value> {
let data = self.data.read().unwrap();
data.clone()
}
fn get(&self, key: &str) -> Option<Value> {
let data = self.data.read().unwrap();
data.get(key).cloned()
}
fn set(&self, key: &str, value: Value) {
let mut data = self.data.write().unwrap();
data.insert(key.to_string(), value);
}
fn mset(&self, kv_pairs: &[(String, Value)]) {
let mut data = self.data.write().unwrap();
for (key, value) in kv_pairs {
data.insert(key.to_string(), value.clone());
}
}
fn exists(&self, key: &str) -> bool {
let data = self.data.read().unwrap();
data.contains_key(key)
}
fn keys(&self) -> Vec<String> {
let data = self.data.read().unwrap();
data.keys().cloned().collect()
}
fn mget(&self, keys: &[String]) -> HashMap<String, Value> {
let data = self.data.read().unwrap();
let mut result = HashMap::with_capacity(keys.len());
for key in keys {
if let Some(value) = data.get(key) {
result.insert(key.clone(), value.clone());
}
}
result
}
fn delete(&self, key: &str) {
let mut data = self.data.write().unwrap();
data.remove(key);
}
fn get_prefix(&self, prefix: &str) -> HashMap<String, Value> {
let data = self.data.read().unwrap();
data.iter()
.filter(|(key, _)| key.starts_with(prefix))
.map(|(key, value)| (key.clone(), value.clone()))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pldag::{Constraint, Node};
#[test]
fn test_delete_removes_backward_references() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("child1", Node::Primitive((0, 1)));
node_store.set_node("child2", Node::Primitive((0, 1)));
let parent = Node::Composite(Constraint {
coefficients: vec![("child1".to_string(), 2), ("child2".to_string(), 3)],
bias: (0, 0),
});
node_store.set_node("parent", parent);
let parent_ids = node_store.get_parent_ids(&["child1".to_string(), "child2".to_string()]);
assert_eq!(
parent_ids.get("child1").unwrap(),
&vec!["parent".to_string()]
);
assert_eq!(
parent_ids.get("child2").unwrap(),
&vec!["parent".to_string()]
);
node_store.delete("parent");
assert!(!node_store.node_exists("parent"));
let parent_ids_after =
node_store.get_parent_ids(&["child1".to_string(), "child2".to_string()]);
assert_eq!(
parent_ids_after.get("child1").unwrap(),
&Vec::<String>::new()
);
assert_eq!(
parent_ids_after.get("child2").unwrap(),
&Vec::<String>::new()
);
assert!(node_store.node_exists("child1"));
assert!(node_store.node_exists("child2"));
}
#[test]
fn test_delete_with_multiple_parents() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("child", Node::Primitive((0, 1)));
let parent1 = Node::Composite(Constraint {
coefficients: vec![("child".to_string(), 1)],
bias: (0, 0),
});
let parent2 = Node::Composite(Constraint {
coefficients: vec![("child".to_string(), 2)],
bias: (0, 0),
});
node_store.set_node("parent1", parent1);
node_store.set_node("parent2", parent2);
let parent_ids = node_store.get_parent_ids(&["child".to_string()]);
let mut parents = parent_ids.get("child").unwrap().clone();
parents.sort();
assert_eq!(parents, vec!["parent1".to_string(), "parent2".to_string()]);
node_store.delete("parent1");
let parent_ids_after = node_store.get_parent_ids(&["child".to_string()]);
assert_eq!(
parent_ids_after.get("child").unwrap(),
&vec!["parent2".to_string()]
);
assert!(!node_store.node_exists("parent1"));
assert!(node_store.node_exists("parent2"));
}
#[test]
fn test_delete_primitive_node() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("prim", Node::Primitive((0, 10)));
assert!(node_store.node_exists("prim"));
node_store.delete("prim");
assert!(!node_store.node_exists("prim"));
}
#[test]
fn test_delete_nonexistent_node() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.delete("nonexistent");
}
#[test]
fn test_set_and_get_nodes() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("prim1", Node::Primitive((0, 5)));
node_store.set_node("prim2", Node::Primitive((-10, 10)));
let composite = Node::Composite(Constraint {
coefficients: vec![("prim1".to_string(), 2), ("prim2".to_string(), -1)],
bias: (3, 3),
});
node_store.set_node("comp1", composite.clone());
let nodes = node_store.get_nodes(&[
"prim1".to_string(),
"prim2".to_string(),
"comp1".to_string(),
]);
assert_eq!(nodes.len(), 3);
assert_eq!(nodes.get("prim1").unwrap(), &Node::Primitive((0, 5)));
assert_eq!(nodes.get("prim2").unwrap(), &Node::Primitive((-10, 10)));
assert_eq!(nodes.get("comp1").unwrap(), &composite);
let nodes = node_store.get_nodes(&["prim1".to_string(), "nonexistent".to_string()]);
assert_eq!(nodes.len(), 1);
assert!(nodes.contains_key("prim1"));
assert!(!nodes.contains_key("nonexistent"));
}
#[test]
fn test_get_all_nodes() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
let all_nodes = node_store.get_all_nodes();
assert_eq!(all_nodes.len(), 0);
node_store.set_node("a", Node::Primitive((0, 1)));
node_store.set_node("b", Node::Primitive((0, 2)));
node_store.set_node(
"c",
Node::Composite(Constraint {
coefficients: vec![("a".to_string(), 1)],
bias: (0, 0),
}),
);
let all_nodes = node_store.get_all_nodes();
assert_eq!(all_nodes.len(), 3);
assert!(all_nodes.contains_key("a"));
assert!(all_nodes.contains_key("b"));
assert!(all_nodes.contains_key("c"));
assert!(!all_nodes.iter().any(|(k, _)| k.starts_with("__outgoing__")));
}
#[test]
fn test_node_exists() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
assert!(!node_store.node_exists("test"));
node_store.set_node("test", Node::Primitive((0, 1)));
assert!(node_store.node_exists("test"));
node_store.delete("test");
assert!(!node_store.node_exists("test"));
}
#[test]
fn test_node_ids() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
let ids = node_store.node_ids();
assert_eq!(ids.len(), 0);
node_store.set_node("node1", Node::Primitive((0, 1)));
node_store.set_node("node2", Node::Primitive((0, 2)));
node_store.set_node(
"parent",
Node::Composite(Constraint {
coefficients: vec![("node1".to_string(), 1), ("node2".to_string(), 2)],
bias: (0, 0),
}),
);
let mut ids = node_store.node_ids();
ids.sort();
assert_eq!(ids, vec!["node1", "node2", "parent"]);
assert!(!ids.iter().any(|id| id.starts_with("__outgoing__")));
}
#[test]
fn test_get_parent_ids() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("child1", Node::Primitive((0, 1)));
node_store.set_node("child2", Node::Primitive((0, 1)));
node_store.set_node("child3", Node::Primitive((0, 1)));
let parent_ids = node_store.get_parent_ids(&["child1".to_string()]);
assert_eq!(parent_ids.get("child1").unwrap(), &Vec::<String>::new());
node_store.set_node(
"parent1",
Node::Composite(Constraint {
coefficients: vec![("child1".to_string(), 1), ("child2".to_string(), 2)],
bias: (0, 0),
}),
);
node_store.set_node(
"parent2",
Node::Composite(Constraint {
coefficients: vec![("child1".to_string(), 3), ("child3".to_string(), 4)],
bias: (0, 0),
}),
);
let parent_ids = node_store.get_parent_ids(&[
"child1".to_string(),
"child2".to_string(),
"child3".to_string(),
]);
let mut child1_parents = parent_ids.get("child1").unwrap().clone();
child1_parents.sort();
assert_eq!(child1_parents, vec!["parent1", "parent2"]);
assert_eq!(parent_ids.get("child2").unwrap(), &vec!["parent1"]);
assert_eq!(parent_ids.get("child3").unwrap(), &vec!["parent2"]);
let parent_ids = node_store.get_parent_ids(&["nonexistent".to_string()]);
assert_eq!(
parent_ids.get("nonexistent").unwrap(),
&Vec::<String>::new()
);
}
#[test]
fn test_get_children_ids() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("prim", Node::Primitive((0, 1)));
node_store.set_node("child1", Node::Primitive((0, 1)));
node_store.set_node("child2", Node::Primitive((0, 1)));
node_store.set_node("child3", Node::Primitive((0, 1)));
node_store.set_node(
"parent1",
Node::Composite(Constraint {
coefficients: vec![("child1".to_string(), 1), ("child2".to_string(), 2)],
bias: (0, 0),
}),
);
node_store.set_node(
"parent2",
Node::Composite(Constraint {
coefficients: vec![("child3".to_string(), 1)],
bias: (0, 0),
}),
);
let children_map = node_store.get_children_ids(&[
"prim".to_string(),
"parent1".to_string(),
"parent2".to_string(),
]);
assert_eq!(children_map.get("prim").unwrap(), &Vec::<String>::new());
assert_eq!(
children_map.get("parent1").unwrap(),
&vec!["child1", "child2"]
);
assert_eq!(children_map.get("parent2").unwrap(), &vec!["child3"]);
let children_map = node_store.get_children_ids(&["nonexistent".to_string()]);
assert_eq!(children_map.len(), 0);
}
#[test]
fn test_set_node_updates_backward_references() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("child", Node::Primitive((0, 1)));
node_store.set_node(
"parent",
Node::Composite(Constraint {
coefficients: vec![("child".to_string(), 1)],
bias: (0, 0),
}),
);
let parent_ids = node_store.get_parent_ids(&["child".to_string()]);
assert_eq!(parent_ids.get("child").unwrap(), &vec!["parent"]);
node_store.set_node("child2", Node::Primitive((0, 1)));
node_store.set_node(
"parent",
Node::Composite(Constraint {
coefficients: vec![("child".to_string(), 1), ("child2".to_string(), 2)],
bias: (0, 0),
}),
);
let parent_ids = node_store.get_parent_ids(&["child".to_string(), "child2".to_string()]);
assert_eq!(parent_ids.get("child").unwrap(), &vec!["parent"]);
assert_eq!(parent_ids.get("child2").unwrap(), &vec!["parent"]);
}
#[test]
fn test_set_node_does_not_duplicate_references() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
node_store.set_node("child", Node::Primitive((0, 1)));
let composite = Node::Composite(Constraint {
coefficients: vec![("child".to_string(), 1)],
bias: (0, 0),
});
node_store.set_node("parent", composite.clone());
node_store.set_node("parent", composite.clone());
node_store.set_node("parent", composite.clone());
let parent_ids = node_store.get_parent_ids(&["child".to_string()]);
assert_eq!(parent_ids.get("child").unwrap(), &vec!["parent"]);
}
#[test]
fn test_get_kv_store() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store.clone());
let kv_store = node_store.get_kv_store();
kv_store.set("test_key", serde_json::json!("test_value"));
let value = kv_store.get("test_key");
assert_eq!(value, Some(serde_json::json!("test_value")));
}
#[test]
fn test_store_method() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store.clone());
let kv_store = node_store.store();
kv_store.set("key", serde_json::json!(42));
assert_eq!(kv_store.get("key"), Some(serde_json::json!(42)));
}
#[test]
fn test_set_primitives() {
let store = Arc::new(InMemoryStore::new());
let node_store = NodeStore::new(store);
let primitives: Vec<(&str, Bound)> = vec![
("prim1", (0, 1)),
("prim2", (-5, 5)),
("prim3", (10, 20)),
];
let primitives_ref: Vec<(&str, &Bound)> = primitives.iter().map(|(s, b)| (*s, b)).collect();
node_store.set_primitives(&primitives_ref);
let nodes = node_store.get_nodes(&["prim1".to_string(), "prim2".to_string(), "prim3".to_string()]);
assert_eq!(nodes.len(), 3);
assert_eq!(nodes.get("prim1").unwrap(), &Node::Primitive((0, 1)));
assert_eq!(nodes.get("prim2").unwrap(), &Node::Primitive((-5, 5)));
assert_eq!(nodes.get("prim3").unwrap(), &Node::Primitive((10, 20)));
let all_nodes = node_store.get_all_nodes();
assert_eq!(all_nodes.len(), 3);
assert_eq!(all_nodes.get("prim1").unwrap(), &Node::Primitive((0, 1)));
}
}