From 1d28a792a3d3d73a0616b58d1483b89964265608 Mon Sep 17 00:00:00 2001 From: niquola Date: Fri, 4 Aug 2023 11:56:40 +0100 Subject: [PATCH] datalog sugar --- src/zd/blocks/content.clj | 110 ++++++++++++++++++++++++++++++++++++++ src/zd/datalog.clj | 17 ++++-- src/zd/render.clj | 4 +- 3 files changed, 124 insertions(+), 7 deletions(-) diff --git a/src/zd/blocks/content.clj b/src/zd/blocks/content.clj index 3c683b5..6a2394f 100644 --- a/src/zd/blocks/content.clj +++ b/src/zd/blocks/content.clj @@ -9,6 +9,8 @@ [clojure.pprint :as pprint] [stylo.core :refer [c]] [zd.methods :as methods] + [clojure.walk] + [edamame.core] [zd.utils :as utils])) (defmethod methods/rendercontent :edn @@ -47,6 +49,114 @@ (apply d/query ztx data params) (d/query ztx data))) + + +(defn parse-query [q] + (let [xs (->> (str/split q #"\n") + (remove (fn [s] (or (str/blank? s) (str/starts-with? s "\\"))))) + columns (->> xs + (filterv #(re-matches #"^\s?>.*" %)) + (mapv #(subs % 1)) + (mapv str/trim) + (filterv #(not (str/blank? %))) + (mapv (fn [x] + (if (str/starts-with? x "(") + ['expr (edamame.core/parse-string x)] + (let [[e k] (str/split x #":" 2)] + [(symbol e) (cond + (= k "*") (symbol k) + (nil? k) :meta/docname + :else (keyword k))]))))) + index (atom {}) + find-items (->> (group-by first columns) + (reduce (fn [acc [k xs]] + (if (= 'expr k) + (->> (mapv second xs) + (reduce (fn [acc e] + (swap! index assoc e (count acc)) + (conj acc e)) + acc)) + (let [cs (->> (mapv second xs) (dedupe) (into []))] + (swap! index assoc k (count acc)) + (if (filter (fn [x] (contains? #{'x :?} x)) cs) + (conj acc (list 'pull k ['*])) + (if (seq cs) + (conj acc (list 'pull k cs)) + (conj acc k)))))) + [])) + where-items (->> xs + (filterv #(not (re-matches #"^\s?>.*" %))) + (mapv (fn [x] (edamame.core/parse-string (str/replace (str "[" x "]") #"#" ":symbol/"))))) + where (->> where-items + (mapv (fn [x] + (clojure.walk/postwalk + (fn [y] + (if (and (keyword? y) (= "symbol" (namespace y))) + (str "'" (name y)) + y)) x))))] + (into {:where where + :find find-items + :columns columns + :index @index} ))) + + + +(def q + " +e :parent #organizations +e :rel #rel.partner +p :organization e +p :role #roles.cto +> d +> e:xt/id +> e:rel +> (count e) +> (mean e) +") + +;; (parse-query q) + +(defn render-table-value [ztx v block] + (cond + (symbol? v) (link/symbol-link ztx v) + (string? v) (zentext/parse-block ztx v block) + (number? v) v + (nil? v) "~" + (set? v) [:div {:class (c :flex :items-center [:space-x 2])} + (->> v (map (fn [x] [:div {:key (str x)} (render-table-value ztx x block)])))] + (keyword? v) (str v) + :else (with-out-str (clojure.pprint/pprint v)))) + +(defmethod methods/rendercontent :? + [ztx ctx {{headers :table-of} :ann data :data :as block}] + (let [q (parse-query data) + idx (:index q) + res (d/query ztx (dissoc q :columns :index)) + res (->> res + (mapv (fn [x] + (->> (:columns q) + (mapv (fn [[e c]] + (cond + (list? c) (get-in x [(get idx c)]) + (= c '*) (get-in x [(get idx e)]) + (= c :?) (keys (get-in x [(get idx e)])) + :else (get-in x [(get idx e) c])))))))) + cols (->> (:columns q) (mapv second))] + [:div + [:details {:class (c :text-xs [:mb 2])} + [:summary {:class (c [:text :gray-500]) }"query"] + [:pre {:class (c :border [:p 2] [:bg :gray-100])} + (with-out-str (clojure.pprint/pprint (dissoc q :columns :index :args)))]] + [:table + (into [:thead] + (->> cols (map (fn [col] [:th {:class (c [:py 1] [:bg :gray-100] :border {:font-weight "500"})} (str col)])))) + (into [:tbody] + (for [vs res] + [:tr {:key (hash vs)} + (for [v vs] + [:td {:key v :class (c :border [:px 2] [:py 1] {:vertical-align "top"})} + (render-table-value ztx v block)])]))]])) + (defmethod methods/rendercontent :mm [ztx ctx {d :data :as block}] (let [id (str (gensym)) diff --git a/src/zd/datalog.clj b/src/zd/datalog.clj index 6425b01..d5d89b8 100644 --- a/src/zd/datalog.clj +++ b/src/zd/datalog.clj @@ -1,5 +1,7 @@ (ns zd.datalog (:require [zen.core :as zen] + [clojure.walk] + [clojure.string :as str] [xtdb.api :as xt])) (defn get-state [ztx] @@ -17,16 +19,21 @@ (defn query [ztx query & params] (if-let [{n :node} (get-state ztx)] - (apply xt/q (xt/db n) query params) + (clojure.walk/postwalk + (fn [x] (if (and (string? x) (str/starts-with? x "'")) + (symbol (subs x 1)) + x)) + (apply xt/q (xt/db n) query params)) :no/xtdb)) (defn flatten-doc [ztx {{dn :docname :as m} :zd/meta :as doc}] (let [meta (->> m (map (fn [[k v]] [(keyword "meta" (name k)) v])) - (into {}))] - (-> (dissoc doc :zd/backlinks :zd/subdocs :zd/meta) - (merge meta) - (assoc :xt/id (str (:docname m)))))) + (into {})) + doc (-> (dissoc doc :zd/backlinks :zd/subdocs :zd/meta) + (merge meta) + (assoc :xt/id (str "'" (:docname m))))] + (clojure.walk/postwalk (fn [x] (if (symbol? x) (str "'" x) x)) doc))) ;; TODO rename to zd.datalog (defmethod zen/op 'zd/query diff --git a/src/zd/render.clj b/src/zd/render.clj index e939d58..d727e3e 100644 --- a/src/zd/render.clj +++ b/src/zd/render.clj @@ -92,7 +92,7 @@ (defn render-blocks [ztx ctx {m :zd/meta subs :zd/subdocs :as doc} & [render-subdoc?]] [:div {:class (if (:zd/render-preview? ctx) (c [:overflow-x-auto] [:w-max "50vw"]) - (c [:w "60vw"] [:overflow-x-auto] [:w-max "60rem"]))} + (c [:w "60vw"] #_[:overflow-x-auto] [:w-max "60rem"]))} ;; TODO render errors in doc view (when-let [errs (seq (:errors m))] (methods/renderkey ztx ctx {:data errs :ann {} :key :zd/errors})) @@ -170,7 +170,7 @@ (defn render-doc [ztx ctx doc] [:div {:class (c :flex :flex-col [:flex-grow 1])} (topbar ztx ctx doc) - [:div#blocks {:class (c :flex :flex-row )} + [:div#blocks {:class (c :flex :flex-row :items-start)} (render-blocks ztx ctx doc) (contents-sidebar ztx ctx doc)]])