Procházet zdrojové kódy

Allow advanced queries to pull in dsl rules as needed

- Remove unused query' and when checks from custom-query
- Update bb command to use correct watch
- Provide $EXAMPLE mode in query dsl test to document rule usage
Gabriel Horner před 3 roky
rodič
revize
79bbf21f7d

+ 1 - 1
scripts/src/logseq/tasks/dev.clj

@@ -6,7 +6,7 @@
 (defn watch
   "Watches environment to reload cljs, css and other assets"
   []
-  (shell "yarn watch"))
+  (shell "yarn electron-watch"))
 
 (defn- file-modified-later-than?
   [file comparison-instant]

+ 26 - 20
src/main/frontend/db/query_custom.cljs

@@ -1,11 +1,11 @@
 (ns frontend.db.query-custom
-  "Custom queries."
+  "Handles executing custom queries a.k.a. advanced queries"
   (:require [frontend.state :as state]
-            [clojure.string :as string]
-            [cljs.reader :as reader]
             [frontend.db.query-react :as react]
             [frontend.db.query-dsl :as query-dsl]
             [frontend.db.model :as model]
+            [frontend.db.rules :as rules]
+            [frontend.util.datalog :as datalog-util]
             [clojure.walk :as walk]))
 
 ;; FIXME: what if users want to query other attributes than block-attrs?
@@ -21,24 +21,30 @@
        f))
    l))
 
