use crate::graph::{Id, Node, Relationship};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
#[derive(Debug)]
pub struct PropertyIndex {
index: HashMap<(String, Value), HashSet<Id>>,
indexed_properties: HashSet<String>,
}
impl PropertyIndex {
pub fn new() -> Self {
PropertyIndex {
index: HashMap::new(),
indexed_properties: HashSet::new(),
}
}
pub fn add_indexed_property(&mut self, property_key: String) {
self.indexed_properties.insert(property_key);
}
pub fn is_property_indexed(&self, property_key: &str) -> bool {
self.indexed_properties.contains(property_key)
}
pub fn get_indexed_properties(&self) -> &HashSet<String> {
&self.indexed_properties
}
pub fn add_entity(&mut self, entity_id: Id, property_key: &str, property_value: &Value) {
if !self.is_property_indexed(property_key) {
return; }
let key = (property_key.to_string(), property_value.clone());
self.index.entry(key).or_default().insert(entity_id);
}
pub fn remove_entity(&mut self, entity_id: Id, property_key: &str, property_value: &Value) {
let key = (property_key.to_string(), property_value.clone());
if let Some(entity_set) = self.index.get_mut(&key) {
entity_set.remove(&entity_id);
if entity_set.is_empty() {
self.index.remove(&key);
}
}
}
pub fn update_entity(
&mut self,
entity_id: Id,
property_key: &str,
old_value: Option<&Value>,
new_value: Option<&Value>,
) {
if !self.is_property_indexed(property_key) {
return;
}
if let Some(old_val) = old_value {
self.remove_entity(entity_id, property_key, old_val);
}
if let Some(new_val) = new_value {
self.add_entity(entity_id, property_key, new_val);
}
}
pub fn find_entities(&self, property_key: &str, property_value: &Value) -> Vec<Id> {
let key = (property_key.to_string(), property_value.clone());
self.index
.get(&key)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
}
pub fn find_entities_with_property(&self, property_key: &str) -> Vec<Id> {
let mut result = HashSet::new();
for ((key, _), entity_set) in &self.index {
if key == property_key {
result.extend(entity_set);
}
}
result.into_iter().collect()
}
pub fn get_property_values(&self, property_key: &str) -> Vec<Value> {
let mut values = Vec::new();
for (key, value) in self.index.keys() {
if key == property_key {
values.push(value.clone());
}
}
values
}
pub fn size(&self) -> usize {
self.index.len()
}
pub fn is_empty(&self) -> bool {
self.index.is_empty()
}
}
impl Default for PropertyIndex {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct RelationshipTypeIndex {
by_type: HashMap<String, HashSet<Id>>,
by_from_node: HashMap<(Id, String), HashSet<Id>>,
by_to_node: HashMap<(Id, String), HashSet<Id>>,
}
impl RelationshipTypeIndex {
pub fn new() -> Self {
RelationshipTypeIndex {
by_type: HashMap::new(),
by_from_node: HashMap::new(),
by_to_node: HashMap::new(),
}
}
pub fn add_relationship(&mut self, rel_id: Id, from_id: Id, to_id: Id, rel_type: &str) {
let rel_type = rel_type.to_string();
self.by_type
.entry(rel_type.clone())
.or_default()
.insert(rel_id);
self.by_from_node
.entry((from_id, rel_type.clone()))
.or_default()
.insert(rel_id);
self.by_to_node
.entry((to_id, rel_type))
.or_default()
.insert(rel_id);
}
pub fn remove_relationship(&mut self, rel_id: Id, from_id: Id, to_id: Id, rel_type: &str) {
let rel_type = rel_type.to_string();
if let Some(rel_set) = self.by_type.get_mut(&rel_type) {
rel_set.remove(&rel_id);
if rel_set.is_empty() {
self.by_type.remove(&rel_type);
}
}
let from_key = (from_id, rel_type.clone());
if let Some(rel_set) = self.by_from_node.get_mut(&from_key) {
rel_set.remove(&rel_id);
if rel_set.is_empty() {
self.by_from_node.remove(&from_key);
}
}
let to_key = (to_id, rel_type);
if let Some(rel_set) = self.by_to_node.get_mut(&to_key) {
rel_set.remove(&rel_id);
if rel_set.is_empty() {
self.by_to_node.remove(&to_key);
}
}
}
pub fn find_by_type(&self, rel_type: &str) -> Vec<Id> {
self.by_type
.get(rel_type)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
}
pub fn find_outgoing(&self, from_id: Id, rel_type: &str) -> Vec<Id> {
let key = (from_id, rel_type.to_string());
self.by_from_node
.get(&key)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
}
pub fn find_incoming(&self, to_id: Id, rel_type: &str) -> Vec<Id> {
let key = (to_id, rel_type.to_string());
self.by_to_node
.get(&key)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
}
}
impl Default for RelationshipTypeIndex {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct RangeIndex {
indexes: HashMap<String, HashMap<String, HashSet<Id>>>,
}
impl RangeIndex {
pub fn new() -> Self {
RangeIndex {
indexes: HashMap::new(),
}
}
pub fn add_entity(&mut self, entity_id: Id, property_key: &str, property_value: &Value) {
let string_value = property_value.to_string();
self.indexes
.entry(property_key.to_string())
.or_default()
.entry(string_value)
.or_default()
.insert(entity_id);
}
pub fn remove_entity(&mut self, entity_id: Id, property_key: &str, property_value: &Value) {
let string_value = property_value.to_string();
if let Some(map) = self.indexes.get_mut(property_key) {
if let Some(entity_set) = map.get_mut(&string_value) {
entity_set.remove(&entity_id);
if entity_set.is_empty() {
map.remove(&string_value);
}
}
if map.is_empty() {
self.indexes.remove(property_key);
}
}
}
pub fn find_entities_in_range(
&self,
property_key: &str,
min_value: &Value,
max_value: &Value,
) -> Vec<Id> {
let min_str = min_value.to_string();
let max_str = max_value.to_string();
if let Some(map) = self.indexes.get(property_key) {
let mut result = HashSet::new();
for (value_str, entity_set) in map {
if value_str >= &min_str && value_str <= &max_str {
result.extend(entity_set);
}
}
result.into_iter().collect()
} else {
Vec::new()
}
}
pub fn find_entities_greater_than(&self, property_key: &str, value: &Value) -> Vec<Id> {
let value_str = value.to_string();
if let Some(map) = self.indexes.get(property_key) {
let mut result = HashSet::new();
for (v_str, entity_set) in map {
if v_str > &value_str {
result.extend(entity_set);
}
}
result.into_iter().collect()
} else {
Vec::new()
}
}
pub fn find_entities_less_than(&self, property_key: &str, value: &Value) -> Vec<Id> {
let value_str = value.to_string();
if let Some(map) = self.indexes.get(property_key) {
let mut result = HashSet::new();
for (v_str, entity_set) in map {
if v_str < &value_str {
result.extend(entity_set);
}
}
result.into_iter().collect()
} else {
Vec::new()
}
}
}
impl Default for RangeIndex {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct CompositeIndex {
index: HashMap<Vec<(String, Value)>, HashSet<Id>>,
property_keys: Vec<String>,
}
impl CompositeIndex {
pub fn new(property_keys: Vec<String>) -> Self {
CompositeIndex {
index: HashMap::new(),
property_keys,
}
}
pub fn get_property_keys(&self) -> &[String] {
&self.property_keys
}
pub fn add_entity(&mut self, entity_id: Id, properties: &HashMap<String, Value>) {
let composite_key = self.build_composite_key(properties);
if composite_key.len() == self.property_keys.len() {
self.index
.entry(composite_key)
.or_default()
.insert(entity_id);
}
}
pub fn remove_entity(&mut self, entity_id: Id, properties: &HashMap<String, Value>) {
let composite_key = self.build_composite_key(properties);
if let Some(entity_set) = self.index.get_mut(&composite_key) {
entity_set.remove(&entity_id);
if entity_set.is_empty() {
self.index.remove(&composite_key);
}
}
}
pub fn find_entities(&self, query_properties: &HashMap<String, Value>) -> Vec<Id> {
let query_key = self.build_composite_key(query_properties);
if query_key.len() == self.property_keys.len() {
self.index
.get(&query_key)
.map(|set| set.iter().copied().collect())
.unwrap_or_default()
} else {
let mut result = HashSet::new();
for (key, entity_set) in &self.index {
if self.key_matches_query(key, &query_key) {
result.extend(entity_set);
}
}
result.into_iter().collect()
}
}
fn build_composite_key(&self, properties: &HashMap<String, Value>) -> Vec<(String, Value)> {
let mut key = Vec::new();
for prop_key in &self.property_keys {
if let Some(value) = properties.get(prop_key) {
key.push((prop_key.clone(), value.clone()));
}
}
key
}
fn key_matches_query(
&self,
index_key: &[(String, Value)],
query_key: &[(String, Value)],
) -> bool {
for (query_prop, query_value) in query_key {
let matches = index_key.iter().any(|(index_prop, index_value)| {
index_prop == query_prop && index_value == query_value
});
if !matches {
return false;
}
}
true
}
}
#[derive(Debug)]
pub struct IndexManager {
property_indexes: HashMap<String, PropertyIndex>,
range_indexes: HashMap<String, RangeIndex>,
composite_indexes: Vec<CompositeIndex>,
relationship_type_index: RelationshipTypeIndex,
}
impl IndexManager {
pub fn new() -> Self {
IndexManager {
property_indexes: HashMap::new(),
range_indexes: HashMap::new(),
composite_indexes: Vec::new(),
relationship_type_index: RelationshipTypeIndex::new(),
}
}
pub fn create_property_index(&mut self, property_key: String) {
let mut index = PropertyIndex::new();
index.add_indexed_property(property_key.clone());
self.property_indexes.insert(property_key, index);
}
pub fn create_range_index(&mut self, property_key: String) {
self.range_indexes.insert(property_key, RangeIndex::new());
}
pub fn create_composite_index(&mut self, property_keys: Vec<String>) {
self.composite_indexes
.push(CompositeIndex::new(property_keys));
}
pub fn add_node(&mut self, node: &Node) {
for (prop_key, prop_value) in &node.properties {
if let Some(index) = self.property_indexes.get_mut(prop_key) {
index.add_entity(node.id, prop_key, prop_value);
}
if let Some(index) = self.range_indexes.get_mut(prop_key) {
index.add_entity(node.id, prop_key, prop_value);
}
}
for composite_index in &mut self.composite_indexes {
composite_index.add_entity(node.id, &node.properties);
}
}
pub fn remove_node(&mut self, node: &Node) {
for (prop_key, prop_value) in &node.properties {
if let Some(index) = self.property_indexes.get_mut(prop_key) {
index.remove_entity(node.id, prop_key, prop_value);
}
if let Some(index) = self.range_indexes.get_mut(prop_key) {
index.remove_entity(node.id, prop_key, prop_value);
}
}
for composite_index in &mut self.composite_indexes {
composite_index.remove_entity(node.id, &node.properties);
}
}
pub fn add_relationship(&mut self, relationship: &Relationship) {
self.relationship_type_index.add_relationship(
relationship.id,
relationship.from_id,
relationship.to_id,
&relationship.rel_type,
);
for (prop_key, prop_value) in &relationship.properties {
if let Some(index) = self.property_indexes.get_mut(prop_key) {
index.add_entity(relationship.id, prop_key, prop_value);
}
if let Some(index) = self.range_indexes.get_mut(prop_key) {
index.add_entity(relationship.id, prop_key, prop_value);
}
}
for composite_index in &mut self.composite_indexes {
composite_index.add_entity(relationship.id, &relationship.properties);
}
}
pub fn remove_relationship(&mut self, relationship: &Relationship) {
self.relationship_type_index.remove_relationship(
relationship.id,
relationship.from_id,
relationship.to_id,
&relationship.rel_type,
);
for (prop_key, prop_value) in &relationship.properties {
if let Some(index) = self.property_indexes.get_mut(prop_key) {
index.remove_entity(relationship.id, prop_key, prop_value);
}
if let Some(index) = self.range_indexes.get_mut(prop_key) {
index.remove_entity(relationship.id, prop_key, prop_value);
}
}
for composite_index in &mut self.composite_indexes {
composite_index.remove_entity(relationship.id, &relationship.properties);
}
}
pub fn find_by_property(&self, property_key: &str, property_value: &Value) -> Vec<Id> {
if let Some(index) = self.property_indexes.get(property_key) {
index.find_entities(property_key, property_value)
} else {
Vec::new()
}
}
pub fn find_in_range(
&self,
property_key: &str,
min_value: &Value,
max_value: &Value,
) -> Vec<Id> {
if let Some(index) = self.range_indexes.get(property_key) {
index.find_entities_in_range(property_key, min_value, max_value)
} else {
Vec::new()
}
}
pub fn find_relationships_by_type(&self, rel_type: &str) -> Vec<Id> {
self.relationship_type_index.find_by_type(rel_type)
}
pub fn find_outgoing_relationships(&self, from_id: Id, rel_type: &str) -> Vec<Id> {
self.relationship_type_index
.find_outgoing(from_id, rel_type)
}
pub fn find_incoming_relationships(&self, to_id: Id, rel_type: &str) -> Vec<Id> {
self.relationship_type_index.find_incoming(to_id, rel_type)
}
pub fn get_index_stats(&self) -> IndexStats {
IndexStats {
property_index_count: self.property_indexes.len(),
range_index_count: self.range_indexes.len(),
composite_index_count: self.composite_indexes.len(),
total_indexed_entities: self.calculate_total_indexed_entities(),
}
}
fn calculate_total_indexed_entities(&self) -> usize {
let mut total = 0;
for index in self.property_indexes.values() {
total += index.size();
}
total
}
}
impl Default for IndexManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct IndexStats {
pub property_index_count: usize,
pub range_index_count: usize,
pub composite_index_count: usize,
pub total_indexed_entities: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_property_index() {
let mut index = PropertyIndex::new();
index.add_indexed_property("name".to_string());
index.add_indexed_property("age".to_string());
index.add_entity(1, "name", &json!("Alice"));
index.add_entity(2, "name", &json!("Bob"));
index.add_entity(3, "name", &json!("Alice"));
index.add_entity(4, "age", &json!(30));
let alice_entities = index.find_entities("name", &json!("Alice"));
assert_eq!(alice_entities.len(), 2);
assert!(alice_entities.contains(&1));
assert!(alice_entities.contains(&3));
let bob_entities = index.find_entities("name", &json!("Bob"));
assert_eq!(bob_entities.len(), 1);
assert!(bob_entities.contains(&2));
let name_entities = index.find_entities_with_property("name");
assert_eq!(name_entities.len(), 3);
index.remove_entity(1, "name", &json!("Alice"));
let alice_entities = index.find_entities("name", &json!("Alice"));
assert_eq!(alice_entities.len(), 1);
assert!(alice_entities.contains(&3));
}
#[test]
fn test_range_index() {
let mut index = RangeIndex::new();
index.add_entity(1, "age", &json!(25));
index.add_entity(2, "age", &json!(30));
index.add_entity(3, "age", &json!(35));
index.add_entity(4, "age", &json!(40));
let entities_gt = index.find_entities_greater_than("age", &json!(30));
let _ = entities_gt.len(); }
#[test]
fn test_composite_index() {
let mut index = CompositeIndex::new(vec!["name".to_string(), "age".to_string()]);
let mut props1 = HashMap::new();
props1.insert("name".to_string(), json!("Alice"));
props1.insert("age".to_string(), json!(30));
let mut props2 = HashMap::new();
props2.insert("name".to_string(), json!("Bob"));
props2.insert("age".to_string(), json!(25));
let mut props3 = HashMap::new();
props3.insert("name".to_string(), json!("Alice"));
props3.insert("age".to_string(), json!(35));
index.add_entity(1, &props1);
index.add_entity(2, &props2);
index.add_entity(3, &props3);
let exact_match = index.find_entities(&props1);
assert_eq!(exact_match.len(), 1);
assert!(exact_match.contains(&1));
let mut partial_query = HashMap::new();
partial_query.insert("name".to_string(), json!("Alice"));
let partial_match = index.find_entities(&partial_query);
assert_eq!(partial_match.len(), 2);
assert!(partial_match.contains(&1));
assert!(partial_match.contains(&3));
}
#[test]
fn test_relationship_type_index() {
let mut index = RelationshipTypeIndex::new();
index.add_relationship(1, 10, 20, "KNOWS");
index.add_relationship(2, 10, 30, "KNOWS");
index.add_relationship(3, 20, 30, "LIKES");
let knows_rels = index.find_by_type("KNOWS");
assert_eq!(knows_rels.len(), 2);
assert!(knows_rels.contains(&1));
assert!(knows_rels.contains(&2));
let likes_rels = index.find_by_type("LIKES");
assert_eq!(likes_rels.len(), 1);
assert!(likes_rels.contains(&3));
let outgoing_from_10 = index.find_outgoing(10, "KNOWS");
assert_eq!(outgoing_from_10.len(), 2);
let outgoing_from_20 = index.find_outgoing(20, "KNOWS");
assert_eq!(outgoing_from_20.len(), 0);
let incoming_to_30 = index.find_incoming(30, "KNOWS");
assert_eq!(incoming_to_30.len(), 1);
assert!(incoming_to_30.contains(&2));
}
}
#[cfg(test)]
mod index_manager_tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
#[test]
fn test_index_manager_node_operations() {
let mut manager = IndexManager::new();
manager.create_property_index("name".to_string());
manager.create_range_index("age".to_string());
let mut properties = HashMap::new();
properties.insert("name".to_string(), json!("Alice"));
properties.insert("age".to_string(), json!(30));
let node = Node::new(1, properties);
manager.add_node(&node);
let found_entities = manager.find_by_property("name", &json!("Alice"));
assert_eq!(found_entities.len(), 1);
assert!(found_entities.contains(&1));
let range_entities = manager.find_in_range("age", &json!(25), &json!(35));
let _ = range_entities.len(); }
#[test]
fn test_index_manager_relationship_operations() {
let mut manager = IndexManager::new();
let relationship = Relationship::new(1, 10, 20, "KNOWS".to_string(), HashMap::new());
manager.add_relationship(&relationship);
let found_rels = manager.find_relationships_by_type("KNOWS");
assert_eq!(found_rels.len(), 1);
assert!(found_rels.contains(&1));
let outgoing = manager.find_outgoing_relationships(10, "KNOWS");
assert_eq!(outgoing.len(), 1);
assert!(outgoing.contains(&1));
}
#[test]
fn test_index_stats() {
let mut manager = IndexManager::new();
manager.create_property_index("name".to_string());
manager.create_range_index("age".to_string());
let stats = manager.get_index_stats();
assert_eq!(stats.property_index_count, 1);
assert_eq!(stats.range_index_count, 1);
assert_eq!(stats.composite_index_count, 0);
}
}