|
1 | 1 | (ns fluree.db.query.subject-crawl.reparse
|
2 | 2 | (:require [fluree.db.util.log :as log :include-macros true]
|
3 | 3 | [fluree.db.flake :as flake]
|
4 |
| - [fluree.db.util.core :as util #?(:clj :refer :cljs :refer-macros) [try* catch*]])) |
| 4 | + [fluree.db.util.core :as util #?(:clj :refer :cljs :refer-macros) [try* catch*]] |
| 5 | + #?(:clj [fluree.db.query.exec.select] |
| 6 | + :cljs [fluree.db.query.exec.select :refer [SubgraphSelector]]) |
| 7 | + [fluree.db.query.exec.where :as where]) |
| 8 | + #?(:clj (:import [fluree.db.query.exec.select SubgraphSelector]))) |
5 | 9 |
|
6 | 10 | #?(:clj (set! *warn-on-reflection* true))
|
7 | 11 |
|
|
19 | 23 | {:status 400 :error :db/invalid-query})))))
|
20 | 24 | [] params))
|
21 | 25 |
|
| 26 | +(defn mergeable-where-clause? |
| 27 | + "Returns true if a `where` clause is eligible for merging |
| 28 | + with other clauses to create a filter for the simple-subject-crawl |
| 29 | + strategy" |
| 30 | + [where-clause] |
| 31 | + (#{:class :tuple} (where/pattern-type where-clause))) |
| 32 | + |
| 33 | +(defn clause-subject-var |
| 34 | + [where-clause] |
| 35 | + (-> where-clause |
| 36 | + first |
| 37 | + ::where/var)) |
| 38 | + |
22 | 39 | (defn merge-wheres-to-filter
|
23 | 40 | "Merges all subsequent where clauses (rest where) for simple-subject-crawl
|
24 | 41 | into a map containing predicate filters.
|
|
38 | 55 | Note that for multi-cardinality predicates, the prediate filters must pass for just one flake
|
39 | 56 | "
|
40 | 57 | [first-s rest-where supplied-vars]
|
41 |
| - (loop [[{:keys [type s p o] :as where-smt} & r] rest-where |
| 58 | + (loop [[{:keys [s p o] :as where-smt} & r] rest-where |
42 | 59 | required-p #{} ;; set of 'p' values that are going to be required for a subject to have
|
43 | 60 | filter-map {}] ;; key 'p' value, val is list of filtering fns
|
44 |
| - (if where-smt |
45 |
| - (when (and (= :tuple type) |
46 |
| - (= first-s (:variable s))) |
47 |
| - (let [{:keys [value filter variable]} o |
48 |
| - f (cond |
49 |
| - value |
50 |
| - (fn [flake _] (= value (flake/o flake))) |
| 61 | + (let [[s p o] where-smt |
| 62 | + {p* ::where/val} p |
| 63 | + type (where/pattern-type where-smt)] |
| 64 | + (if where-smt |
| 65 | + (when (and (= :tuple type) |
| 66 | + (= first-s (clause-subject-var where-smt))) |
| 67 | + (let [{::where/keys [val var]} o |
| 68 | + f (cond |
| 69 | + val |
| 70 | + (fn [flake _] (= val (flake/o flake))) |
51 | 71 |
|
52 |
| - filter |
53 |
| - (let [{:keys [params variable function]} filter] |
54 |
| - (if (= 1 (count params)) |
55 |
| - (fn [flake _] (function (flake/o flake))) |
56 |
| - (fn [flake vars] |
57 |
| - (let [params (fill-fn-params params (flake/o flake) variable vars)] |
58 |
| - (log/debug (str "Calling query-filter fn: " (:fn-str filter) |
59 |
| - "with params: " params ".")) |
60 |
| - (apply function params))))) |
| 72 | + ;;TODO: filters are not yet supported |
| 73 | + #_#_filter |
| 74 | + (let [{:keys [params variable function]} filter] |
| 75 | + (if (= 1 (count params)) |
| 76 | + (fn [flake _] (function (flake/o flake))) |
| 77 | + (fn [flake vars] |
| 78 | + (let [params (fill-fn-params params (flake/o flake) variable vars)] |
| 79 | + (log/debug (str "Calling query-filter fn: " ("fn-str" filter) |
| 80 | + "with params: " params ".")) |
| 81 | + (apply function params))))) |
| 82 | + ;;TODO: vars are not yet supported |
| 83 | + #_#_(and var (get supplied-vars var)) |
| 84 | + (fn [flake vars] |
| 85 | + (= (flake/o flake) (get vars var))))] |
| 86 | + (recur r |
| 87 | + (conj required-p p*) |
| 88 | + (if f |
| 89 | + (update filter-map p* util/conjv f) |
| 90 | + filter-map)))) |
| 91 | + (assoc filter-map :required-p required-p))))) |
61 | 92 |
|
62 |
| - (and variable (supplied-vars variable)) |
63 |
| - (fn [flake vars] |
64 |
| - (= (flake/o flake) (get vars variable))))] |
65 |
| - (recur r |
66 |
| - (conj required-p p) |
67 |
| - (if f |
68 |
| - (update filter-map p util/conjv f) |
69 |
| - filter-map)))) |
70 |
| - (assoc filter-map :required-p required-p)))) |
71 | 93 |
|
| 94 | +(defn re-parse-pattern |
| 95 | + "Re-parses a pattern into the format recognized |
| 96 | + by downstream simple-subject-crawl code" |
| 97 | + [pattern] |
| 98 | + (let [type (where/pattern-type pattern) |
| 99 | + [s p o] (if (= :tuple type) |
| 100 | + pattern |
| 101 | + (let [[_type-kw tuple] pattern] |
| 102 | + tuple)) |
| 103 | + reparse-component (fn [component] |
| 104 | + (let [{::where/keys [var val]} component] |
| 105 | + (cond |
| 106 | + var {:variable var} |
| 107 | + val {:value val})))] |
| 108 | + {:type type |
| 109 | + :s (reparse-component s) |
| 110 | + :p (reparse-component p) |
| 111 | + :o (assoc (reparse-component o) :datatype (::where/datatype o))})) |
72 | 112 |
|
73 | 113 | (defn simple-subject-merge-where
|
74 | 114 | "Revises where clause for simple-subject-crawl query to optimize processing.
|
75 | 115 | If where does not end up meeting simple-subject-crawl criteria, returns nil
|
76 | 116 | so other strategies can be tried."
|
77 |
| - [{:keys [where supplied-vars] :as parsed-query}] |
78 |
| - (let [first-where (first where) |
79 |
| - rest-where (rest where) |
80 |
| - first-type (:type first-where) |
81 |
| - first-s (when (and (#{:rdf/type :collection :_id :iri :tuple} first-type) |
82 |
| - (-> first-where :s :variable)) |
83 |
| - (-> first-where :s :variable))] |
84 |
| - (when first-s |
85 |
| - (if (empty? rest-where) |
86 |
| - (assoc parsed-query :strategy :simple-subject-crawl) |
87 |
| - (if-let [subj-filter-map (merge-wheres-to-filter first-s rest-where supplied-vars)] |
88 |
| - (assoc parsed-query :where [first-where |
| 117 | + [{:keys [where vars] :as parsed-query}] |
| 118 | + (let [{::where/keys [patterns]} where |
| 119 | + [first-pattern & rest-patterns] patterns |
| 120 | + reparsed-first-clause (re-parse-pattern first-pattern)] |
| 121 | + (when-let [first-s (and (mergeable-where-clause? first-pattern) |
| 122 | + (clause-subject-var first-pattern))] |
| 123 | + (if (empty? rest-patterns) |
| 124 | + (assoc parsed-query |
| 125 | + :where [reparsed-first-clause] |
| 126 | + :strategy :simple-subject-crawl) |
| 127 | + (if-let [subj-filter-map (merge-wheres-to-filter first-s rest-patterns vars)] |
| 128 | + (assoc parsed-query :where [reparsed-first-clause |
89 | 129 | {:s-filter subj-filter-map}]
|
90 | 130 | :strategy :simple-subject-crawl))))))
|
91 |
| - |
92 |
| -(defn subject-crawl? |
93 |
| - "Returns true if, when given parsed query, the select statement is a |
94 |
| - subject crawl - meaning there is nothing else in the :select except a |
95 |
| - graph crawl on a list of subjects" |
96 |
| - [{:keys [select] :as _parsed-query}] |
97 |
| - (and (:expandMaps? select) |
98 |
| - (not (:inVector? select)))) |
99 |
| - |
100 | 131 | (defn simple-subject-crawl?
|
101 | 132 | "Simple subject crawl is where the same variable is used in the leading
|
102 | 133 | position of each where statement."
|
103 |
| - [{:keys [where select] :as _parsed-query}] |
104 |
| - (let [select-var (or (-> select :select first :variable) ;; legacy select spec |
105 |
| - (-> select :spec first :variable))] |
106 |
| - (when select-var ;; for now exclude any filters on the first where, not implemented |
107 |
| - (every? #(and (= select-var (-> % :s :variable)) |
108 |
| - ;; exclude if any recursion specified in where statement (e.g. person/follows+3) |
109 |
| - (not (:recur %))) |
110 |
| - where)))) |
| 134 | + [{:keys [where select vars] :as _parsed-query}] |
| 135 | + (and (instance? SubgraphSelector select) |
| 136 | + ;;TODO, filtering not supported yet |
| 137 | + (empty? (::where/filters where)) |
| 138 | + ;;TODO: vars support not complete |
| 139 | + (empty? vars) |
| 140 | + (if-let [{select-var :var} select] |
| 141 | + (let [{::where/keys [patterns]} where] |
| 142 | + (every? (fn [pattern] |
| 143 | + (let [pred (second pattern)] |
| 144 | + (and (= select-var (clause-subject-var pattern)) |
| 145 | + (not (::where/recur pred)) |
| 146 | + (not (::where/fullText pred))))) patterns))))) |
111 | 147 |
|
112 | 148 | (defn re-parse-as-simple-subj-crawl
|
113 | 149 | "Returns true if query contains a single subject crawl.
|
114 | 150 | e.g.
|
115 | 151 | {:select {?subjects ['*']}
|
116 | 152 | :where [...]}"
|
117 |
| - [{:keys [op-type order-by group-by] :as parsed-query}] |
118 |
| - (when (and (= :select op-type) |
119 |
| - (subject-crawl? parsed-query) |
120 |
| - (simple-subject-crawl? parsed-query) |
121 |
| - (not group-by) |
122 |
| - (not= :variable (:type order-by))) |
| 153 | + [{:keys [order-by group-by] :as parsed-query}] |
| 154 | + (when (and (not group-by) |
| 155 | + (not order-by) |
| 156 | + (simple-subject-crawl? parsed-query)) |
123 | 157 | ;; following will return nil if parts of where clause exclude it from being a simple-subject-crawl
|
124 | 158 | (simple-subject-merge-where parsed-query)))
|
0 commit comments