use std::collections::{BTreeMap, BTreeSet, btree_map};
use crate::{
ElementId, LabelId, PropertyKeyId, PropertySubject, RelationId, RelationTypeId,
state::{ElementRecord, RelationRecord},
value::PropertyValue,
};
type EqualityKey = (PropertyKeyId, PropertyValue);
#[derive(Clone, Debug)]
pub(crate) struct BaseIndex {
label_members: BTreeMap<LabelId, BTreeSet<ElementId>>,
relation_type_members: BTreeMap<RelationTypeId, BTreeSet<RelationId>>,
property_equality: BTreeMap<EqualityKey, BTreeSet<PropertySubject>>,
}
impl BaseIndex {
pub(crate) const fn empty() -> Self {
Self {
label_members: BTreeMap::new(),
relation_type_members: BTreeMap::new(),
property_equality: BTreeMap::new(),
}
}
pub(crate) fn from_records(
elements: &BTreeMap<ElementId, ElementRecord>,
relations: &BTreeMap<RelationId, RelationRecord>,
properties: &BTreeMap<PropertySubject, BTreeMap<PropertyKeyId, PropertyValue>>,
) -> Self {
let mut label_members: BTreeMap<LabelId, BTreeSet<ElementId>> = BTreeMap::new();
for record in elements.values() {
for label in &record.labels {
label_members.entry(*label).or_default().insert(record.id);
}
}
let mut relation_type_members: BTreeMap<RelationTypeId, BTreeSet<RelationId>> =
BTreeMap::new();
for record in relations.values() {
if let Some(relation_type) = record.relation_type {
relation_type_members
.entry(relation_type)
.or_default()
.insert(record.id);
}
}
let mut property_equality: BTreeMap<EqualityKey, BTreeSet<PropertySubject>> =
BTreeMap::new();
for (subject, keys) in properties {
for (key, value) in keys {
property_equality
.entry((*key, value.clone()))
.or_default()
.insert(*subject);
}
}
Self {
label_members,
relation_type_members,
property_equality,
}
}
fn label_posting(&self, label: LabelId) -> Option<&BTreeSet<ElementId>> {
self.label_members.get(&label)
}
fn relation_type_posting(
&self,
relation_type: RelationTypeId,
) -> Option<&BTreeSet<RelationId>> {
self.relation_type_members.get(&relation_type)
}
fn equality_posting(
&self,
key: PropertyKeyId,
value: &PropertyValue,
) -> Option<&BTreeSet<PropertySubject>> {
self.property_equality.get(&(key, value.clone()))
}
fn range_postings(
&self,
key: PropertyKeyId,
min: &PropertyValue,
max: &PropertyValue,
) -> impl Iterator<Item = (&EqualityKey, &BTreeSet<PropertySubject>)> {
self.property_equality
.range((key, min.clone())..=(key, max.clone()))
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct OverlayIndex {
label_added: BTreeMap<LabelId, BTreeSet<ElementId>>,
label_removed: BTreeMap<LabelId, BTreeSet<ElementId>>,
relation_type_added: BTreeMap<RelationTypeId, BTreeSet<RelationId>>,
relation_type_removed: BTreeMap<RelationTypeId, BTreeSet<RelationId>>,
equality_added: BTreeMap<EqualityKey, BTreeSet<PropertySubject>>,
equality_removed: BTreeMap<EqualityKey, BTreeSet<PropertySubject>>,
}
impl OverlayIndex {
pub(crate) const fn new() -> Self {
Self {
label_added: BTreeMap::new(),
label_removed: BTreeMap::new(),
relation_type_added: BTreeMap::new(),
relation_type_removed: BTreeMap::new(),
equality_added: BTreeMap::new(),
equality_removed: BTreeMap::new(),
}
}
fn add_equality(&mut self, subject: PropertySubject, key: PropertyKeyId, value: PropertyValue) {
remove_posting(&mut self.equality_removed, (key, value.clone()), &subject);
self.equality_added
.entry((key, value))
.or_default()
.insert(subject);
}
fn remove_equality(
&mut self,
subject: PropertySubject,
key: PropertyKeyId,
value: PropertyValue,
) {
remove_posting(&mut self.equality_added, (key, value.clone()), &subject);
self.equality_removed
.entry((key, value))
.or_default()
.insert(subject);
}
pub(crate) fn on_set_property(
&mut self,
subject: PropertySubject,
key: PropertyKeyId,
previous: Option<&PropertyValue>,
new_value: &PropertyValue,
) {
if let Some(previous) = previous
&& previous != new_value
{
self.remove_equality(subject, key, previous.clone());
}
self.add_equality(subject, key, new_value.clone());
}
pub(crate) fn on_remove_property(
&mut self,
subject: PropertySubject,
key: PropertyKeyId,
previous: Option<&PropertyValue>,
) {
if let Some(previous) = previous {
self.remove_equality(subject, key, previous.clone());
}
}
pub(crate) fn on_add_element_label(&mut self, element: ElementId, label: LabelId) {
remove_posting(&mut self.label_removed, label, &element);
self.label_added.entry(label).or_default().insert(element);
}
pub(crate) fn on_set_relation_type(
&mut self,
relation: RelationId,
previous: Option<RelationTypeId>,
new_type: RelationTypeId,
) {
if let Some(previous) = previous
&& previous != new_type
{
self.remove_relation_type(relation, previous);
}
remove_posting(&mut self.relation_type_removed, new_type, &relation);
self.relation_type_added
.entry(new_type)
.or_default()
.insert(relation);
}
fn remove_relation_type(&mut self, relation: RelationId, relation_type: RelationTypeId) {
remove_posting(&mut self.relation_type_added, relation_type, &relation);
self.relation_type_removed
.entry(relation_type)
.or_default()
.insert(relation);
}
pub(crate) fn on_tombstone_element(
&mut self,
element: ElementId,
labels: &BTreeSet<LabelId>,
properties: &BTreeMap<PropertyKeyId, PropertyValue>,
) {
for label in labels {
remove_posting(&mut self.label_added, *label, &element);
self.label_removed
.entry(*label)
.or_default()
.insert(element);
}
self.withdraw_subject_properties(PropertySubject::Element(element), properties);
}
pub(crate) fn on_tombstone_relation(
&mut self,
relation: RelationId,
relation_type: Option<RelationTypeId>,
properties: &BTreeMap<PropertyKeyId, PropertyValue>,
) {
if let Some(relation_type) = relation_type {
self.remove_relation_type(relation, relation_type);
}
self.withdraw_subject_properties(PropertySubject::Relation(relation), properties);
}
pub(crate) fn on_tombstone_incidence(
&mut self,
subject: PropertySubject,
properties: &BTreeMap<PropertyKeyId, PropertyValue>,
) {
self.withdraw_subject_properties(subject, properties);
}
fn withdraw_subject_properties(
&mut self,
subject: PropertySubject,
properties: &BTreeMap<PropertyKeyId, PropertyValue>,
) {
for (key, value) in properties {
self.remove_equality(subject, *key, value.clone());
}
}
pub(crate) fn elements_with_label(&self, base: &BaseIndex, label: LabelId) -> Vec<ElementId> {
merge_posting(
base.label_posting(label),
self.label_added.get(&label),
self.label_removed.get(&label),
)
}
pub(crate) fn relations_with_type(
&self,
base: &BaseIndex,
relation_type: RelationTypeId,
) -> Vec<RelationId> {
merge_posting(
base.relation_type_posting(relation_type),
self.relation_type_added.get(&relation_type),
self.relation_type_removed.get(&relation_type),
)
}
pub(crate) fn property_equal(
&self,
base: &BaseIndex,
key: PropertyKeyId,
value: &PropertyValue,
) -> Vec<PropertySubject> {
let pair = (key, value.clone());
merge_posting(
base.equality_posting(key, value),
self.equality_added.get(&pair),
self.equality_removed.get(&pair),
)
}
pub(crate) fn property_range(
&self,
base: &BaseIndex,
key: PropertyKeyId,
min: &PropertyValue,
max: &PropertyValue,
) -> Vec<PropertySubject> {
if min > max {
return Vec::new();
}
let mut visible: BTreeSet<PropertySubject> = BTreeSet::new();
for (pair, subjects) in base.range_postings(key, min, max) {
let removed = self.equality_removed.get(pair);
let kept = subjects
.iter()
.filter(|subject| removed.is_none_or(|set| !set.contains(subject)))
.copied();
visible.extend(kept);
}
for (_pair, subjects) in self
.equality_added
.range((key, min.clone())..=(key, max.clone()))
{
visible.extend(subjects.iter().copied());
}
visible.into_iter().collect()
}
pub(crate) fn property_composite_equal(
&self,
base: &BaseIndex,
pairs: &[(PropertyKeyId, PropertyValue)],
) -> Vec<PropertySubject> {
let mut per_key: Vec<BTreeSet<PropertySubject>> = pairs
.iter()
.map(|(key, value)| {
self.property_equal(base, *key, value)
.into_iter()
.collect::<BTreeSet<_>>()
})
.collect();
if per_key.is_empty() {
return Vec::new();
}
per_key.sort_by_key(BTreeSet::len);
let mut iter = per_key.into_iter();
let Some(mut accumulator) = iter.next() else {
return Vec::new();
};
for posting in iter {
if accumulator.is_empty() {
break;
}
accumulator.retain(|subject| posting.contains(subject));
}
accumulator.into_iter().collect()
}
}
fn remove_posting<K: Ord, M: Ord>(map: &mut BTreeMap<K, BTreeSet<M>>, key: K, member: &M) {
if let btree_map::Entry::Occupied(mut entry) = map.entry(key) {
entry.get_mut().remove(member);
if entry.get().is_empty() {
entry.remove();
}
}
}
fn merge_posting<M: Copy + Ord>(
base: Option<&BTreeSet<M>>,
added: Option<&BTreeSet<M>>,
removed: Option<&BTreeSet<M>>,
) -> Vec<M> {
let mut visible: BTreeSet<M> = BTreeSet::new();
if let Some(base) = base {
visible.extend(base.iter().copied());
}
if let Some(added) = added {
visible.extend(added.iter().copied());
}
if let Some(removed) = removed {
for member in removed {
visible.remove(member);
}
}
visible.into_iter().collect()
}