فهرست منبع

enhance: convert has-page-property rule with tests

also started on page-property rule. Tests confirm that
binding won't be an issue unlike the previous version
of these rules
Gabriel Horner 1 سال پیش
والد
کامیت
c448e9a0d0
2فایلهای تغییر یافته به همراه147 افزوده شده و 33 حذف شده
  1. 47 33
      deps/db/src/logseq/db/frontend/rules.cljc
  2. 100 0
      deps/db/test/logseq/db/frontend/rules_test.cljs

+ 47 - 33
deps/db/src/logseq/db/frontend/rules.cljc

@@ -154,52 +154,66 @@
   (merge
    query-dsl-rules
    {:page-tags
-   '[(page-tags ?p ?tags)
-     [?p :block/tags ?t]
-     [?t :block/name ?tag]
-     [(missing? $ ?p :block/link)]
-     [(contains? ?tags ?tag)]]
+    '[(page-tags ?p ?tags)
+      [?p :block/tags ?t]
+      [?t :block/name ?tag]
+      [(missing? $ ?p :block/link)]
+      [(contains? ?tags ?tag)]]
 
     :has-page-property
     '[(has-page-property ?p ?prop)
       [?p :block/name]
-      [?p :block/properties ?bp]
-      [(name ?prop) ?prop-name-str]
-      [?prop-b :block/name ?prop-name-str]
-      [?prop-b :block/type "property"]
-      [?prop-b :block/uuid ?prop-uuid]
-      [(get ?bp ?prop-uuid) ?v]
+      [?p ?prop ?v]
+      [?prop-e :db/ident ?prop]
+      [?prop-e :block/type "property"]
       ;; Some deleted properties leave #{} which this rule shouldn't match on
       [(not= #{} ?v)]]
+    ;; TODO: Delete when DB_GRAPH query-dsl tests are passing
+    #_'[(has-page-property ?p ?prop)
+        [?p :block/name]
+        [?p :block/properties ?bp]
+        [(name ?prop) ?prop-name-str]
+        [?prop-b :block/name ?prop-name-str]
+        [?prop-b :block/type "property"]
+        [?prop-b :block/uuid ?prop-uuid]
+        [(get ?bp ?prop-uuid) ?v]
+      ;; Some deleted properties leave #{} which this rule shouldn't match on
+        [(not= #{} ?v)]]
 
     :page-property
     '[;; Clause 1: Match non-ref values
-      [(page-property ?p ?key ?val)
+      [(page-property ?p ?prop ?val)
        [?p :block/name]
-       [?p :block/properties ?prop]
-       [(name ?key) ?key-str]
-       [?prop-b :block/name ?key-str]
-       [?prop-b :block/type "property"]
-       [?prop-b :block/uuid ?prop-uuid]
-       [(get ?prop ?prop-uuid) ?v]
-       (or ([= ?v ?val])
-           [(contains? ?v ?val)])]
+       [?p ?prop ?val]
+       [?prop-e :db/ident ?prop]
+       [?prop-e :block/type "property"]]
+      ;; TODO: Delete when DB_GRAPH query-dsl tests are passing
+      #_[(page-property ?p ?key ?val)
+         [?p :block/name]
+         [?p :block/properties ?prop]
+         [(name ?key) ?key-str]
+         [?prop-b :block/name ?key-str]
+         [?prop-b :block/type "property"]
+         [?prop-b :block/uuid ?prop-uuid]
+         [(get ?prop ?prop-uuid) ?v]
+         (or ([= ?v ?val])
+             [(contains? ?v ?val)])]
 
       ;; Clause 2: Match values joined by refs
-      [(page-property ?p ?key ?val)
-       [?p :block/name]
-       [?p :block/properties ?prop]
-       [(name ?key) ?key-str]
-       [?prop-b :block/name ?key-str]
-       [?prop-b :block/type "property"]
-       [?prop-b :block/uuid ?prop-uuid]
-       [(get ?prop ?prop-uuid) ?v]
-       [(str ?val) ?str-val]
+      #_[(page-property ?p ?key ?val)
+         [?p :block/name]
+         [?p :block/properties ?prop]
+         [(name ?key) ?key-str]
+         [?prop-b :block/name ?key-str]
+         [?prop-b :block/type "property"]
+         [?prop-b :block/uuid ?prop-uuid]
+         [(get ?prop ?prop-uuid) ?v]
+         [(str ?val) ?str-val]
       ;; str-val is for integer pages that aren't strings
-       [?prop-val-b :block/original-name ?str-val]
-       [?prop-val-b :block/uuid ?val-uuid]
-       (or ([= ?v ?val-uuid])
-           [(contains? ?v ?val-uuid)])]]
+         [?prop-val-b :block/original-name ?str-val]
+         [?prop-val-b :block/uuid ?val-uuid]
+         (or ([= ?v ?val-uuid])
+             [(contains? ?v ?val-uuid)])]]
 
     :has-property
     '[(has-property ?b ?prop)

