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;
7#[cfg(feature = "text-index")]
8use parking_lot::RwLock;
9#[cfg(any(feature = "vector-index", feature = "text-index"))]
10use std::sync::Arc;
11
12#[cfg(feature = "vector-index")]
13use crate::index::vector::VectorIndexKind;
14
15impl LpgStore {
16 pub fn create_property_index(&self, property: &str) {
42 let key = PropertyKey::new(property);
43
44 let mut indexes = self.property_indexes.write();
45 if indexes.contains_key(&key) {
46 return; }
48
49 let index: DashMap<HashableValue, FxHashSet<NodeId>> = DashMap::new();
51
52 for node_id in self.node_ids() {
54 if let Some(value) = self.node_properties.get(node_id, &key) {
55 let hv = HashableValue::new(value);
56 index.entry(hv).or_default().insert(node_id);
57 }
58 }
59
60 indexes.insert(key, index);
61 }
62
63 pub fn drop_property_index(&self, property: &str) -> bool {
67 let key = PropertyKey::new(property);
68 self.property_indexes.write().remove(&key).is_some()
69 }
70
71 #[must_use]
73 pub fn has_property_index(&self, property: &str) -> bool {
74 let key = PropertyKey::new(property);
75 self.property_indexes.read().contains_key(&key)
76 }
77
78 #[must_use]
80 pub fn property_index_keys(&self) -> Vec<String> {
81 self.property_indexes
82 .read()
83 .keys()
84 .map(|k| k.to_string())
85 .collect()
86 }
87
88 pub(super) fn update_property_index_on_set(
90 &self,
91 node_id: NodeId,
92 key: &PropertyKey,
93 new_value: &Value,
94 ) {
95 let indexes = self.property_indexes.read();
96 if let Some(index) = indexes.get(key) {
97 if let Some(old_value) = self.node_properties.get(node_id, key) {
99 let old_hv = HashableValue::new(old_value);
100 if let Some(mut nodes) = index.get_mut(&old_hv) {
101 nodes.remove(&node_id);
102 if nodes.is_empty() {
103 drop(nodes);
104 index.remove(&old_hv);
105 }
106 }
107 }
108
109 let new_hv = HashableValue::new(new_value.clone());
111 index
112 .entry(new_hv)
113 .or_insert_with(FxHashSet::default)
114 .insert(node_id);
115 }
116 }
117
118 pub(super) fn update_property_index_on_remove(&self, node_id: NodeId, key: &PropertyKey) {
120 let indexes = self.property_indexes.read();
121 if let Some(index) = indexes.get(key) {
122 if let Some(old_value) = self.node_properties.get(node_id, key) {
124 let old_hv = HashableValue::new(old_value);
125 if let Some(mut nodes) = index.get_mut(&old_hv) {
126 nodes.remove(&node_id);
127 if nodes.is_empty() {
128 drop(nodes);
129 index.remove(&old_hv);
130 }
131 }
132 }
133 }
134 }
135
136 #[cfg(feature = "vector-index")]
138 pub fn add_vector_index(&self, label: &str, property: &str, index: Arc<VectorIndexKind>) {
139 let key = format!("{label}:{property}");
140 self.vector_indexes.write().insert(key, index);
141 }
142
143 #[cfg(feature = "vector-index")]
145 #[must_use]
146 pub fn get_vector_index(&self, label: &str, property: &str) -> Option<Arc<VectorIndexKind>> {
147 let key = format!("{label}:{property}");
148 self.vector_indexes.read().get(&key).cloned()
149 }
150
151 #[cfg(feature = "vector-index")]
155 pub fn remove_vector_index(&self, label: &str, property: &str) -> bool {
156 let key = format!("{label}:{property}");
157 self.vector_indexes.write().remove(&key).is_some()
158 }
159
160 #[cfg(feature = "vector-index")]
164 #[must_use]
165 pub fn vector_index_entries(&self) -> Vec<(String, Arc<VectorIndexKind>)> {
166 self.vector_indexes
167 .read()
168 .iter()
169 .map(|(k, v)| (k.clone(), v.clone()))
170 .collect()
171 }
172
173 #[cfg(feature = "vector-index")]
175 #[must_use]
176 pub fn get_vector_index_by_key(&self, key: &str) -> Option<Arc<VectorIndexKind>> {
177 self.vector_indexes.read().get(key).cloned()
178 }
179
180 #[cfg(feature = "text-index")]
182 pub fn add_text_index(
183 &self,
184 label: &str,
185 property: &str,
186 index: Arc<RwLock<crate::index::text::InvertedIndex>>,
187 ) {
188 let key = format!("{label}:{property}");
189 self.text_indexes.write().insert(key, index);
190 }
191
192 #[cfg(feature = "text-index")]
194 #[must_use]
195 pub fn get_text_index(
196 &self,
197 label: &str,
198 property: &str,
199 ) -> Option<Arc<RwLock<crate::index::text::InvertedIndex>>> {
200 let key = format!("{label}:{property}");
201 self.text_indexes.read().get(&key).cloned()
202 }
203
204 #[cfg(feature = "text-index")]
208 pub fn remove_text_index(&self, label: &str, property: &str) -> bool {
209 let key = format!("{label}:{property}");
210 self.text_indexes.write().remove(&key).is_some()
211 }
212
213 #[cfg(feature = "text-index")]
217 pub fn text_index_entries(
218 &self,
219 ) -> Vec<(String, Arc<RwLock<crate::index::text::InvertedIndex>>)> {
220 self.text_indexes
221 .read()
222 .iter()
223 .map(|(k, v)| (k.clone(), v.clone()))
224 .collect()
225 }
226
227 #[cfg(feature = "text-index")]
232 pub(super) fn update_text_index_on_set(&self, id: NodeId, key: &str, value: &Value) {
233 let text_indexes = self.text_indexes.read();
234 if text_indexes.is_empty() {
235 return;
236 }
237 let registry = self.label_registry.read();
238 let node_labels = self.node_labels.read();
239 #[cfg(not(feature = "temporal"))]
240 let label_set = node_labels.get(&id);
241 #[cfg(feature = "temporal")]
242 let label_set = node_labels.get(&id).and_then(|log| log.latest());
243 if let Some(label_ids) = label_set {
244 for &label_id in label_ids {
245 if let Some(label_name) = registry.get_name(label_id) {
246 let index_key = format!("{label_name}:{key}");
247 if let Some(index) = text_indexes.get(&index_key) {
248 let mut idx = index.write();
249 idx.remove(id);
251 if let Value::String(text) = value {
252 idx.insert(id, text);
253 }
254 }
255 }
256 }
257 }
258 }
259
260 #[cfg(feature = "text-index")]
262 pub(super) fn update_text_index_on_remove(&self, id: NodeId, key: &str) {
263 let text_indexes = self.text_indexes.read();
264 if text_indexes.is_empty() {
265 return;
266 }
267 let registry = self.label_registry.read();
268 let node_labels = self.node_labels.read();
269 #[cfg(not(feature = "temporal"))]
270 let label_set = node_labels.get(&id);
271 #[cfg(feature = "temporal")]
272 let label_set = node_labels.get(&id).and_then(|log| log.latest());
273 if let Some(label_ids) = label_set {
274 for &label_id in label_ids {
275 if let Some(label_name) = registry.get_name(label_id) {
276 let index_key = format!("{label_name}:{key}");
277 if let Some(index) = text_indexes.get(&index_key) {
278 index.write().remove(id);
279 }
280 }
281 }
282 }
283 }
284
285 #[cfg(feature = "text-index")]
287 pub(super) fn remove_from_all_text_indexes(&self, id: NodeId) {
288 let text_indexes = self.text_indexes.read();
289 if text_indexes.is_empty() {
290 return;
291 }
292 for (_, index) in text_indexes.iter() {
293 index.write().remove(id);
294 }
295 }
296}