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
pull/11196/head
Gabriel Horner 2024-04-03 17:20:36 -04:00
parent 30d218169e
commit c448e9a0d0
2 changed files with 147 additions and 33 deletions

View File

@ -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)

View File

@ -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"))))