|
27 | 27 | (s/defn paths :- [[EPVPattern]] |
28 | 28 | "Returns a seq of all paths through the constraints. A path is defined |
29 | 29 | by new patterns containing at least one variable common to the patterns |
30 | | - that appeared before it. This prevents cross products in a join." |
| 30 | + that appeared before it. Patterns must form a group." |
31 | 31 | ([patterns :- [EPVPattern]] |
32 | 32 | (let [all-paths (paths #{} patterns)] |
33 | 33 | (assert (every? (partial = (count patterns)) (map count all-paths)) |
|
75 | 75 | (recur (into plan nxt-filters) bound patterns remaining-filters) |
76 | 76 | (recur (conj plan np) (into bound (get-vars np)) rp filters))))))) |
77 | 77 |
|
| 78 | +(s/defn first-group :- [(s/one [Pattern] "group") (s/one [Pattern] "remainder")] |
| 79 | + "Finds a group from a sequence of patterns. A group is defined by every pattern |
| 80 | + sharing at least one var with at least one other pattern. Returns a pair. |
| 81 | + The first returned element is the Patterns in the group, the second is what was left over." |
| 82 | + [[fp & rp] :- [Pattern]] |
| 83 | + (letfn [;; Define a reduction step. |
| 84 | + ;; Accumulates a triple of: known vars; patterns that are part of the group; |
| 85 | + ;; patterns that are not in the group. Each step looks at a pattern for |
| 86 | + ;; inclusion or exclusion |
| 87 | + (step [[vs included excluded] next-pattern] |
| 88 | + (let [new-vars (get-vars next-pattern)] |
| 89 | + (if (seq (set/intersection vs new-vars)) |
| 90 | + [(into vs new-vars) (conj included next-pattern) excluded] |
| 91 | + [vs included (conj excluded next-pattern)]))) |
| 92 | + ;; apply the reduction steps, with a given set of known vars, and |
| 93 | + ;; included patterns. Previously excluded patterns are being scanned |
| 94 | + ;; again using the new known vars. |
| 95 | + (groups [[v i e]] (reduce step [v i []] e))] |
| 96 | + ;; scan for everything that matches the first pattern, and then iterate until |
| 97 | + ;; everything that matches the resulting patterns has also been found. |
| 98 | + ;; Drop the set of vars before returning. |
| 99 | + (rest (u/fixpoint groups [(get-vars fp) [fp] rp])))) |
| 100 | + |
78 | 101 | (s/defn min-join-path :- [EPVPattern] |
79 | 102 | "Calculates a plan based on no outer joins (a cross product), and minimized joins. |
80 | 103 | A plan is the order in which to evaluate constraints and join them to the accumulated |
81 | 104 | evaluated data. If it is not possible to create a path without a cross product, |
82 | 105 | then return a plan of the patterns in the provided order." |
83 | 106 | [patterns :- [Pattern] |
84 | 107 | count-map :- {EPVPattern s/Num}] |
85 | | - (or |
86 | | - (->> (paths patterns) |
87 | | - (sort-by (partial mapv count-map)) |
88 | | - first) |
89 | | - patterns)) ;; TODO: longest paths with minimized cross products |
| 108 | + (loop [[grp rmdr] (first-group patterns) ordered []] |
| 109 | + (let [all-ordered (->> (paths grp) |
| 110 | + (sort-by (partial mapv count-map)) |
| 111 | + first |
| 112 | + (concat ordered))] |
| 113 | + (if (empty? rmdr) |
| 114 | + all-ordered |
| 115 | + (recur (first-group rmdr) all-ordered))))) |
90 | 116 |
|
91 | 117 | (s/defn user-plan :- [EPVPattern] |
92 | 118 | "Returns the original path specified by the user" |
|
0 commit comments