linkrs_query/planning/join_order/
subquery_graph.rs1use std::sync::Arc;
14
15use super::query_graph::QueryGraph;
16
17pub const MAX_NUM_QUERY_VARIABLES: usize = 64;
19
20#[derive(Debug, Clone)]
22pub struct SubqueryGraph {
23 query_graph: Arc<QueryGraph>,
24 pub query_nodes_selector: u64,
26 pub query_rels_selector: u64,
28}
29
30impl SubqueryGraph {
31 pub fn new(query_graph: Arc<QueryGraph>) -> Self {
32 Self {
33 query_graph,
34 query_nodes_selector: 0,
35 query_rels_selector: 0,
36 }
37 }
38
39 pub fn with_selectors(
40 query_graph: Arc<QueryGraph>,
41 query_nodes_selector: u64,
42 query_rels_selector: u64,
43 ) -> Self {
44 Self {
45 query_graph,
46 query_nodes_selector,
47 query_rels_selector,
48 }
49 }
50
51 pub fn single_node(query_graph: &Arc<QueryGraph>, node_pos: usize) -> Self {
53 assert!(node_pos < 64, "node position out of bitset range");
54 Self {
55 query_graph: Arc::clone(query_graph),
56 query_nodes_selector: 1u64 << node_pos,
57 query_rels_selector: 0,
58 }
59 }
60
61 pub fn single_rel(query_graph: &Arc<QueryGraph>, rel_pos: usize) -> Self {
67 assert!(rel_pos < 64, "rel position out of bitset range");
68 Self {
69 query_graph: Arc::clone(query_graph),
70 query_nodes_selector: 0,
71 query_rels_selector: 1u64 << rel_pos,
72 }
73 }
74
75 pub fn query_graph(&self) -> &Arc<QueryGraph> {
76 &self.query_graph
77 }
78
79 pub fn add_query_node(&mut self, node_pos: usize) {
80 assert!(node_pos < 64, "node position out of bitset range");
81 self.query_nodes_selector |= 1u64 << node_pos;
82 }
83
84 pub fn add_query_rel(&mut self, rel_pos: usize) {
85 assert!(rel_pos < 64, "rel position out of bitset range");
86 self.query_rels_selector |= 1u64 << rel_pos;
87 }
88
89 pub fn add_subquery_graph(&mut self, other: &SubqueryGraph) {
90 debug_assert!(Arc::ptr_eq(&self.query_graph, &other.query_graph));
91 self.query_nodes_selector |= other.query_nodes_selector;
92 self.query_rels_selector |= other.query_rels_selector;
93 }
94
95 pub fn contains_node(&self, node_pos: usize) -> bool {
96 node_pos < 64 && (self.query_nodes_selector >> node_pos) & 1 == 1
97 }
98
99 pub fn contains_rel(&self, rel_pos: usize) -> bool {
100 rel_pos < 64 && (self.query_rels_selector >> rel_pos) & 1 == 1
101 }
102
103 pub fn is_empty(&self) -> bool {
104 self.query_nodes_selector == 0 && self.query_rels_selector == 0
105 }
106
107 pub fn total_num_variables(&self) -> usize {
109 self.query_nodes_selector.count_ones() as usize
110 + self.query_rels_selector.count_ones() as usize
111 }
112
113 pub fn num_nodes(&self) -> usize {
114 self.query_nodes_selector.count_ones() as usize
115 }
116
117 pub fn num_rels(&self) -> usize {
118 self.query_rels_selector.count_ones() as usize
119 }
120
121 pub fn node_positions(&self) -> Vec<usize> {
122 (0..self.query_graph.num_nodes())
123 .filter(|p| self.contains_node(*p))
124 .collect()
125 }
126
127 pub fn rel_positions(&self) -> Vec<usize> {
128 (0..self.query_graph.num_rels())
129 .filter(|p| self.contains_rel(*p))
130 .collect()
131 }
132
133 fn contained_endpoint_positions(&self) -> Vec<usize> {
135 let mut out = Vec::new();
136 for rel_pos in self.rel_positions() {
137 if let Some((src, dst)) = self.query_graph.rel_endpoint_positions(rel_pos) {
138 if !out.contains(&src) {
139 out.push(src);
140 }
141 if !out.contains(&dst) {
142 out.push(dst);
143 }
144 }
145 }
146 out
147 }
148
149 pub fn get_node_neighbor_positions(&self) -> Vec<usize> {
152 let mut out = Vec::new();
153 for node_pos in self
154 .contained_endpoint_positions()
155 .into_iter()
156 .chain(self.frontier_endpoint_positions())
157 {
158 if !self.contains_node(node_pos) && !out.contains(&node_pos) {
159 out.push(node_pos);
160 }
161 }
162 out.sort_unstable();
163 out
164 }
165
166 fn frontier_endpoint_positions(&self) -> Vec<usize> {
168 let mut out = Vec::new();
169 for rel_pos in self.get_rel_neighbor_positions() {
170 if let Some((src, dst)) = self.query_graph.rel_endpoint_positions(rel_pos) {
171 if !self.contains_node(src) && !out.contains(&src) {
172 out.push(src);
173 }
174 if !self.contains_node(dst) && !out.contains(&dst) {
175 out.push(dst);
176 }
177 }
178 }
179 out
180 }
181
182 pub fn get_rel_neighbor_positions(&self) -> Vec<usize> {
186 let mut out = Vec::new();
187 for rel_pos in 0..self.query_graph.num_rels() {
188 if self.contains_rel(rel_pos) {
189 continue;
190 }
191 if self.touches_subgraph(rel_pos) {
192 out.push(rel_pos);
193 }
194 }
195 out.sort_unstable();
196 out
197 }
198
199 fn touches_subgraph(&self, rel_pos: usize) -> bool {
200 let Some((src, dst)) = self.query_graph.rel_endpoint_positions(rel_pos) else {
201 return false;
202 };
203 if self.contains_node(src) || self.contains_node(dst) {
204 return true;
205 }
206 let contained = self.contained_endpoint_positions();
207 contained.contains(&src) || contained.contains(&dst)
208 }
209
210 pub fn get_neighbor_subgraphs(&self, target_size: usize) -> Vec<SubqueryGraph> {
217 if target_size == 0 || target_size > MAX_NUM_QUERY_VARIABLES {
218 return Vec::new();
219 }
220 let mut out = Vec::new();
221 let mut seeds = self.frontier_singletons();
222 while let Some(seed) = seeds.pop() {
223 let size = seed.total_num_variables();
224 if size == target_size {
225 if !out.iter().any(|s: &SubqueryGraph| s.key() == seed.key()) {
226 out.push(seed);
227 }
228 } else if size < target_size {
229 seeds.extend(self.grown_seeds(&seed));
230 }
231 }
232 out
233 }
234
235 fn frontier_singletons(&self) -> Vec<SubqueryGraph> {
237 let mut out = Vec::new();
238 for rel_pos in self.get_rel_neighbor_positions() {
239 let mut seed = SubqueryGraph::new(Arc::clone(&self.query_graph));
240 seed.add_query_rel(rel_pos);
241 out.push(seed);
242 }
243 for node_pos in self.get_node_neighbor_positions() {
244 let mut seed = SubqueryGraph::new(Arc::clone(&self.query_graph));
245 seed.add_query_node(node_pos);
246 if !out.iter().any(|s: &SubqueryGraph| s.key() == seed.key()) {
247 out.push(seed);
248 }
249 }
250 out
251 }
252
253 fn grown_seeds(&self, seed: &SubqueryGraph) -> Vec<SubqueryGraph> {
256 let mut combined = self.clone();
257 combined.add_subquery_graph(seed);
258 let mut out = Vec::new();
259 for rel_pos in combined.get_rel_neighbor_positions() {
260 if seed.contains_rel(rel_pos) || self.contains_rel(rel_pos) {
261 continue;
262 }
263 let mut next = seed.clone();
264 next.add_query_rel(rel_pos);
265 out.push(next);
266 }
267 for node_pos in combined.get_node_neighbor_positions() {
268 if seed.contains_node(node_pos) || self.contains_node(node_pos) {
269 continue;
270 }
271 let mut next = seed.clone();
272 next.add_query_node(node_pos);
273 if !out.iter().any(|s: &SubqueryGraph| s.key() == next.key()) {
274 out.push(next);
275 }
276 }
277 out
278 }
279
280 pub fn get_connected_node_positions(&self, other: &SubqueryGraph) -> Vec<usize> {
282 let mut out = Vec::new();
283 for pos in 0..self.query_graph.num_nodes() {
284 if self.contains_node(pos) && other.contains_node(pos) {
285 out.push(pos);
286 }
287 }
288 out.sort_unstable();
289 out
290 }
291
292 pub fn is_connected(&self, other: &SubqueryGraph) -> bool {
294 !self.get_connected_node_positions(other).is_empty()
295 }
296
297 pub fn is_extendable_with(&self, other: &SubqueryGraph) -> bool {
302 for rel_pos in self.rel_positions() {
303 if let Some((src, dst)) = self.query_graph.rel_endpoint_positions(rel_pos) {
304 if other.contains_node(src) || other.contains_node(dst) {
305 return true;
306 }
307 }
308 }
309 for rel_pos in other.rel_positions() {
310 if let Some((src, dst)) = self.query_graph.rel_endpoint_positions(rel_pos) {
311 if self.contains_node(src) || self.contains_node(dst) {
312 return true;
313 }
314 }
315 }
316 false
317 }
318
319 pub fn is_joinable_with(&self, other: &SubqueryGraph) -> bool {
322 self.is_connected(other) || self.is_extendable_with(other)
323 }
324
325 pub fn key(&self) -> (u64, u64) {
327 (self.query_nodes_selector, self.query_rels_selector)
328 }
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334 use crate::planning::join_order::query_graph::{QueryNode, QueryRel};
335
336 fn triangle() -> Arc<QueryGraph> {
337 let mut qg = QueryGraph::new();
338 qg.add_node(QueryNode::new("a", "a"));
339 qg.add_node(QueryNode::new("b", "b"));
340 qg.add_node(QueryNode::new("c", "c"));
341 qg.add_rel(QueryRel::new("e1", "e1", "a", "b"));
342 qg.add_rel(QueryRel::new("e2", "e2", "a", "c"));
343 qg.add_rel(QueryRel::new("e3", "e3", "b", "c"));
344 Arc::new(qg)
345 }
346
347 #[test]
348 fn add_and_count() {
349 let qg = triangle();
350 let mut sg = SubqueryGraph::new(Arc::clone(&qg));
351 assert!(sg.is_empty());
352 sg.add_query_node(0);
353 sg.add_query_rel(0);
354 assert_eq!(sg.total_num_variables(), 2);
355 assert!(sg.contains_node(0));
356 assert!(!sg.contains_node(1));
357 }
358
359 #[test]
360 fn single_rel_is_rel_only() {
361 let qg = triangle();
362 let sg = SubqueryGraph::single_rel(&qg, 0);
363 assert!(sg.contains_rel(0));
364 assert_eq!(sg.total_num_variables(), 1);
365 assert_eq!(sg.num_nodes(), 0);
366 }
367
368 #[test]
369 fn rel_only_neighbors_are_endpoints() {
370 let qg = triangle();
371 let sg = SubqueryGraph::single_rel(&qg, 0);
372 assert_eq!(sg.get_node_neighbor_positions(), vec![0, 1, 2]);
374 let mut rel_nbrs = sg.get_rel_neighbor_positions();
375 rel_nbrs.sort_unstable();
376 assert_eq!(rel_nbrs, vec![1, 2]);
377 }
378
379 #[test]
380 fn rel_neighbors() {
381 let qg = triangle();
382 let mut sg = SubqueryGraph::new(Arc::clone(&qg));
383 sg.add_query_node(0);
384 sg.add_query_node(1);
385 sg.add_query_rel(0);
386 let mut nbrs = sg.get_rel_neighbor_positions();
387 nbrs.sort_unstable();
388 assert_eq!(nbrs, vec![1, 2]);
389 }
390
391 #[test]
392 fn connected_nodes_are_join_keys() {
393 let qg = triangle();
394 let mut left = SubqueryGraph::single_rel(&qg, 0);
395 left.add_query_node(0);
396 let mut right = SubqueryGraph::single_rel(&qg, 1);
397 right.add_query_node(0);
398 assert_eq!(left.get_connected_node_positions(&right), vec![0]);
399 assert!(left.is_connected(&right));
400 }
401
402 #[test]
403 fn rel_node_pair_is_extendable_but_not_connected() {
404 let qg = triangle();
405 let rel = SubqueryGraph::single_rel(&qg, 0);
406 let node = SubqueryGraph::single_node(&qg, 0);
407 assert!(!rel.is_connected(&node));
408 assert!(rel.is_extendable_with(&node));
409 assert!(rel.is_joinable_with(&node));
410 let far = SubqueryGraph::single_node(&qg, 2);
411 assert!(!rel.is_joinable_with(&far));
412 }
413
414 #[test]
415 fn neighbor_subgraphs_size_one() {
416 let qg = triangle();
417 let left = SubqueryGraph::single_rel(&qg, 0);
418 let nbrs = left.get_neighbor_subgraphs(1);
419 assert!(!nbrs.is_empty());
420 for n in &nbrs {
421 assert_eq!(n.total_num_variables(), 1);
422 }
423 assert!(nbrs.iter().any(|n| n.contains_node(0)));
425 assert!(nbrs.iter().any(|n| n.contains_node(1)));
426 }
427
428 #[test]
429 fn neighbor_subgraphs_grow_to_target_size() {
430 let qg = triangle();
431 let mut sg = SubqueryGraph::new(Arc::clone(&qg));
432 sg.add_query_node(0);
433 let nbrs = sg.get_neighbor_subgraphs(2);
434 assert!(!nbrs.is_empty());
435 for n in &nbrs {
436 assert_eq!(n.total_num_variables(), 2);
437 }
438 }
439}