drasi_core/path_solver/
match_path.rs

1// Copyright 2024 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use drasi_query_ast::ast::Expression;
16
17use super::merge_relation_match;
18
19use super::merge_node_match;
20
21use crate::evaluation::EvaluationError;
22use std::collections::HashMap;
23use std::collections::HashSet;
24use std::sync::Arc;
25
26use drasi_query_ast::ast;
27
28#[derive(Debug)]
29pub struct MatchPath {
30    pub slots: Vec<MatchPathSlot>,
31    optional_paths: HashSet<usize>,
32}
33
34impl MatchPath {
35    pub fn from_query(query_part: &ast::QueryPart) -> Result<Self, EvaluationError> {
36        let mut slots = Vec::new();
37
38        let mut alias_map = HashMap::new();
39        let mut optional_paths = HashSet::new();
40
41        for (path_index, mc) in query_part.match_clauses.iter().enumerate() {
42            if mc.optional {
43                optional_paths.insert(path_index);
44            }
45            let slot_num = merge_node_match(
46                &mc.start,
47                &mut slots,
48                &mut alias_map,
49                path_index,
50                mc.optional,
51            )?;
52            let mut prev_slot_num = slot_num;
53
54            for p in &mc.path {
55                let rel_slot_num = merge_relation_match(
56                    &p.0,
57                    &mut slots,
58                    &mut alias_map,
59                    path_index,
60                    mc.optional,
61                )?;
62                let node_slot_num =
63                    merge_node_match(&p.1, &mut slots, &mut alias_map, path_index, mc.optional)?;
64
65                match p.0.direction {
66                    ast::Direction::Right => {
67                        slots[prev_slot_num].out_slots.push(rel_slot_num);
68                        slots[rel_slot_num].in_slots.push(prev_slot_num);
69
70                        slots[rel_slot_num].out_slots.push(node_slot_num);
71                        slots[node_slot_num].in_slots.push(rel_slot_num);
72                    }
73                    ast::Direction::Left => {
74                        slots[prev_slot_num].in_slots.push(rel_slot_num);
75                        slots[rel_slot_num].out_slots.push(prev_slot_num);
76
77                        slots[rel_slot_num].in_slots.push(node_slot_num);
78                        slots[node_slot_num].out_slots.push(rel_slot_num);
79                    }
80                    ast::Direction::Either => {
81                        slots[prev_slot_num].in_slots.push(rel_slot_num);
82                        slots[prev_slot_num].out_slots.push(rel_slot_num);
83                        slots[rel_slot_num].in_slots.push(prev_slot_num);
84                        slots[rel_slot_num].out_slots.push(prev_slot_num);
85
86                        slots[node_slot_num].in_slots.push(rel_slot_num);
87                        slots[node_slot_num].out_slots.push(rel_slot_num);
88                        slots[rel_slot_num].in_slots.push(node_slot_num);
89                        slots[rel_slot_num].out_slots.push(node_slot_num);
90                    }
91                }
92
93                prev_slot_num = node_slot_num;
94            }
95        }
96
97        Ok(MatchPath {
98            slots,
99            optional_paths,
100        })
101    }
102
103    pub fn get_optional_slots_on_common_paths(
104        &self,
105        anchor_slot_num: usize,
106        empty_slots: HashSet<usize>,
107    ) -> HashSet<usize> {
108        let mut optional_slots = HashSet::new();
109        for path in &self.slots[anchor_slot_num].paths {
110            let mut has_empty_slots = false;
111            let mut path_slots = HashSet::new();
112
113            for (slot_num, slot) in self.slots.iter().enumerate() {
114                if slot.optional && slot.paths.contains(path) {
115                    if empty_slots.contains(&slot_num) {
116                        has_empty_slots = true;
117                        break;
118                    }
119
120                    if slot_num != anchor_slot_num && slot.paths.len() > 1 {
121                        continue;
122                    }
123
124                    path_slots.insert(slot_num);
125                }
126            }
127            if !has_empty_slots {
128                optional_slots.extend(path_slots);
129            }
130        }
131
132        optional_slots
133    }
134}
135
136#[derive(Debug)]
137pub struct MatchPathSlot {
138    pub spec: SlotElementSpec,
139    pub in_slots: Vec<usize>,
140    pub out_slots: Vec<usize>,
141    pub paths: HashSet<usize>,
142    pub optional: bool,
143}
144
145#[derive(Debug)]
146pub struct SlotElementSpec {
147    pub annotation: Option<Arc<str>>,
148    pub labels: Vec<Arc<str>>,
149    pub predicates: Vec<Expression>,
150}
151
152impl SlotElementSpec {
153    pub fn new(
154        annotation: Option<Arc<str>>,
155        labels: Vec<Arc<str>>,
156        predicates: Vec<Expression>,
157    ) -> SlotElementSpec {
158        SlotElementSpec {
159            annotation,
160            labels,
161            predicates,
162        }
163    }
164
165    pub fn from_node_match(node_match: &ast::NodeMatch) -> SlotElementSpec {
166        let annotation = &node_match.annotation.name;
167        let labels = node_match.labels.clone();
168        let predicates = node_match.property_predicates.clone();
169
170        SlotElementSpec::new(annotation.clone(), labels, predicates)
171    }
172
173    pub fn from_relation_match(node_match: &ast::RelationMatch) -> SlotElementSpec {
174        let annotation = &node_match.annotation.name;
175        let labels = node_match.labels.clone();
176        let predicates = node_match.property_predicates.clone();
177
178        SlotElementSpec::new(annotation.clone(), labels, predicates)
179    }
180}