1pub mod sql;
2
3use hawdb_core::{RelationshipDirection, ValidatedRegex, Value};
4
5mod null_rejection;
6
7pub use null_rejection::{
8 prove_null_rejecting, BindingId, BindingSet, BoundPredicate, BoundScalarExpression,
9 NullRejectionProof, ScalarNullability, TruthSet, TruthValue,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Predicate {
14 And(Vec<Predicate>),
15 Or(Vec<Predicate>),
16 Not(Box<Predicate>),
17 ConstantBool(bool),
18 RelationshipExists {
19 variable: String,
20 rel_type: String,
21 direction: RelationshipDirection,
22 target_label: String,
23 },
24 BoundRelationshipExists {
25 source_variable: String,
26 rel_type: String,
27 direction: RelationshipDirection,
28 target_variable: String,
29 },
30 IdEq {
31 variable: String,
32 value: Value,
33 },
34 IdNotEq {
35 variable: String,
36 value: Value,
37 },
38 IdCompare {
39 variable: String,
40 op: ComparisonOp,
41 value: Value,
42 },
43 IdIn {
44 variable: String,
45 values: Vec<Value>,
46 },
47 PropertyEq {
48 variable: String,
49 property: String,
50 value: Value,
51 },
52 PropertyNotEq {
53 variable: String,
54 property: String,
55 value: Value,
56 },
57 PropertyCompare {
58 variable: String,
59 property: String,
60 op: ComparisonOp,
61 value: Value,
62 },
63 ExpressionEq {
64 expression: ProjectionExpression,
65 value: ProjectionExpression,
66 },
67 ExpressionNotEq {
68 expression: ProjectionExpression,
69 value: ProjectionExpression,
70 },
71 ExpressionCompare {
72 expression: ProjectionExpression,
73 op: ComparisonOp,
74 value: ProjectionExpression,
75 },
76 ExpressionContains {
77 expression: ProjectionExpression,
78 value: ProjectionExpression,
79 },
80 ExpressionIn {
81 expression: ProjectionExpression,
82 values: Vec<Value>,
83 },
84 PropertyListContains {
85 variable: String,
86 property: String,
87 value: Value,
88 },
89 PropertyListContainsLower {
90 variable: String,
91 property: String,
92 value: String,
93 },
94 PropertyContains {
95 variable: String,
96 property: String,
97 value: String,
98 },
99 PropertyStartsWith {
100 variable: String,
101 property: String,
102 value: String,
103 },
104 PropertyEndsWith {
105 variable: String,
106 property: String,
107 value: String,
108 },
109 PropertyRegexMatch {
110 variable: String,
111 property: String,
112 pattern: ValidatedRegex,
113 },
114 PropertyIsNull {
115 variable: String,
116 property: String,
117 },
118 PropertyIsNotNull {
119 variable: String,
120 property: String,
121 },
122 PropertyIn {
123 variable: String,
124 property: String,
125 values: Vec<Value>,
126 },
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum ComparisonOp {
131 Lt,
132 Lte,
133 Gt,
134 Gte,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138pub enum DatePart {
139 Year,
140 Month,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
144pub enum ProjectionExpression {
145 Variable {
146 variable: String,
147 },
148 Property {
149 variable: String,
150 property: String,
151 },
152 Id {
153 variable: String,
154 },
155 RelationshipType {
156 variable: String,
157 },
158 Literal(Value),
159 Coalesce(Vec<ProjectionExpression>),
160 VectorDistance {
161 metric: hawdb_core::VectorDistance,
162 left: Box<ProjectionExpression>,
163 right: Box<ProjectionExpression>,
164 },
165 JsonValue {
166 expression: Box<ProjectionExpression>,
167 path: String,
168 },
169 Left {
170 expression: Box<ProjectionExpression>,
171 length: usize,
172 },
173 Lower(Box<ProjectionExpression>),
174 DatePart {
175 part: DatePart,
176 variable: String,
177 property: String,
178 },
179 DefaultIfNullOrEq {
180 variable: String,
181 property: String,
182 empty: Value,
183 default: Value,
184 },
185 DefaultIfNull {
186 variable: String,
187 property: String,
188 default: Value,
189 },
190 CasePropertyNotNullOrEq {
191 variable: String,
192 property: String,
193 empty: Value,
194 non_empty: Value,
195 null_or_empty: Value,
196 },
197 CasePropertyEqualsRank {
198 variable: String,
199 property: String,
200 branches: Vec<(Value, Value)>,
201 default: Value,
202 },
203 CaseLowerPropertyDefault {
204 variable: String,
205 property: String,
206 default: Value,
207 },
208 CaseCoalesceDifferenceFloorZero {
209 variable: String,
210 terms: Vec<CoalesceDifferenceProjectionTerm>,
211 },
212 Case {
213 operand: Option<Box<ProjectionExpression>>,
214 branches: Vec<(ProjectionExpression, ProjectionExpression)>,
215 otherwise: Option<Box<ProjectionExpression>>,
216 },
217 Binary {
218 left: Box<ProjectionExpression>,
219 op: ScalarBinaryOp,
220 right: Box<ProjectionExpression>,
221 },
222 Not(Box<ProjectionExpression>),
223 IsNull {
224 expression: Box<ProjectionExpression>,
225 negated: bool,
226 },
227 CaseEntitySearchRank(Box<CaseEntitySearchRankProjection>),
228 CaseColumnSearchRank(Box<CaseColumnSearchRankProjection>),
229 ColumnDefaultIfNullOrEq {
230 column: String,
231 property: String,
232 empty: Value,
233 default: Value,
234 },
235 ColumnValueDefaultIfNull {
236 column: String,
237 default: Value,
238 },
239 ColumnValueCasePropertyNotNullOrEq {
240 column: String,
241 empty: Value,
242 non_empty: Value,
243 null_or_empty: Value,
244 },
245 Column(String),
246 ColumnProperty {
247 column: String,
248 property: String,
249 },
250}
251
252#[derive(Debug, Clone, Copy, PartialEq, Eq)]
253pub enum ScalarBinaryOp {
254 Add,
255 Subtract,
256 Multiply,
257 Divide,
258 Remainder,
259 Eq,
260 NotEq,
261 Lt,
262 Lte,
263 Gt,
264 Gte,
265 Contains,
266 ListContains,
267 And,
268 Or,
269}
270
271#[derive(Debug, Clone, PartialEq, Eq)]
272pub struct CaseEntitySearchRankProjection {
273 pub variable: String,
274 pub name_property: String,
275 pub aliases_property: String,
276 pub raw_query: Value,
277 pub normalized_query: Value,
278 pub raw_input: Value,
279 pub exact_rank: Value,
280 pub alias_rank: Value,
281 pub fallback_rank: Value,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct CaseColumnSearchRankProjection {
286 pub column: String,
287 pub raw_query: Value,
288 pub normalized_query: Value,
289 pub exact_rank: Value,
290 pub contains_rank: Value,
291 pub fallback_rank: Value,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub struct CoalesceDifferenceProjectionTerm {
296 pub property: String,
297 pub default: Value,
298}
299
300impl ProjectionExpression {
301 pub fn all_children(&self, mut visit: impl FnMut(&ProjectionExpression) -> bool) -> bool {
303 match self {
304 Self::Case {
305 operand,
306 branches,
307 otherwise,
308 } => operand
309 .iter()
310 .map(Box::as_ref)
311 .chain(
312 branches
313 .iter()
314 .flat_map(|(condition, result)| [condition, result]),
315 )
316 .chain(otherwise.iter().map(Box::as_ref))
317 .all(visit),
318 Self::Binary { left, right, .. } | Self::VectorDistance { left, right, .. } => {
319 visit(left) && visit(right)
320 }
321 Self::Not(expression)
322 | Self::IsNull { expression, .. }
323 | Self::Lower(expression)
324 | Self::Left { expression, .. }
325 | Self::JsonValue { expression, .. } => visit(expression),
326 Self::Coalesce(expressions) => expressions.iter().all(visit),
327 _ => true,
328 }
329 }
330}
331
332impl ProjectionExpression {
333 pub fn try_for_each_child_mut<E>(
335 &mut self,
336 mut visit: impl FnMut(&mut Self) -> Result<(), E>,
337 ) -> Result<(), E> {
338 match self {
339 Self::Case {
340 operand,
341 branches,
342 otherwise,
343 } => {
344 for expression in operand
345 .iter_mut()
346 .map(Box::as_mut)
347 .chain(
348 branches
349 .iter_mut()
350 .flat_map(|(condition, result)| [condition, result]),
351 )
352 .chain(otherwise.iter_mut().map(Box::as_mut))
353 {
354 visit(expression)?;
355 }
356 }
357 Self::Binary { left, right, .. } | Self::VectorDistance { left, right, .. } => {
358 visit(left)?;
359 visit(right)?;
360 }
361 Self::Not(expression)
362 | Self::IsNull { expression, .. }
363 | Self::Lower(expression)
364 | Self::Left { expression, .. }
365 | Self::JsonValue { expression, .. } => visit(expression)?,
366 Self::Coalesce(expressions) => {
367 for expression in expressions {
368 visit(expression)?;
369 }
370 }
371 _ => {}
372 }
373 Ok(())
374 }
375}
376
377#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct SortItem {
379 pub key: SortKey,
380 pub direction: SortDirection,
381}
382
383#[derive(Debug, Clone, PartialEq, Eq)]
384pub enum SortKey {
385 Property { variable: String, property: String },
386 Id { variable: String },
387 Expression(ProjectionExpression),
388 Column(String),
389}
390
391#[derive(Debug, Clone, Copy, PartialEq, Eq)]
392pub enum SortDirection {
393 Asc,
394 Desc,
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn expression_ir_composes_boolean_and_scalar_nodes() {
403 let predicate = Predicate::ExpressionCompare {
404 expression: ProjectionExpression::Property {
405 variable: "memory".to_string(),
406 property: "importance".to_string(),
407 },
408 op: ComparisonOp::Gte,
409 value: ProjectionExpression::Literal(Value::Float(0.75)),
410 };
411
412 assert!(matches!(
413 predicate,
414 Predicate::ExpressionCompare {
415 op: ComparisonOp::Gte,
416 ..
417 }
418 ));
419 }
420}