hayson_engine/catalog/
indexes.rs1use std::collections::BTreeMap;
2
3use serde_json::json;
4
5use crate::schema::IndexKind;
6
7use super::types::{CatalogFieldSpec, CatalogIndexKind, CatalogPredicate, IndexEntry, TableEntry};
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct ExpandedIndex {
11 pub name: String,
12 pub fields: Vec<String>,
13 pub kind: CatalogIndexKind,
14 pub unique: bool,
15 pub where_: Option<Vec<CatalogPredicate>>,
16 pub expire_after_seconds: Option<u64>,
17}
18
19pub fn sugar_index_name(table: &str, field: &str, kind: &str) -> String {
20 format!("{table}_{field}_{kind}")
21}
22
23fn catalog_kind_of(kind: IndexKind) -> CatalogIndexKind {
24 match kind {
25 IndexKind::Exact => CatalogIndexKind::Exact,
26 IndexKind::Sorted => CatalogIndexKind::Sorted,
27 }
28}
29
30pub fn sugar_indexes_of(table: &str, fields: &BTreeMap<String, CatalogFieldSpec>) -> Vec<ExpandedIndex> {
31 let mut sugar = Vec::new();
32 for (field, entry) in fields {
33 if let Some(kind) = entry.spec.index {
34 let name_part = match kind {
35 IndexKind::Exact => "exact",
36 IndexKind::Sorted => "sorted",
37 };
38 sugar.push(ExpandedIndex {
39 name: sugar_index_name(table, field, name_part),
40 fields: vec![field.clone()],
41 kind: catalog_kind_of(kind),
42 unique: entry.spec.unique,
43 where_: None,
44 expire_after_seconds: None,
45 });
46 }
47 if entry.spec.fulltext {
48 sugar.push(ExpandedIndex {
49 name: sugar_index_name(table, field, "fulltext"),
50 fields: vec![field.clone()],
51 kind: CatalogIndexKind::Fulltext,
52 unique: false,
53 where_: None,
54 expire_after_seconds: None,
55 });
56 }
57 }
58 sugar
59}
60
61fn from_declared(declared: &IndexEntry) -> ExpandedIndex {
62 ExpandedIndex {
63 name: declared.name.clone(),
64 fields: declared.fields.clone(),
65 kind: declared.kind,
66 unique: declared.unique,
67 where_: declared.where_.clone(),
68 expire_after_seconds: declared.expire_after_seconds,
69 }
70}
71
72pub fn index_identity(index: &ExpandedIndex) -> String {
73 let mut clauses: Vec<(String, String, serde_json::Value)> = index
74 .where_
75 .as_ref()
76 .map(|clauses| {
77 clauses
78 .iter()
79 .map(|clause| (clause.field.clone(), clause.op.clone(), clause.value.clone()))
80 .collect()
81 })
82 .unwrap_or_default();
83 clauses.sort_by(|a, b| {
84 let left = serde_json::to_string(a).unwrap_or_default();
85 let right = serde_json::to_string(b).unwrap_or_default();
86 left.cmp(&right)
87 });
88 json!([
89 index.kind.as_str(),
90 index.fields,
91 index.unique,
92 clauses,
93 index.expire_after_seconds,
94 ])
95 .to_string()
96}
97
98pub fn expand_indexes(table: &str, entry: &TableEntry) -> Vec<ExpandedIndex> {
101 let mut by_identity: BTreeMap<String, ExpandedIndex> = BTreeMap::new();
102 for declared in entry.indexes.iter().flatten() {
103 let expanded = from_declared(declared);
104 let identity = index_identity(&expanded);
105 by_identity.entry(identity).or_insert(expanded);
106 }
107 for expanded in sugar_indexes_of(table, &entry.fields) {
108 let identity = index_identity(&expanded);
109 by_identity.entry(identity).or_insert(expanded);
110 }
111 by_identity.into_values().collect()
113}