1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! `VIEW.DESCRIBE` — a view's declaration read back (split from
//! `describe.rs` for the 500-line rule; the reply conventions are there).
use crate::describe::{Described, argv, b, flag, n};
use crate::value::IndexValue;
use crate::view::{Tree, ViewMode, ViewSpec};
/// `VIEW.DESCRIBE`: `name query order_by desc mode topk via
/// declaration`. `query` is the tree as the tokens `VIEW.CREATE` reads.
///
/// ```
/// use kevy_index::{Described, IndexValue, Leaf, Tree, ViewMode, ViewSpec, describe_view};
///
/// let leaf = Leaf::new("age", IndexValue::I64(18), IndexValue::I64(99));
/// let v = ViewSpec::new("adults", Tree::Leaf(leaf), "age").with_mode(ViewMode::Virtual);
/// let Described::Array(fields) = describe_view(&v) else { unreachable!() };
/// let query = Described::Array(
/// ["age", "RANGE", "18", "99"].iter().map(|w| Described::Bulk(w.as_bytes().to_vec())).collect(),
/// );
/// assert_eq!(fields[3], query);
/// ```
pub fn describe_view(v: &ViewSpec) -> Described {
let top_k = match v.mode {
ViewMode::Materialized { top_k } => top_k,
ViewMode::Virtual => 0,
};
let mode = v.mode.name();
let mut query = Vec::new();
tree_words(&v.tree, &mut query);
Described::Array(vec![
b("name"),
b(&v.name),
b("query"),
argv(query),
b("order_by"),
b(&v.order_by),
b("desc"),
flag(v.order == kevy_text::SortOrder::Desc),
b("mode"),
b(mode),
b("topk"),
n(top_k),
b("via"),
v.via.as_ref().map_or_else(|| b("-"), b),
b("declaration"),
argv(view_declaration(v)),
])
}
/// The `VIEW.CREATE` argv that recreates `v`.
///
/// ```
/// use kevy_index::{IndexValue, Leaf, SortOrder, Tree, ViewMode, ViewSpec, view_declaration};
///
/// let leaf = |i: &str, v: i64| Box::new(Tree::Leaf(Leaf::new(i, IndexValue::I64(v), IndexValue::I64(v))));
/// let v = ViewSpec::new("both", Tree::And(leaf("a", 1), leaf("b", 2)), "a")
/// .with_order(SortOrder::Desc)
/// .with_mode(ViewMode::Materialized { top_k: 0 });
/// let line: Vec<String> =
/// view_declaration(&v).iter().map(|w| String::from_utf8_lossy(w).into_owned()).collect();
/// assert_eq!(line.join(" "), "VIEW.CREATE both QUERY ( AND a EQ 1 b EQ 2 ) ORDER BY a DESC MODE materialized");
/// ```
pub fn view_declaration(v: &ViewSpec) -> Vec<Vec<u8>> {
let mut w: Vec<Vec<u8>> = vec![b"VIEW.CREATE".to_vec(), v.name.clone(), b"QUERY".to_vec()];
tree_words(&v.tree, &mut w);
w.extend([b"ORDER".to_vec(), b"BY".to_vec(), v.order_by.clone()]);
if v.order == kevy_text::SortOrder::Desc {
w.push(b"DESC".to_vec());
}
if let ViewMode::Materialized { top_k } = v.mode {
w.extend([b"MODE".to_vec(), b"materialized".to_vec()]);
if top_k != 0 {
w.extend([b"TOPK".to_vec(), top_k.to_string().into()]);
}
}
if let Some(via) = &v.via {
w.extend([b"VIA".to_vec(), via.clone()]);
}
w
}
fn tree_words(t: &Tree, w: &mut Vec<Vec<u8>>) {
let (op, l, r) = match t {
Tree::Leaf(leaf) => {
w.push(leaf.index.clone());
if leaf.min == leaf.max {
w.extend([b"EQ".to_vec(), literal(&leaf.min)]);
} else {
w.extend([b"RANGE".to_vec(), literal(&leaf.min), literal(&leaf.max)]);
}
return;
}
Tree::And(l, r) => ("AND", l, r),
Tree::Or(l, r) => ("OR", l, r),
Tree::Diff(l, r) => ("DIFF", l, r),
};
w.extend([b"(".to_vec(), op.into()]);
tree_words(l, w);
tree_words(r, w);
w.push(b")".to_vec());
}
/// A bound as the literal that coerces back to it. Rust's float
/// `Display` is the shortest text that parses back to the same bits.
fn literal(v: &IndexValue) -> Vec<u8> {
match v {
IndexValue::I64(i) => i.to_string().into_bytes(),
IndexValue::F64(f) => f.to_string().into_bytes(),
IndexValue::Str(s) => s.clone(),
}
}