use std::collections::BTreeMap;
use serde_json::json;
use crate::schema::IndexKind;
use super::types::{CatalogFieldSpec, CatalogIndexKind, CatalogPredicate, IndexEntry, TableEntry};
#[derive(Debug, Clone, PartialEq)]
pub struct ExpandedIndex {
pub name: String,
pub fields: Vec<String>,
pub kind: CatalogIndexKind,
pub unique: bool,
pub where_: Option<Vec<CatalogPredicate>>,
pub expire_after_seconds: Option<u64>,
}
pub fn sugar_index_name(table: &str, field: &str, kind: &str) -> String {
format!("{table}_{field}_{kind}")
}
fn catalog_kind_of(kind: IndexKind) -> CatalogIndexKind {
match kind {
IndexKind::Exact => CatalogIndexKind::Exact,
IndexKind::Sorted => CatalogIndexKind::Sorted,
}
}
pub fn sugar_indexes_of(table: &str, fields: &BTreeMap<String, CatalogFieldSpec>) -> Vec<ExpandedIndex> {
let mut sugar = Vec::new();
for (field, entry) in fields {
if let Some(kind) = entry.spec.index {
let name_part = match kind {
IndexKind::Exact => "exact",
IndexKind::Sorted => "sorted",
};
sugar.push(ExpandedIndex {
name: sugar_index_name(table, field, name_part),
fields: vec![field.clone()],
kind: catalog_kind_of(kind),
unique: entry.spec.unique,
where_: None,
expire_after_seconds: None,
});
}
if entry.spec.fulltext {
sugar.push(ExpandedIndex {
name: sugar_index_name(table, field, "fulltext"),
fields: vec![field.clone()],
kind: CatalogIndexKind::Fulltext,
unique: false,
where_: None,
expire_after_seconds: None,
});
}
}
sugar
}
fn from_declared(declared: &IndexEntry) -> ExpandedIndex {
ExpandedIndex {
name: declared.name.clone(),
fields: declared.fields.clone(),
kind: declared.kind,
unique: declared.unique,
where_: declared.where_.clone(),
expire_after_seconds: declared.expire_after_seconds,
}
}
pub fn index_identity(index: &ExpandedIndex) -> String {
let mut clauses: Vec<(String, String, serde_json::Value)> = index
.where_
.as_ref()
.map(|clauses| {
clauses
.iter()
.map(|clause| (clause.field.clone(), clause.op.clone(), clause.value.clone()))
.collect()
})
.unwrap_or_default();
clauses.sort_by(|a, b| {
let left = serde_json::to_string(a).unwrap_or_default();
let right = serde_json::to_string(b).unwrap_or_default();
left.cmp(&right)
});
json!([
index.kind.as_str(),
index.fields,
index.unique,
clauses,
index.expire_after_seconds,
])
.to_string()
}
pub fn expand_indexes(table: &str, entry: &TableEntry) -> Vec<ExpandedIndex> {
let mut by_identity: BTreeMap<String, ExpandedIndex> = BTreeMap::new();
for declared in entry.indexes.iter().flatten() {
let expanded = from_declared(declared);
let identity = index_identity(&expanded);
by_identity.entry(identity).or_insert(expanded);
}
for expanded in sugar_indexes_of(table, &entry.fields) {
let identity = index_identity(&expanded);
by_identity.entry(identity).or_insert(expanded);
}
by_identity.into_values().collect()
}