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