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 PropertyListContains {
81 variable: String,
82 property: String,
83 value: Value,
84 },
85 PropertyListContainsLower {
86 variable: String,
87 property: String,
88 value: String,
89 },
90 PropertyContains {
91 variable: String,
92 property: String,
93 value: String,
94 },
95 PropertyStartsWith {
96 variable: String,
97 property: String,
98 value: String,
99 },
100 PropertyEndsWith {
101 variable: String,
102 property: String,
103 value: String,
104 },
105 PropertyRegexMatch {
106 variable: String,
107 property: String,
108 pattern: ValidatedRegex,
109 },
110 PropertyIsNull {
111 variable: String,
112 property: String,
113 },
114 PropertyIsNotNull {
115 variable: String,
116 property: String,
117 },
118 PropertyIn {
119 variable: String,
120 property: String,
121 values: Vec<Value>,
122 },
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum ComparisonOp {
127 Lt,
128 Lte,
129 Gt,
130 Gte,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum DatePart {
135 Year,
136 Month,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub enum ProjectionExpression {
141 Variable {
142 variable: String,
143 },
144 Property {
145 variable: String,
146 property: String,
147 },
148 Id {
149 variable: String,
150 },
151 RelationshipType {
152 variable: String,
153 },
154 Literal(Value),
155 Coalesce(Vec<ProjectionExpression>),
156 Left {
157 expression: Box<ProjectionExpression>,
158 length: usize,
159 },
160 Lower(Box<ProjectionExpression>),
161 DatePart {
162 part: DatePart,
163 variable: String,
164 property: String,
165 },
166 DefaultIfNullOrEq {
167 variable: String,
168 property: String,
169 empty: Value,
170 default: Value,
171 },
172 DefaultIfNull {
173 variable: String,
174 property: String,
175 default: Value,
176 },
177 CasePropertyNotNullOrEq {
178 variable: String,
179 property: String,
180 empty: Value,
181 non_empty: Value,
182 null_or_empty: Value,
183 },
184 CasePropertyEqualsRank {
185 variable: String,
186 property: String,
187 branches: Vec<(Value, Value)>,
188 default: Value,
189 },
190 CaseLowerPropertyDefault {
191 variable: String,
192 property: String,
193 default: Value,
194 },
195 CaseCoalesceDifferenceFloorZero {
196 variable: String,
197 terms: Vec<CoalesceDifferenceProjectionTerm>,
198 },
199 Case {
200 operand: Option<Box<ProjectionExpression>>,
201 branches: Vec<(ProjectionExpression, ProjectionExpression)>,
202 otherwise: Option<Box<ProjectionExpression>>,
203 },
204 Binary {
205 left: Box<ProjectionExpression>,
206 op: ScalarBinaryOp,
207 right: Box<ProjectionExpression>,
208 },
209 Not(Box<ProjectionExpression>),
210 IsNull {
211 expression: Box<ProjectionExpression>,
212 negated: bool,
213 },
214 CaseEntitySearchRank(Box<CaseEntitySearchRankProjection>),
215 CaseColumnSearchRank(Box<CaseColumnSearchRankProjection>),
216 ColumnDefaultIfNullOrEq {
217 column: String,
218 property: String,
219 empty: Value,
220 default: Value,
221 },
222 ColumnValueDefaultIfNull {
223 column: String,
224 default: Value,
225 },
226 ColumnValueCasePropertyNotNullOrEq {
227 column: String,
228 empty: Value,
229 non_empty: Value,
230 null_or_empty: Value,
231 },
232 Column(String),
233 ColumnProperty {
234 column: String,
235 property: String,
236 },
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
240pub enum ScalarBinaryOp {
241 Add,
242 Subtract,
243 Multiply,
244 Divide,
245 Remainder,
246 Eq,
247 NotEq,
248 Lt,
249 Lte,
250 Gt,
251 Gte,
252 Contains,
253 ListContains,
254 And,
255 Or,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq)]
259pub struct CaseEntitySearchRankProjection {
260 pub variable: String,
261 pub name_property: String,
262 pub aliases_property: String,
263 pub raw_query: Value,
264 pub normalized_query: Value,
265 pub raw_input: Value,
266 pub exact_rank: Value,
267 pub alias_rank: Value,
268 pub fallback_rank: Value,
269}
270
271#[derive(Debug, Clone, PartialEq, Eq)]
272pub struct CaseColumnSearchRankProjection {
273 pub column: String,
274 pub raw_query: Value,
275 pub normalized_query: Value,
276 pub exact_rank: Value,
277 pub contains_rank: Value,
278 pub fallback_rank: Value,
279}
280
281#[derive(Debug, Clone, PartialEq, Eq)]
282pub struct CoalesceDifferenceProjectionTerm {
283 pub property: String,
284 pub default: Value,
285}
286
287impl ProjectionExpression {
288 pub fn all_children(&self, mut visit: impl FnMut(&ProjectionExpression) -> bool) -> bool {
290 match self {
291 Self::Case {
292 operand,
293 branches,
294 otherwise,
295 } => operand
296 .iter()
297 .map(Box::as_ref)
298 .chain(
299 branches
300 .iter()
301 .flat_map(|(condition, result)| [condition, result]),
302 )
303 .chain(otherwise.iter().map(Box::as_ref))
304 .all(visit),
305 Self::Binary { left, right, .. } => visit(left) && visit(right),
306 Self::Not(expression)
307 | Self::IsNull { expression, .. }
308 | Self::Lower(expression)
309 | Self::Left { expression, .. } => visit(expression),
310 Self::Coalesce(expressions) => expressions.iter().all(visit),
311 _ => true,
312 }
313 }
314}
315
316impl ProjectionExpression {
317 pub fn try_for_each_child_mut<E>(
319 &mut self,
320 mut visit: impl FnMut(&mut Self) -> Result<(), E>,
321 ) -> Result<(), E> {
322 match self {
323 Self::Case {
324 operand,
325 branches,
326 otherwise,
327 } => {
328 for expression in operand
329 .iter_mut()
330 .map(Box::as_mut)
331 .chain(
332 branches
333 .iter_mut()
334 .flat_map(|(condition, result)| [condition, result]),
335 )
336 .chain(otherwise.iter_mut().map(Box::as_mut))
337 {
338 visit(expression)?;
339 }
340 }
341 Self::Binary { left, right, .. } => {
342 visit(left)?;
343 visit(right)?;
344 }
345 Self::Not(expression)
346 | Self::IsNull { expression, .. }
347 | Self::Lower(expression)
348 | Self::Left { expression, .. } => visit(expression)?,
349 Self::Coalesce(expressions) => {
350 for expression in expressions {
351 visit(expression)?;
352 }
353 }
354 _ => {}
355 }
356 Ok(())
357 }
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub struct SortItem {
362 pub key: SortKey,
363 pub direction: SortDirection,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq)]
367pub enum SortKey {
368 Property { variable: String, property: String },
369 Id { variable: String },
370 Expression(ProjectionExpression),
371 Column(String),
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq)]
375pub enum SortDirection {
376 Asc,
377 Desc,
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383
384 #[test]
385 fn expression_ir_composes_boolean_and_scalar_nodes() {
386 let predicate = Predicate::ExpressionCompare {
387 expression: ProjectionExpression::Property {
388 variable: "memory".to_string(),
389 property: "importance".to_string(),
390 },
391 op: ComparisonOp::Gte,
392 value: ProjectionExpression::Literal(Value::Float(0.75)),
393 };
394
395 assert!(matches!(
396 predicate,
397 Predicate::ExpressionCompare {
398 op: ComparisonOp::Gte,
399 ..
400 }
401 ));
402 }
403}