+ 100 - 0
deps/db/test/logseq/db/frontend/rules_test.cljs

@@ -0,0 +1,100 @@
+(ns logseq.db.frontend.rules-test
+  (:require [cljs.test :refer [deftest is testing]]
+            [datascript.core :as d]
+            [logseq.db.frontend.schema :as db-schema]
+            [logseq.db.frontend.rules :as rules]
+            [logseq.db.sqlite.create-graph :as sqlite-create-graph]
+            [logseq.db.sqlite.util :as sqlite-util]))
+
+(defn- new-db-conn []
+  (let [conn (d/create-conn db-schema/schema-for-db-based-graph)
+        _ (d/transact! conn (sqlite-create-graph/build-db-initial-data "{}"))]
+    conn))
+
+(defn q-with-rules [query db]
+  ;; query assumes no :in given
+  (d/q (into query [:in '$ '%])
+       db
+       (rules/extract-rules rules/db-query-dsl-rules)))
+
+(deftest has-page-property-rule
+  (let [conn (new-db-conn)
+        _ (d/transact! conn [(sqlite-util/build-new-property :user.property/foo "foo" {})
+                             (sqlite-util/build-new-property :user.property/foo2 "foo2" {})
+                             (assoc (sqlite-util/build-new-page "Page") :block/format :markdown)
+                             {:block/original-name "Page" :user.property/foo "bar"}])]
+    (is (= ["Page"]
+           (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (has-page-property ?b :user.property/foo)]
+                              @conn)
+                (map (comp :block/original-name first))))
+        "has-page-property returns result when page has property")
+    (is (= []
+           (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (has-page-property ?b :user.property/foo2)]
+                              @conn)
+                (map (comp :block/original-name first))))
+        "has-page-property returns no result when page doesn't have property")
+    (is (= [:user.property/foo]
+           (q-with-rules '[:find [?p ...]
+                           :where (has-page-property ?b ?p) [?b :block/original-name "Page"]]
+                         @conn))
+        "has-page-property can bind to property arg")))
+
+(deftest page-property-rule
+  (let [conn (new-db-conn)
+        _ (d/transact! conn [(sqlite-util/build-new-property :user.property/foo "foo" {})
+                             (sqlite-util/build-new-property :user.property/foo2 "foo2" {})
+                             (sqlite-util/build-new-property :user.property/number-many "number-many" {:type :number :cardinality :many})
+                             (assoc (sqlite-util/build-new-page "Page") :block/format :markdown)
+                             {:block/original-name "Page" :user.property/foo "bar"}
+                             {:block/original-name "Page" :user.property/number-many #{5 10}}])]
+    (testing "cardinality :one property"
+      (is (= ["Page"]
+             (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (page-property ?b :user.property/foo "bar")]
+                                @conn)
+                  (map (comp :block/original-name first))))
+          "page-property returns result when page has property")
+      (is (= []
+             (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (page-property ?b :user.property/foo "baz")]
+                                @conn)
+                  (map (comp :block/original-name first))))
+          "page-property returns no result when page doesn't have property value")
+      (is (= #{:user.property/foo}
+             (->> (q-with-rules '[:find [?p ...]
+                                  :where (page-property ?b ?p "bar") [?b :block/original-name "Page"]]
+                                @conn)
+                  set))
+          "page-property can bind to property arg with bound property value"))
+
+    (testing "cardinality :many property"
+      (is (= ["Page"]
+             (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (page-property ?b :user.property/number-many 5)]
+                                @conn)
+                  (map (comp :block/original-name first))))
+          "page-property returns result when page has property")
+      (is (= []
+             (->> (q-with-rules '[:find (pull ?b [:block/original-name]) :where (page-property ?b :user.property/number-many 20)]
+                                @conn)
+                  (map (comp :block/original-name first))))
+          "page-property returns no result when page doesn't have property value")
+      (is (= #{:user.property/number-many}
+             (->> (q-with-rules '[:find [?p ...]
+                                  :where (page-property ?b ?p 5) [?b :block/original-name "Page"]]
+                                @conn)
+                  set))
+          "page-property can bind to property arg with bound property value"))
+
+    (testing "binding when property value is unspecified"
+      (is (= #{:user.property/foo :user.property/number-many}
+             (->> (q-with-rules '[:find [?p ...]
+                                  :where (page-property ?b ?p _) [?b :block/original-name "Page"]]
+                                @conn)
+                  set))
+          "page-property can bind to property arg with unbound property value")
+      (is (= #{[:user.property/number-many 10]
+               [:user.property/number-many 5]
+               [:user.property/foo "bar"]}
+             (->> (q-with-rules '[:find ?p ?v
+                                  :where (page-property ?b ?p ?v) [?b :block/original-name "Page"]]
+                                @conn)
+                  set))
+          "page-property can bind to property and property value args"))))