Fix up model tests and remove unused rules

pull/4140/head
Gabriel Horner 2022-11-09 03:17:39 -05:00 committed by Tienson Qin
parent 96381a9919
commit 70a476dde3
3 changed files with 101 additions and 124 deletions

View File

@ -2,62 +2,63 @@
"Datalog rules for use with logseq.db.schema") "Datalog rules for use with logseq.db.schema")
(def ^:large-vars/data-var rules (def ^:large-vars/data-var rules
"Rules used mainly in frontend.db.model"
;; rule "parent" is optimized for parent node -> child node nesting queries ;; rule "parent" is optimized for parent node -> child node nesting queries
'[[(parent ?p ?c) {:namespace
[?c :block/parent ?p]] '[[(namespace ?p ?c)
[(parent ?p ?c) [?c :block/namespace ?p]]
[?c :block/parent ?t] [(namespace ?p ?c)
(parent ?p ?t)] [?t :block/namespace ?p]
(namespace ?t ?c)]]
;; rule "child" is optimized for child node -> parent node nesting queries :alias
[(child ?p ?c) '[[(alias ?e2 ?e1)
[?c :block/parent ?p]] [?e2 :block/alias ?e1]]
[(child ?p ?c) [(alias ?e2 ?e1)
[?t :block/parent ?p] [?e1 :block/alias ?e2]]
(child ?t ?c)] [(alias ?e1 ?e3)
[?e1 :block/alias ?e2]
[?e2 :block/alias ?e3]]
[(alias ?e3 ?e1)
[?e1 :block/alias ?e2]
[?e2 :block/alias ?e3]]]})
;; rule "namespace" is optimized for child node -> node of upper namespace level nesting queries ;; Rules writing advice
[(namespace ?p ?c) ;; ====================
[?c :block/namespace ?p]] ;; Select rules carefully, as it is critical for performance.
[(namespace ?p ?c) ;; The rules have different clause order and resolving directions.
[?t :block/namespace ?p] ;; Clause order Reference:
(namespace ?t ?c)] ;; https://docs.datomic.com/on-prem/query/query-executing.html#clause-order
;; Recursive optimization Reference:
;; https://stackoverflow.com/questions/42457136/recursive-datalog-queries-for-datomic-really-slow
;; Should optimize for query the decendents of a block
;; Quote:
;; My theory is that your rules are not written in a way that Datalog can optimize for this read pattern - probably resulting in a traversal of all the entities. I suggest to rewrite them as follows:
;; [[(ubersymbol ?c ?p)
;; (?c :ml/parent ?p)]
;; [(ubersymbol ?c ?p)
;; ;; we bind a child of the ancestor, instead of a parent of the descendant
;; (?c1 :ml/parent ?p)
;; (ubersymbol ?c ?c1)]]
;; Select rules carefully, as it is critical for performance. ;; This way of writing the ruleset is optimized to find the descendants of some node. The way you originally wrote it is optimized to find the anscestors of some node.
;; The rules have different clause order and resolving directions.
;; Clause order Reference:
;; https://docs.datomic.com/on-prem/query/query-executing.html#clause-order
;; Recursive optimization Reference:
;; https://stackoverflow.com/questions/42457136/recursive-datalog-queries-for-datomic-really-slow
;; Should optimize for query the decendents of a block
;; Quote:
;; My theory is that your rules are not written in a way that Datalog can optimize for this read pattern - probably resulting in a traversal of all the entities. I suggest to rewrite them as follows:
;; [[(ubersymbol ?c ?p)
;; (?c :ml/parent ?p)]
;; [(ubersymbol ?c ?p)
;; ;; we bind a child of the ancestor, instead of a parent of the descendant
;; (?c1 :ml/parent ?p)
;; (ubersymbol ?c ?c1)]]
;; This way of writing the ruleset is optimized to find the descendants of some node. The way you originally wrote it is optimized to find the anscestors of some node. ;; from https://stackoverflow.com/questions/43784258/find-entities-whose-ref-to-many-attribute-contains-all-elements-of-input
;; Quote:
;; You're tackling the general problem of 'dynamic conjunction' in Datomic's Datalog.
;; Write a dynamic Datalog query which uses 2 negations and 1 disjunction or a recursive rule
;; Datalog has no direct way of expressing dynamic conjunction (logical AND / 'for all ...' / set intersection).
;; However, you can achieve it in pure Datalog by combining one disjunction
;; (logical OR / 'exists ...' / set union) and two negations, i.e
;; (For all ?g in ?Gs p(?e,?g)) <=> NOT(Exists ?g in ?Gs, such that NOT(p(?e, ?g)))
;; from https://stackoverflow.com/questions/43784258/find-entities-whose-ref-to-many-attribute-contains-all-elements-of-input ;; [(matches-all ?e ?a ?vs)
;; Quote: ;; [(first ?vs) ?v0]
;; You're tackling the general problem of 'dynamic conjunction' in Datomic's Datalog. ;; [?e ?a ?v0]
;; Write a dynamic Datalog query which uses 2 negations and 1 disjunction or a recursive rule ;; (not-join [?e ?vs]
;; Datalog has no direct way of expressing dynamic conjunction (logical AND / 'for all ...' / set intersection). ;; [(identity ?vs) [?v ...]]
;; However, you can achieve it in pure Datalog by combining one disjunction ;; (not-join [?e ?v]
;; (logical OR / 'exists ...' / set union) and two negations, i.e ;; [?e ?a ?v]))]
;; (For all ?g in ?Gs p(?e,?g)) <=> NOT(Exists ?g in ?Gs, such that NOT(p(?e, ?g)))
;; [(matches-all ?e ?a ?vs)
;; [(first ?vs) ?v0]
;; [?e ?a ?v0]
;; (not-join [?e ?vs]
;; [(identity ?vs) [?v ...]]
;; (not-join [?e ?v]
;; [?e ?a ?v]))]
])
(def ^:large-vars/data-var query-dsl-rules (def ^:large-vars/data-var query-dsl-rules
"Rules used by frontend.db.query-dsl. The symbols ?b and ?p respectively refer "Rules used by frontend.db.query-dsl. The symbols ?b and ?p respectively refer

View File

@ -16,7 +16,7 @@
[frontend.util :as util :refer [react]] [frontend.util :as util :refer [react]]
[frontend.util.drawer :as drawer] [frontend.util.drawer :as drawer]
[logseq.db.default :as default-db] [logseq.db.default :as default-db]
[logseq.db.rules :refer [rules]] [logseq.db.rules :as rules]
[logseq.db.schema :as db-schema] [logseq.db.schema :as db-schema]
[logseq.graph-parser.config :as gp-config] [logseq.graph-parser.config :as gp-config]
[logseq.graph-parser.text :as text] [logseq.graph-parser.text :as text]
@ -300,16 +300,7 @@
(alias ?page ?e)] (alias ?page ?e)]
(conn/get-db repo-url) (conn/get-db repo-url)
(util/safe-page-name-sanity-lc page) (util/safe-page-name-sanity-lc page)
'[[(alias ?e2 ?e1) (:alias rules/rules))
[?e2 :block/alias ?e1]]
[(alias ?e2 ?e1)
[?e1 :block/alias ?e2]]
[(alias ?e1 ?e3)
[?e1 :block/alias ?e2]
[?e2 :block/alias ?e3]]
[(alias ?e3 ?e1)
[?e1 :block/alias ?e2]
[?e2 :block/alias ?e3]]])
db-utils/seq-flatten db-utils/seq-flatten
(set) (set)
(set/union #{page-id})))) (set/union #{page-id}))))
@ -1585,7 +1576,7 @@
[?p :block/name ?namespace] [?p :block/name ?namespace]
(namespace ?p ?c)] (namespace ?p ?c)]
(conn/get-db repo) (conn/get-db repo)
rules (:namespace rules/rules)
namespace))) namespace)))
(defn- tree [flat-col root] (defn- tree [flat-col root]

View File

@ -1,11 +1,25 @@
(ns frontend.db.model-test (ns frontend.db.model-test
(:require [cljs.test :refer [use-fixtures deftest is]] (:require [cljs.test :refer [use-fixtures deftest is are]]
[frontend.db.model :as model] [frontend.db.model :as model]
[frontend.test.helper :as test-helper :refer [load-test-files]])) [frontend.test.helper :as test-helper :refer [load-test-files]]))
(use-fixtures :each {:before test-helper/start-test-db! (use-fixtures :each {:before test-helper/start-test-db!
:after test-helper/destroy-test-db!}) :after test-helper/destroy-test-db!})
(deftest get-namespace-pages
(load-test-files [{:file/path "pages/a.b.c.md"
:file/content "foo"}
{:file/path "pages/b.c.md"
:file/content "bar"}
{:file/path "pages/b.d.md"
:file/content "baz"}])
(is (= ["a/b" "a/b/c"]
(map :block/name (model/get-namespace-pages test-helper/test-db "a"))))
(is (= ["b/c" "b/d"]
(map :block/name (model/get-namespace-pages test-helper/test-db "b")))))
(deftest get-page-namespace-routes (deftest get-page-namespace-routes
(load-test-files [{:file/path "pages/a.b.c.md" (load-test-files [{:file/path "pages/a.b.c.md"
:file/content "foo"} :file/content "foo"}
@ -18,69 +32,40 @@
(map :block/name (model/get-page-namespace-routes test-helper/test-db "b/c"))) (map :block/name (model/get-page-namespace-routes test-helper/test-db "b/c")))
"Empty if page exists")) "Empty if page exists"))
;; (deftest test-page-alias-with-multiple-alias (deftest test-page-alias-with-multiple-alias
;; [] (load-test-files [{:file/path "aa.md"
;; (p/let [files [{:file/path "a.md" :file/content "alias:: ab, ac"}
;; :file/content "---\ntitle: a\nalias: b, c\n---"} {:file/path "ab.md"
;; {:file/path "b.md" :file/content "alias:: aa, ad"}
;; :file/content "---\ntitle: b\nalias: a, d\n---"} {:file/path "ae.md"
;; {:file/path "e.md" :file/content "## ref to [[ab]]"}])
;; :file/content "---\ntitle: e\n---\n## ref to [[b]]"}] (let [a-aliases (model/page-alias-set test-helper/test-db "aa")
;; _ (-> (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false}) b-aliases (model/page-alias-set test-helper/test-db "ab")
;; (p/catch (fn [] "ignore indexedDB error"))) alias-names (model/get-page-alias-names test-helper/test-db "aa")
;; a-aliases (model/page-alias-set test-db "a") b-ref-blocks (model/get-page-referenced-blocks "ab")
;; b-aliases (model/page-alias-set test-db "b") a-ref-blocks (model/get-page-referenced-blocks "aa")]
;; alias-names (model/get-page-alias-names test-db "a")
;; b-ref-blocks (model/get-page-referenced-blocks test-db "b")
;; a-ref-blocks (model/get-page-referenced-blocks test-db "a")]
;; (are [x y] (= x y)
;; 4 (count a-aliases)
;; 4 (count b-aliases)
;; 1 (count b-ref-blocks)
;; 1 (count a-ref-blocks)
;; (set ["b" "c" "d"]) (set alias-names))))
;; (deftest test-page-alias-set (are [x y] (= x y)
;; [] 4 (count a-aliases)
;; (p/let [files [{:file/path "a.md" 4 (count b-aliases)
;; :file/content "---\ntitle: a\nalias: [[b]]\n---"} 4 (count b-ref-blocks)
;; {:file/path "b.md" 4 (count a-ref-blocks)
;; :file/content "---\ntitle: b\nalias: [[c]]\n---"} #{"ab" "ac" "ad"} (set alias-names))))
;; {:file/path "d.md"
;; :file/content "---\ntitle: d\n---\n## ref to [[b]]"}]
;; _ (-> (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false})
;; (p/catch (fn [] "ignore indexedDB error")))
;; a-aliases (model/page-alias-set test-db "a")
;; b-aliases (model/page-alias-set test-db "b")
;; alias-names (model/get-page-alias-names test-db "a")
;; b-ref-blocks (model/get-page-referenced-blocks test-db "b")
;; a-ref-blocks (model/get-page-referenced-blocks test-db "a")]
;; (are [x y] (= x y)
;; 3 (count a-aliases)
;; 1 (count b-ref-blocks)
;; 1 (count a-ref-blocks)
;; (set ["b" "c"]) (set alias-names))))
;; (deftest test-page-alias-without-brackets (deftest test-page-alias-set
;; [] (load-test-files [{:file/path "aa.md"
;; (p/let [files [{:file/path "a.md" :file/content "alias:: ab"}
;; :file/content "---\ntitle: a\nalias: b\n---"} {:file/path "ab.md"
;; {:file/path "b.md" :file/content "alias:: ac"}
;; :file/content "---\ntitle: b\nalias: c\n---"} {:file/path "ad.md"
;; {:file/path "d.md" :file/content "## ref to [[ab]]"}])
;; :file/content "---\ntitle: d\n---\n## ref to [[b]]"}] (let [a-aliases (model/page-alias-set test-helper/test-db "aa")
;; _ (-> (repo-handler/parse-files-and-load-to-db! test-db files {:re-render? false}) alias-names (model/get-page-alias-names test-helper/test-db "aa")
;; (p/catch (fn [] "ignore indexedDB error"))) a-ref-blocks (model/get-page-referenced-blocks "aa")]
;; a-aliases (model/page-alias-set test-db "a") (are [x y] (= x y)
;; b-aliases (model/page-alias-set test-db "b") 3 (count a-aliases)
;; alias-names (model/get-page-alias-names test-db "a") 3 (count a-ref-blocks)
;; b-ref-blocks (model/get-page-referenced-blocks test-db "b") #{"ab" "ac"} (set alias-names))))
;; a-ref-blocks (model/get-page-referenced-blocks test-db "a")]
;; (are [x y] (= x y)
;; 3 (count a-aliases)
;; 1 (count b-ref-blocks)
;; 1 (count a-ref-blocks)
;; (set ["b" "c"]) (set alias-names))))
(deftest get-pages-that-mentioned-page-with-show-journal (deftest get-pages-that-mentioned-page-with-show-journal
(load-test-files [{:file/path "journals/2020_08_15.md" (load-test-files [{:file/path "journals/2020_08_15.md"