+(defn- add-rules-to-query
+  "Searches query's :where for rules and adds them to query if used"
+  [{:keys [query] :as query-m}]
+  (let [{:keys [where]} (datalog-util/query-vec->map query)
+        rules-found (datalog-util/find-rules-in-where where (-> rules/query-dsl-rules keys set))]
+    (if (seq rules-found)
+      (-> query-m
+          (update :query (fn [q]
+                           (if (contains? (set q) :in)
+                             (datalog-util/add-to-end-of-query-section q :in ['%])
+                             (into q [:in '$ '%]))))
+          (assoc :rules (mapv rules/query-dsl-rules rules-found)))
+      query-m)))
+
 (defn custom-query
+  "Executes a datalog query through query-react, given either a regular datalog
+  query or a simple query"
   ([query]
    (custom-query query {}))
   ([query query-opts]
-   #_:clj-kondo/ignore
-   (when-let [query' (cond
-                       (and (string? query)
-                            (not (string/blank? query)))
-                       (reader/read-string query)
-
-                       (map? query)
-                       query
-
-                       :else
-                       nil)]
-     (let [repo (state/get-current-repo)
-           query' (replace-star-with-block-attrs! query)]
-       (if (or (list? (:query query'))
-               (not= :find (first (:query query')))) ; dsl query
-         (query-dsl/custom-query repo query' query-opts)
-         (react/react-query repo query' query-opts))))))
+   (custom-query (state/get-current-repo) query query-opts))
+  ([repo query query-opts]
+   (let [query' (replace-star-with-block-attrs! query)]
+     (if (or (list? (:query query'))
+             (not= :find (first (:query query')))) ; dsl query
+       (query-dsl/custom-query repo query' query-opts)
+       (react/react-query repo (add-rules-to-query query') query-opts)))))

+ 1 - 0
src/main/frontend/db/query_dsl.cljs

@@ -1,4 +1,5 @@
 (ns frontend.db.query-dsl
+  "Handles executing dsl queries a.k.a. simple queries"
   (:require [cljs-time.coerce :as tc]
             [cljs-time.core :as t]
             [cljs.reader :as reader]

+ 34 - 0
src/main/frontend/util/datalog.cljc

@@ -0,0 +1,34 @@
+(ns frontend.util.datalog
+  "Utility fns related to datalog queries and rules")
+
+(defn find-rules-in-where
+  "Given where clauses and a set of valid rules, returns rules found in where
+  clause as keywords. A more advanced version of this would use a datalog parser
+and not require valid-rules"
+  [where valid-rules]
+  (->> where
+       flatten
+       (filter #(and (symbol? %) (contains? valid-rules (keyword %))))
+       (map keyword)))
+
+(defn query-vec->map
+  "Converts query vec to query map. Modified version of
+  datascript.parser/query->map which preserves insertion order in case map is
+  converted back to vec"
+  [query-vec]
+  (loop [parsed (array-map) key nil qs query-vec]
+    (if-let [q (first qs)]
+      (if (keyword? q)
+        (recur parsed q (next qs))
+        (recur (update-in parsed [key] (fnil conj []) q) key (next qs)))
+      parsed)))
+
+(defn add-to-end-of-query-section
+  "Adds vec of elements to end of a query section e.g. :find or :in"
+  [query-vec query-kw elems]
+  (let [query-map (query-vec->map query-vec)]
+    (vec
+     (reduce (fn [acc [k v]]
+               (concat acc [k] v (when (= k query-kw) elems)))
+             '()
+             query-map))))

+ 45 - 0
src/test/frontend/db/query_custom_test.cljs

@@ -0,0 +1,45 @@
+(ns frontend.db.query-custom-test
+  (:require [cljs.test :refer [deftest is use-fixtures]]
+            [frontend.test.helper :as test-helper :refer [load-test-files]]
+            [frontend.db.query-custom :as query-custom]
+            [frontend.db.react :as db-react]))
+
+(use-fixtures :each {:before test-helper/start-test-db!
+                     :after test-helper/destroy-test-db!})
+
+(defn- custom-query
+  [query]
+  (db-react/clear-query-state!)
+  (when-let [result (query-custom/custom-query test-helper/test-db query {})]
+    (map first (deref result))))
+
+(deftest custom-query-test
+  (load-test-files [{:file/path "pages/page1.md"
+                     :file/content "foo:: bar
+- NOW b1
+- TODO b2
+- LATER b3
+- b3"}])
+
+  (is (= ["LATER b3"]
+         (map :block/content
+              (custom-query {:query '[:find (pull ?b [*])
+                                      :where
+                                      (block-content ?b "b")
+                                      (task ?b #{"LATER"})]})))
+      "datalog query returns correct results")
+
+  (is (= ["LATER b3"]
+         (map :block/content
+              (custom-query {:query '[:find (pull ?b [*])
+                                      :in $
+                                      :where
+                                      (block-content ?b "b")
+                                      (task ?b #{"LATER"})]})))
+      "datalog query with :in returns correct results")
+
+
+  (is (= ["LATER b3"]
+         (map :block/content
+              (custom-query {:query (list 'and '(task later) "b")})))
+      "Simple query returns correct results"))

+ 16 - 1
src/test/frontend/db/query_dsl_test.cljs

@@ -15,10 +15,25 @@
 
 ;; Test helpers
 ;; ============
+
+(def dsl-query*
+  "When $EXAMPLE set, prints query result of build query. Useful for
+   documenting examples and debugging"
+  (if (some? js/process.env.EXAMPLE)
+    (fn dsl-query-star [& args]
+      (let [old-build-query query-dsl/build-query]
+       (with-redefs [query-dsl/build-query
+                     (fn [& args']
+                       (let [res (apply old-build-query args')]
+                         (println "EXAMPLE:" (pr-str (:query res)))
+                         res))]
+         (apply query-dsl/query args))))
+    query-dsl/query))
+
 (defn- dsl-query
   [s]
   (db/clear-query-state!)
-  (when-let [result (query-dsl/query test-helper/test-db s)]
+  (when-let [result (dsl-query* test-helper/test-db s)]
     (map first (deref result))))
 
 (defn- custom-query

+ 30 - 0
src/test/frontend/util/datalog_test.cljs

@@ -0,0 +1,30 @@
+(ns frontend.util.datalog-test
+  (:require [cljs.test :refer [deftest is]]
+            [frontend.util.datalog :as datalog-util]))
+
+(deftest add-to-end-of-query-in
+  (is (= '[:find ?b
+           :in $ ?query %
+           :where
+           (block-content ?b ?query)]
+         (datalog-util/add-to-end-of-query-section
+          '[:find ?b
+            :in $ ?query
+            :where
+            (block-content ?b ?query)]
+          :in
+          ['%]))
+      "Add to :in in middle of query")
+
+  (is (= '[:find ?b
+           :where
+           (block-content ?b ?query)
+           :in $ ?query %]
+         (datalog-util/add-to-end-of-query-section
+          '[:find ?b
+            :where
+            (block-content ?b ?query)
+            :in $ ?query]
+          :in
+          ['%]))
+      "Add to :in at end of query"))