grafeo_core/graph/lpg/store/
index.rs1use super::LpgStore;
4use dashmap::DashMap;
5use grafeo_common::types::{HashableValue, NodeId, PropertyKey, Value};
6use grafeo_common::utils::hash::FxHashSet;
7use parking_lot::RwLock;
8use std::sync::Arc;
9
10#[cfg(feature = "vector-index")]
11use crate::index::vector::HnswIndex;
12
13impl LpgStore {
14 pub fn create_property_index(&self, property: &str) {
40 let key = PropertyKey::new(property);
41
42 let mut indexes = self.property_indexes.write();
43 if indexes.contains_key(&key) {
44 return; }
46
47 let index: DashMap<HashableValue, FxHashSet<NodeId>> = DashMap::new();
49
50 for node_id in self.node_ids() {
52 if let Some(value) = self.node_properties.get(node_id, &key) {
53 let hv = HashableValue::new(value);
54 index.entry(hv).or_default().insert(node_id);
55 }
56 }
57
58 indexes.insert(key, index);
59 }
60
61 pub fn drop_property_index(&self, property: &str) -> bool {
65 let key = PropertyKey::new(property);
66 self.property_indexes.write().remove(&key).is_some()
67 }
68
69 #[must_use]
71 pub fn has_property_index(&self, property: &str) -> bool {
72 let key = PropertyKey::new(property);
73 self.property_indexes.read().contains_key(&key)
74 }
75
76 pub(super) fn update_property_index_on_set(
78 &self,
79 node_id: NodeId,
80 key: &PropertyKey,
81 new_value: &Value,
82 ) {
83 let indexes = self.property_indexes.read();
84 if let Some(index) = indexes.get(key) {
85 if let Some(old_value) = self.node_properties.get(node_id, key) {
87 let old_hv = HashableValue::new(old_value);
88 if let Some(mut nodes) = index.get_mut(&old_hv) {
89 nodes.remove(&node_id);
90 if nodes.is_empty() {
91 drop(nodes);
92 index.remove(&old_hv);
93 }
94 }
95 }
96
97 let new_hv = HashableValue::new(new_value.clone());
99 index
100 .entry(new_hv)
101 .or_insert_with(FxHashSet::default)
102 .insert(node_id);
103 }
104 }
105
106 pub(super) fn update_property_index_on_remove(&self, node_id: NodeId, key: &PropertyKey) {
108 let indexes = self.property_indexes.read();
109 if let Some(index) = indexes.get(key) {
110 if let Some(old_value) = self.node_properties.get(node_id, key) {
112 let old_hv = HashableValue::new(old_value);
113 if let Some(mut nodes) = index.get_mut(&old_hv) {
114 nodes.remove(&node_id);
115 if nodes.is_empty() {
116 drop(nodes);
117 index.remove(&old_hv);
118 }
119 }
120 }
121 }
122 }
123
124 #[cfg(feature = "vector-index")]
126 pub fn add_vector_index(&self, label: &str, property: &str, index: Arc<HnswIndex>) {
127 let key = format!("{label}:{property}");
128 self.vector_indexes.write().insert(key, index);
129 }
130
131 #[cfg(feature = "vector-index")]
133 #[must_use]
134 pub fn get_vector_index(&self, label: &str, property: &str) -> Option<Arc<HnswIndex>> {
135 let key = format!("{label}:{property}");
136 self.vector_indexes.read().get(&key).cloned()
137 }
138
139 #[cfg(feature = "vector-index")]
143 pub fn remove_vector_index(&self, label: &str, property: &str) -> bool {
144 let key = format!("{label}:{property}");
145 self.vector_indexes.write().remove(&key).is_some()
146 }
147
148 #[cfg(feature = "vector-index")]
152 #[must_use]
153 pub fn vector_index_entries(&self) -> Vec<(String, Arc<HnswIndex>)> {
154 self.vector_indexes
155 .read()
156 .iter()
157 .map(|(k, v)| (k.clone(), v.clone()))
158 .collect()
159 }
160
161 #[cfg(feature = "text-index")]
163 pub fn add_text_index(
164 &self,
165 label: &str,
166 property: &str,
167 index: Arc<RwLock<crate::index::text::InvertedIndex>>,
168 ) {
169 let key = format!("{label}:{property}");
170 self.text_indexes.write().insert(key, index);
171 }
172
173 #[cfg(feature = "text-index")]
175 #[must_use]
176 pub fn get_text_index(
177 &self,
178 label: &str,
179 property: &str,
180 ) -> Option<Arc<RwLock<crate::index::text::InvertedIndex>>> {
181 let key = format!("{label}:{property}");
182 self.text_indexes.read().get(&key).cloned()
183 }
184
185 #[cfg(feature = "text-index")]
189 pub fn remove_text_index(&self, label: &str, property: &str) -> bool {
190 let key = format!("{label}:{property}");
191 self.text_indexes.write().remove(&key).is_some()
192 }
193
194 #[cfg(feature = "text-index")]
198 pub fn text_index_entries(
199 &self,
200 ) -> Vec<(String, Arc<RwLock<crate::index::text::InvertedIndex>>)> {
201 self.text_indexes
202 .read()
203 .iter()
204 .map(|(k, v)| (k.clone(), v.clone()))
205 .collect()
206 }
207
208 #[cfg(feature = "text-index")]
213 pub(super) fn update_text_index_on_set(&self, id: NodeId, key: &str, value: &Value) {
214 let text_indexes = self.text_indexes.read();
215 if text_indexes.is_empty() {
216 return;
217 }
218 let id_to_label = self.id_to_label.read();
219 let node_labels = self.node_labels.read();
220 if let Some(label_ids) = node_labels.get(&id) {
221 for &label_id in label_ids {
222 if let Some(label_name) = id_to_label.get(label_id as usize) {
223 let index_key = format!("{label_name}:{key}");
224 if let Some(index) = text_indexes.get(&index_key) {
225 let mut idx = index.write();
226 idx.remove(id);
228 if let Value::String(text) = value {
229 idx.insert(id, text);
230 }
231 }
232 }
233 }
234 }
235 }
236
237 #[cfg(feature = "text-index")]
239 pub(super) fn update_text_index_on_remove(&self, id: NodeId, key: &str) {
240 let text_indexes = self.text_indexes.read();
241 if text_indexes.is_empty() {
242 return;
243 }
244 let id_to_label = self.id_to_label.read();
245 let node_labels = self.node_labels.read();
246 if let Some(label_ids) = node_labels.get(&id) {
247 for &label_id in label_ids {
248 if let Some(label_name) = id_to_label.get(label_id as usize) {
249 let index_key = format!("{label_name}:{key}");
250 if let Some(index) = text_indexes.get(&index_key) {
251 index.write().remove(id);
252 }
253 }
254 }
255 }
256 }
257
258 #[cfg(feature = "text-index")]
260 pub(super) fn remove_from_all_text_indexes(&self, id: NodeId) {
261 let text_indexes = self.text_indexes.read();
262 if text_indexes.is_empty() {
263 return;
264 }
265 for (_, index) in text_indexes.iter() {
266 index.write().remove(id);
267 }
268 }
269}