use super::helpers::safe_bitmap_id;
use roaring::RoaringBitmap;
use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct LabelIndex {
labels: HashMap<String, RoaringBitmap>,
has_large_ids: bool,
}
impl LabelIndex {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn index_from_payload(&mut self, node_id: u64, payload: &serde_json::Value) -> usize {
let Some(safe_id) = safe_bitmap_id(node_id) else {
tracing::warn!(
node_id,
"LabelIndex: node_id exceeds u32::MAX, cannot index"
);
if payload.get("_labels").and_then(|v| v.as_array()).is_some() {
self.has_large_ids = true;
}
return 0;
};
let Some(labels_arr) = payload.get("_labels").and_then(|v| v.as_array()) else {
return 0;
};
let mut count = 0usize;
for label_val in labels_arr {
if let Some(label_str) = label_val.as_str() {
self.labels
.entry(label_str.to_string())
.or_default()
.insert(safe_id);
count += 1;
}
}
count
}
pub fn insert(&mut self, label: &str, node_id: u64) -> bool {
let Some(safe_id) = safe_bitmap_id(node_id) else {
return false;
};
self.labels
.entry(label.to_string())
.or_default()
.insert(safe_id)
}
pub fn remove_from_payload(&mut self, node_id: u64, payload: &serde_json::Value) {
let Some(safe_id) = safe_bitmap_id(node_id) else {
return;
};
let Some(labels_arr) = payload.get("_labels").and_then(|v| v.as_array()) else {
return;
};
for label_val in labels_arr {
if let Some(label_str) = label_val.as_str() {
if let Some(bitmap) = self.labels.get_mut(label_str) {
bitmap.remove(safe_id);
if bitmap.is_empty() {
self.labels.remove(label_str);
}
}
}
}
}
#[must_use]
pub fn has_large_ids(&self) -> bool {
self.has_large_ids
}
#[must_use]
pub fn lookup(&self, label: &str) -> Option<&RoaringBitmap> {
self.labels.get(label)
}
#[must_use]
pub fn lookup_intersection(&self, labels: &[String]) -> Option<RoaringBitmap> {
let mut iter = labels.iter();
let first = iter.next()?;
let mut result = self.labels.get(first.as_str())?.clone();
for label in iter {
match self.labels.get(label.as_str()) {
Some(bitmap) => result &= bitmap,
None => return None, }
if result.is_empty() {
return None;
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
#[must_use]
pub fn label_count(&self) -> usize {
self.labels.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.labels.is_empty()
}
pub fn clear(&mut self) {
self.labels.clear();
}
#[must_use]
pub fn memory_usage(&self) -> usize {
let mut total = std::mem::size_of::<Self>();
for (label, bitmap) in &self.labels {
total += label.len() + std::mem::size_of::<String>();
total += bitmap.serialized_size();
}
total
}
}