diff --git a/src/main/frontend/components/query_table.cljs b/src/main/frontend/components/query_table.cljs index 34cb96b51..ad702bbe5 100644 --- a/src/main/frontend/components/query_table.cljs +++ b/src/main/frontend/components/query_table.cljs @@ -40,7 +40,7 @@ (remove (property/built-in-properties)) (remove #{:template})) keys (if page? (cons :page keys) (cons :block keys)) - keys (if page? (concat keys [:created-at :updated-at]) keys)] + keys (if page? (distinct (concat keys [:created-at :updated-at])) keys)] keys)) (defn attach-clock-property @@ -85,11 +85,12 @@ query-properties (get-keys result page?)) included-keys #{:created-at :updated-at} - keys (if (some included-keys keys) - (concat included-keys - (remove included-keys keys) - (filter included-keys keys)) - keys) + keys (distinct + (if (some included-keys keys) + (concat (remove included-keys keys) + (filter included-keys keys) + included-keys) + keys)) sort-by-fn (fn [item] (let [key sort-by-item] (case key diff --git a/src/main/frontend/db/model.cljs b/src/main/frontend/db/model.cljs index a7464a4f0..cfa532115 100644 --- a/src/main/frontend/db/model.cljs +++ b/src/main/frontend/db/model.cljs @@ -13,36 +13,12 @@ [frontend.format :as format] [frontend.state :as state] [frontend.util :as util :refer [react]] - [medley.core :as medley])) + [medley.core :as medley] + [frontend.db.rules :refer [rules]])) ;; TODO: extract to specific models and move data transform logic to the ;; correponding handlers. -(def rules - '[[(parent ?p ?c) - [?c :block/parent ?p]] - [(parent ?p ?c) - [?c :block/parent ?t] - (parent ?p ?t)] - - ;; 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))) - - ;; [(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]))] - ]) - ;; Use it as an input argument for datalog queries (defonce block-attrs '[:db/id diff --git a/src/main/frontend/db/query_dsl.cljs b/src/main/frontend/db/query_dsl.cljs index ca02fec31..751a60d8d 100644 --- a/src/main/frontend/db/query_dsl.cljs +++ b/src/main/frontend/db/query_dsl.cljs @@ -317,10 +317,18 @@ nil)) (= 'page fe) - (let [page-name (string/lower-case (first (rest e))) + (let [page-name (string/lower-case (str (first (rest e)))) page-name (text/page-ref-un-brackets! page-name)] [['?b :block/page [:block/name page-name]]]) + (and (= 'namespace fe) + (= 2 (count e))) + (let [page-name (string/lower-case (str (first (rest e)))) + page (text/page-ref-un-brackets! page-name)] + (when-not (string/blank? page) + [['?p :block/namespace '?parent] + ['?parent :block/name page]])) + (= 'page-property fe) (let [[k v] (rest e) k (string/replace (name k) "_" "-")] diff --git a/src/main/frontend/db/react.cljs b/src/main/frontend/db/react.cljs index 3e5ac227c..0e57fe4ee 100644 --- a/src/main/frontend/db/react.cljs +++ b/src/main/frontend/db/react.cljs @@ -12,7 +12,8 @@ [frontend.db.utils :as db-utils] [frontend.state :as state] [frontend.util :as util :refer [profile react]] - [frontend.util.marker :as marker])) + [frontend.util.marker :as marker] + [frontend.db.rules :as rules])) ;; Query atom of map of Key ([repo q inputs]) -> atom ;; TODO: replace with LRUCache, only keep the latest 20 or 50 items? @@ -129,6 +130,10 @@ (set! (.-state result-atom) result) (add-q! k nil nil result-atom identity identity identity)))))) +(defn add-rules-to-inputs + [inputs] + (conj (vec inputs) rules/rules)) + (defn q [repo k {:keys [use-cache? transform-fn query-fn inputs-fn] :or {use-cache? true diff --git a/src/main/frontend/db/rules.cljs b/src/main/frontend/db/rules.cljs new file mode 100644 index 000000000..d114b4ea5 --- /dev/null +++ b/src/main/frontend/db/rules.cljs @@ -0,0 +1,32 @@ +(ns frontend.db.rules) + +(def rules + '[[(parent ?p ?c) + [?c :block/parent ?p]] + [(parent ?p ?c) + [?c :block/parent ?t] + (parent ?p ?t)] + + [(namespace ?p ?c) + [?c :block/namespace ?p]] + [(namespace ?p ?c) + [?c :block/namespace ?t] + (namespace ?p ?t)] + + ;; 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))) + + ;; [(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]))] + ])