icydb_core/db/query/predicate/
ast.rs1use crate::{
2 db::query::predicate::coercion::{CoercionId, CoercionSpec},
3 value::Value,
4};
5use std::ops::{BitAnd, BitOr};
6use thiserror::Error as ThisError;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26#[repr(u8)]
27pub enum CompareOp {
28 Eq = 0x01,
29 Ne = 0x02,
30 Lt = 0x03,
31 Lte = 0x04,
32 Gt = 0x05,
33 Gte = 0x06,
34 In = 0x07,
35 NotIn = 0x08,
36 Contains = 0x09,
37 StartsWith = 0x0a,
38 EndsWith = 0x0b,
39}
40
41impl CompareOp {
42 #[must_use]
43 pub const fn tag(self) -> u8 {
44 self as u8
45 }
46}
47
48#[derive(Clone, Debug, Eq, PartialEq)]
53pub struct ComparePredicate {
54 pub field: String,
55 pub op: CompareOp,
56 pub value: Value,
57 pub coercion: CoercionSpec,
58}
59
60impl ComparePredicate {
61 fn new(field: String, op: CompareOp, value: Value) -> Self {
62 Self {
63 field,
64 op,
65 value,
66 coercion: CoercionSpec::default(),
67 }
68 }
69
70 #[must_use]
72 pub fn with_coercion(
73 field: impl Into<String>,
74 op: CompareOp,
75 value: Value,
76 coercion: CoercionId,
77 ) -> Self {
78 Self {
79 field: field.into(),
80 op,
81 value,
82 coercion: CoercionSpec::new(coercion),
83 }
84 }
85
86 #[must_use]
87 pub fn eq(field: String, value: Value) -> Self {
88 Self::new(field, CompareOp::Eq, value)
89 }
90
91 #[must_use]
92 pub fn ne(field: String, value: Value) -> Self {
93 Self::new(field, CompareOp::Ne, value)
94 }
95
96 #[must_use]
97 pub fn lt(field: String, value: Value) -> Self {
98 Self::new(field, CompareOp::Lt, value)
99 }
100
101 #[must_use]
102 pub fn lte(field: String, value: Value) -> Self {
103 Self::new(field, CompareOp::Lte, value)
104 }
105
106 #[must_use]
107 pub fn gt(field: String, value: Value) -> Self {
108 Self::new(field, CompareOp::Gt, value)
109 }
110
111 #[must_use]
112 pub fn gte(field: String, value: Value) -> Self {
113 Self::new(field, CompareOp::Gte, value)
114 }
115
116 #[must_use]
117 pub fn in_(field: String, values: Vec<Value>) -> Self {
118 Self::new(field, CompareOp::In, Value::List(values))
119 }
120
121 #[must_use]
122 pub fn not_in(field: String, values: Vec<Value>) -> Self {
123 Self::new(field, CompareOp::NotIn, Value::List(values))
124 }
125}
126
127#[derive(Clone, Debug, Eq, PartialEq)]
132pub enum Predicate {
133 True,
134 False,
135 And(Vec<Self>),
136 Or(Vec<Self>),
137 Not(Box<Self>),
138 Compare(ComparePredicate),
139 IsNull {
140 field: String,
141 },
142 IsMissing {
143 field: String,
144 },
145 IsEmpty {
146 field: String,
147 },
148 IsNotEmpty {
149 field: String,
150 },
151 MapContainsKey {
152 field: String,
153 key: Value,
154 coercion: CoercionSpec,
155 },
156 MapContainsValue {
157 field: String,
158 value: Value,
159 coercion: CoercionSpec,
160 },
161 MapContainsEntry {
162 field: String,
163 key: Value,
164 value: Value,
165 coercion: CoercionSpec,
166 },
167 TextContains {
168 field: String,
169 value: Value,
170 },
171 TextContainsCi {
172 field: String,
173 value: Value,
174 },
175}
176
177impl Predicate {
178 #[must_use]
179 pub const fn and(preds: Vec<Self>) -> Self {
180 Self::And(preds)
181 }
182
183 #[must_use]
184 pub const fn or(preds: Vec<Self>) -> Self {
185 Self::Or(preds)
186 }
187
188 #[allow(clippy::should_implement_trait)]
189 #[must_use]
190 pub fn not(pred: Self) -> Self {
191 Self::Not(Box::new(pred))
192 }
193
194 #[must_use]
195 pub fn eq(field: String, value: Value) -> Self {
196 Self::Compare(ComparePredicate::eq(field, value))
197 }
198
199 #[must_use]
200 pub fn ne(field: String, value: Value) -> Self {
201 Self::Compare(ComparePredicate::ne(field, value))
202 }
203
204 #[must_use]
205 pub fn lt(field: String, value: Value) -> Self {
206 Self::Compare(ComparePredicate::lt(field, value))
207 }
208
209 #[must_use]
210 pub fn lte(field: String, value: Value) -> Self {
211 Self::Compare(ComparePredicate::lte(field, value))
212 }
213
214 #[must_use]
215 pub fn gt(field: String, value: Value) -> Self {
216 Self::Compare(ComparePredicate::gt(field, value))
217 }
218
219 #[must_use]
220 pub fn gte(field: String, value: Value) -> Self {
221 Self::Compare(ComparePredicate::gte(field, value))
222 }
223
224 #[must_use]
225 pub fn in_(field: String, values: Vec<Value>) -> Self {
226 Self::Compare(ComparePredicate::in_(field, values))
227 }
228
229 #[must_use]
230 pub fn not_in(field: String, values: Vec<Value>) -> Self {
231 Self::Compare(ComparePredicate::not_in(field, values))
232 }
233}
234
235impl BitAnd for Predicate {
236 type Output = Self;
237
238 fn bitand(self, rhs: Self) -> Self::Output {
239 Self::And(vec![self, rhs])
240 }
241}
242
243impl BitAnd for &Predicate {
244 type Output = Predicate;
245
246 fn bitand(self, rhs: Self) -> Self::Output {
247 Predicate::And(vec![self.clone(), rhs.clone()])
248 }
249}
250
251impl BitOr for Predicate {
252 type Output = Self;
253
254 fn bitor(self, rhs: Self) -> Self::Output {
255 Self::Or(vec![self, rhs])
256 }
257}
258
259impl BitOr for &Predicate {
260 type Output = Predicate;
261
262 fn bitor(self, rhs: Self) -> Self::Output {
263 Predicate::Or(vec![self.clone(), rhs.clone()])
264 }
265}
266
267#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
278pub enum UnsupportedQueryFeature {
279 #[error(
280 "map field '{field}' is not queryable; map predicates are disabled. model queryable attributes as scalar/indexed fields or list entries"
281 )]
282 MapPredicate { field: String },
283}