Score a triple for join ordering. Lower = more selective = better. Cost = label_edge_count * bound_variable_factor - If both src and dst are already bound: factor = 0.01 (point lookup) - If one endpoint is bound: factor = 0.1 (single-node expansion) - If neither is bound: factor = 1.0 (full scan)
(
triple: &PatternTriple,
csr: &CsrIndex,
bound_vars: &std::collections::HashSet<String>,
)
| 94 | /// - If one endpoint is bound: factor = 0.1 (single-node expansion) |
| 95 | /// - If neither is bound: factor = 1.0 (full scan) |
| 96 | fn score_triple( |
| 97 | triple: &PatternTriple, |
| 98 | csr: &CsrIndex, |
| 99 | bound_vars: &std::collections::HashSet<String>, |
| 100 | ) -> f64 { |
| 101 | // Base cost: label edge count (fewer = more selective). |
| 102 | let label_count = triple |
| 103 | .edge |
| 104 | .edge_type |
| 105 | .as_ref() |
| 106 | .map_or(csr.edge_count(), |label| csr.label_edge_count(label)); |
| 107 | |
| 108 | // If label has zero edges, this triple can't produce results — cheapest possible. |
| 109 | if label_count == 0 { |
| 110 | return 0.0; |
| 111 | } |
| 112 | |
| 113 | let base_cost = label_count as f64; |
| 114 | |
| 115 | // Bound variable factor. |
| 116 | let src_bound = triple |
| 117 | .src |
| 118 | .name |
| 119 | .as_ref() |
| 120 | .is_some_and(|n| bound_vars.contains(n)); |
| 121 | let dst_bound = triple |
| 122 | .dst |
| 123 | .name |
| 124 | .as_ref() |
| 125 | .is_some_and(|n| bound_vars.contains(n)); |
| 126 | |
| 127 | let factor = match (src_bound, dst_bound) { |
| 128 | (true, true) => 0.01, // Both bound: point edge check. |
| 129 | (true, false) | (false, true) => 0.1, // One bound: neighborhood scan. |
| 130 | (false, false) => 1.0, // Neither bound: full label scan. |
| 131 | }; |
| 132 | |
| 133 | // Variable-length paths are more expensive — multiply by max_hops. |
| 134 | let hop_factor = if triple.edge.is_variable_length() { |
| 135 | triple.edge.max_hops as f64 |
| 136 | } else { |
| 137 | 1.0 |
| 138 | }; |
| 139 | |
| 140 | base_cost * factor * hop_factor |
| 141 | } |
| 142 | |
| 143 | #[cfg(test)] |
| 144 | mod tests { |