feat: simple query builder (#8774)

Simple query builder

---------

Co-authored-by: Gabriel Horner <gabriel@logseq.com>
Co-authored-by: charlie <xyhp915@qq.com>
This commit is contained in:
Tienson Qin
2023-03-21 14:27:00 +08:00
committed by GitHub
parent 7a905583cf
commit db6fc6b7ee
37 changed files with 1263 additions and 295 deletions

View File

@@ -14,15 +14,19 @@
[frontend.template :as template]
[logseq.graph-parser.text :as text]
[logseq.graph-parser.util.page-ref :as page-ref]
[logseq.graph-parser.util :as gp-util]
[frontend.util.text :as text-util]
[frontend.util :as util]))
;; Query fields:
;; Operators:
;; and
;; or
;; not
;; Filters:
;; between
;; Example: (between -7d +7d)
;; (between created-at -1d today)
@@ -32,16 +36,17 @@
;; task (block)
;; priority (block)
;; page
;; sample
;; full-text-search ""
;; namespace
;; page-property (page)
;; page-tags (page)
;; all-page-tags
;; project (block, TBD)
;; Sort by (field, asc/desc):
;; (sort-by created-at asc)
;; (between -7d +7d)
;; Time helpers
;; ============
(defn- ->journal-day-int [input]
@@ -443,23 +448,32 @@ Some bindings in this fn:
;; parse fns
;; =========
(defn- pre-transform
(defonce tag-placeholder "~~~tag-placeholder~~~")
(defn pre-transform
[s]
(let [quoted-page-ref (str "\"" page-ref/left-brackets "$1" page-ref/right-brackets "\"")]
(some-> s
(string/replace page-ref/page-ref-re quoted-page-ref)
(string/replace text-util/between-re
(fn [[_ x]]
(->> (string/split x #" ")
(remove string/blank?)
(map (fn [x]
(if (or (contains? #{"+" "-"} (first x))
(and (util/safe-re-find #"\d" (first x))
(some #(string/ends-with? x %) ["y" "m" "d" "h" "min"])))
(keyword (name x))
x)))
(string/join " ")
(util/format "(between %s)")))))))
(if (gp-util/wrapped-by-quotes? s)
s
(let [quoted-page-ref (fn [matches]
(let [match' (string/replace (second matches) "#" tag-placeholder)]
(str "\"" page-ref/left-brackets match' page-ref/right-brackets "\"")))]
(some-> s
(string/replace page-ref/page-ref-re quoted-page-ref)
(string/replace text-util/between-re
(fn [[_ x]]
(->> (string/split x #" ")
(remove string/blank?)
(map (fn [x]
(if (or (contains? #{"+" "-"} (first x))
(and (util/safe-re-find #"\d" (first x))
(some #(string/ends-with? x %) ["y" "m" "d" "h" "min"])))
(keyword (name x))
x)))
(string/join " ")
(util/format "(between %s)"))))
(string/replace #"\"[^\"]+\"" (fn [s] (string/replace s "#" tag-placeholder)))
(string/replace " #" " #tag ")
(string/replace #"^#" "#tag ")
(string/replace tag-placeholder "#")))))
(defn- add-bindings!
[form q]
@@ -499,17 +513,33 @@ Some bindings in this fn:
:else
q)))
(defn simplify-query
[query]
(if (string? query)
query
(walk/postwalk
(fn [f]
(if (and
(coll? f)
(contains? #{'and 'or} (first f))
(= 2 (count f)))
(second f)
f))
query)))
(def custom-readers {:readers {'tag (fn [x] (page-ref/->page-ref x))}})
(defn parse
[s]
(when (and (string? s)
(not (string/blank? s)))
(let [s (if (= \# (first s)) (page-ref/->page-ref (subs s 1)) s)
form (some-> s
(pre-transform)
(reader/read-string))
form (some->> s
(pre-transform)
(reader/read-string custom-readers))
sort-by (atom nil)
blocks? (atom nil)
sample (atom nil)
form (simplify-query form)
{result :query rules :rules}
(when form (build-query form {:sort-by sort-by
:blocks? blocks?
@@ -545,12 +575,21 @@ Some bindings in this fn:
(apply conj q where)
(conj q where))))
(defn parse-query
[q]
(let [q' (template/resolve-dynamic-template! q)]
(parse q')))
(defn pre-transform-query
[q]
(let [q' (template/resolve-dynamic-template! q)]
(pre-transform q')))
(defn query
"Runs a dsl query with query as a string. Primary use is from '{{query }}'"
[repo query-string]
(when (and (string? query-string) (not= "\"\"" query-string))
(let [query-string' (template/resolve-dynamic-template! query-string)
{:keys [query rules sort-by blocks? sample]} (parse query-string')]
(let [{:keys [query rules sort-by blocks? sample]} (parse-query query-string)]
(when-let [query' (some-> query (query-wrapper {:blocks? blocks?}))]
(let [sort-by (or sort-by identity)
random-samples (if @sample