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 JsonValue {
161 expression: Box<ProjectionExpression>,
162 path: String,
163 },
164 Left {
165 expression: Box<ProjectionExpression>,
166 length: usize,
167 },
168 Lower(Box<ProjectionExpression>),
169 DatePart {
170 part: DatePart,
171 variable: String,
172 property: String,
173 },
174 DefaultIfNullOrEq {
175 variable: String,
176 property: String,
177 empty: Value,
178 default: Value,
179 },
180 DefaultIfNull {
181 variable: String,
182 property: String,
183 default: Value,
184 },
185 CasePropertyNotNullOrEq {
186 variable: String,
187 property: String,
188 empty: Value,
189 non_empty: Value,
190 null_or_empty: Value,
191 },
192 CasePropertyEqualsRank {
193 variable: String,
194 property: String,
195 branches: Vec<(Value, Value)>,
196 default: Value,
197 },
198 CaseLowerPropertyDefault {
199 variable: String,
200 property: String,
201 default: Value,
202 },
203 CaseCoalesceDifferenceFloorZero {
204 variable: String,
205 terms: Vec<CoalesceDifferenceProjectionTerm>,
206 },
207 Case {
208 operand: Option<Box<ProjectionExpression>>,
209 branches: Vec<(ProjectionExpression, ProjectionExpression)>,
210 otherwise: Option<Box<ProjectionExpression>>,
211 },
212 Binary {
213 left: Box<ProjectionExpression>,
214 op: ScalarBinaryOp,
215 right: Box<ProjectionExpression>,
216 },
217 Not(Box<ProjectionExpression>),
218 IsNull {
219 expression: Box<ProjectionExpression>,
220 negated: bool,
221 },
222 CaseEntitySearchRank(Box<CaseEntitySearchRankProjection>),
223 CaseColumnSearchRank(Box<CaseColumnSearchRankProjection>),
224 ColumnDefaultIfNullOrEq {
225 column: String,
226 property: String,
227 empty: Value,
228 default: Value,
229 },
230 ColumnValueDefaultIfNull {
231 column: String,
232 default: Value,
233 },
234 ColumnValueCasePropertyNotNullOrEq {
235 column: String,
236 empty: Value,
237 non_empty: Value,
238 null_or_empty: Value,
239 },
240 Column(String),
241 ColumnProperty {
242 column: String,
243 property: String,
244 },
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum ScalarBinaryOp {
249 Add,
250 Subtract,
251 Multiply,
252 Divide,
253 Remainder,
254 Eq,
255 NotEq,
256 Lt,
257 Lte,
258 Gt,
259 Gte,
260 Contains,
261 ListContains,
262 And,
263 Or,
264}
265
266#[derive(Debug, Clone, PartialEq, Eq)]
267pub struct CaseEntitySearchRankProjection {
268 pub variable: String,
269 pub name_property: String,
270 pub aliases_property: String,
271 pub raw_query: Value,
272 pub normalized_query: Value,
273 pub raw_input: Value,
274 pub exact_rank: Value,
275 pub alias_rank: Value,
276 pub fallback_rank: Value,
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
280pub struct CaseColumnSearchRankProjection {
281 pub column: String,
282 pub raw_query: Value,
283 pub normalized_query: Value,
284 pub exact_rank: Value,
285 pub contains_rank: Value,
286 pub fallback_rank: Value,
287}
288
289#[derive(Debug, Clone, PartialEq, Eq)]
290pub struct CoalesceDifferenceProjectionTerm {
291 pub property: String,
292 pub default: Value,
293}
294
295impl ProjectionExpression {
296 pub fn all_children(&self, mut visit: impl FnMut(&ProjectionExpression) -> bool) -> bool {
298 match self {
299 Self::Case {
300 operand,
301 branches,
302 otherwise,
303 } => operand
304 .iter()
305 .map(Box::as_ref)
306 .chain(
307 branches
308 .iter()
309 .flat_map(|(condition, result)| [condition, result]),
310 )
311 .chain(otherwise.iter().map(Box::as_ref))
312 .all(visit),
313 Self::Binary { left, right, .. } => visit(left) && visit(right),
314 Self::Not(expression)
315 | Self::IsNull { expression, .. }
316 | Self::Lower(expression)
317 | Self::Left { expression, .. }
318 | Self::JsonValue { expression, .. } => visit(expression),
319 Self::Coalesce(expressions) => expressions.iter().all(visit),
320 _ => true,
321 }
322 }
323}
324
325impl ProjectionExpression {
326 pub fn try_for_each_child_mut<E>(
328 &mut self,
329 mut visit: impl FnMut(&mut Self) -> Result<(), E>,
330 ) -> Result<(), E> {
331 match self {
332 Self::Case {
333 operand,
334 branches,
335 otherwise,
336 } => {
337 for expression in operand
338 .iter_mut()
339 .map(Box::as_mut)
340 .chain(
341 branches
342 .iter_mut()
343 .flat_map(|(condition, result)| [condition, result]),
344 )
345 .chain(otherwise.iter_mut().map(Box::as_mut))
346 {
347 visit(expression)?;
348 }
349 }
350 Self::Binary { left, right, .. } => {
351 visit(left)?;
352 visit(right)?;
353 }
354 Self::Not(expression)
355 | Self::IsNull { expression, .. }
356 | Self::Lower(expression)
357 | Self::Left { expression, .. }
358 | Self::JsonValue { expression, .. } => visit(expression)?,
359 Self::Coalesce(expressions) => {
360 for expression in expressions {
361 visit(expression)?;
362 }
363 }
364 _ => {}
365 }
366 Ok(())
367 }
368}
369
370#[derive(Debug, Clone, PartialEq, Eq)]
371pub struct SortItem {
372 pub key: SortKey,
373 pub direction: SortDirection,
374}
375
376#[derive(Debug, Clone, PartialEq, Eq)]
377pub enum SortKey {
378 Property { variable: String, property: String },
379 Id { variable: String },
380 Expression(ProjectionExpression),
381 Column(String),
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq)]
385pub enum SortDirection {
386 Asc,
387 Desc,
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn expression_ir_composes_boolean_and_scalar_nodes() {
396 let predicate = Predicate::ExpressionCompare {
397 expression: ProjectionExpression::Property {
398 variable: "memory".to_string(),
399 property: "importance".to_string(),
400 },
401 op: ComparisonOp::Gte,
402 value: ProjectionExpression::Literal(Value::Float(0.75)),
403 };
404
405 assert!(matches!(
406 predicate,
407 Predicate::ExpressionCompare {
408 op: ComparisonOp::Gte,
409 ..
410 }
411 ));
412 }
413}