query.cljs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. (ns query
  2. "A script that queries any db graph from the commandline e.g.
  3. $ yarn -s nbb-logseq script/query.cljs db-name '[:find (pull ?b [:block/name :block/title]) :where [?b :block/created-at]]'"
  4. (:require ["child_process" :as child-process]
  5. [babashka.cli :as cli]
  6. [clojure.edn :as edn]
  7. [clojure.pprint :as pprint]
  8. [datascript.core :as d]
  9. [logseq.db.common.sqlite-cli :as sqlite-cli]
  10. [logseq.db.frontend.rules :as rules]
  11. [nbb.core :as nbb]))
  12. (defn- sh
  13. "Run shell cmd synchronously and print to inherited streams by default. Aims
  14. to be similar to babashka.tasks/shell"
  15. [cmd opts]
  16. (child-process/spawnSync (first cmd)
  17. (clj->js (rest cmd))
  18. (clj->js (merge {:stdio "inherit"} opts))))
  19. (def spec
  20. "Options spec"
  21. {:help {:alias :h
  22. :desc "Print help"}
  23. :verbose {:alias :v
  24. :desc "Print more info"}
  25. :raw {:alias :r
  26. :desc "Print results plainly. Useful when piped to bb"}
  27. :additional-graphs {:alias :a
  28. :coerce []
  29. :desc "Additional graphs to query"}
  30. :entity {:alias :e
  31. :coerce []
  32. :desc "Lookup entities instead of query"}})
  33. (defn query-graph [graph args'' options]
  34. (let [conn (apply sqlite-cli/open-db! (sqlite-cli/->open-db-args graph))
  35. results (if (:entity options)
  36. (map #(when-let [ent (d/entity @conn
  37. (if (string? %) (edn/read-string %) %))]
  38. (cond-> (into {:db/id (:db/id ent)} ent)
  39. (seq (:block/properties ent))
  40. (update :block/properties (fn [props] (map (fn [m] (into {} m)) props)))))
  41. (:entity options))
  42. ;; assumes no :in are in queries
  43. (let [query (into (edn/read-string (first args'')) [:in '$ '%])
  44. res (d/q query @conn (rules/extract-rules rules/db-query-dsl-rules))]
  45. ;; Remove nesting for most queries which just have one :find binding
  46. (if (= 1 (count (first res))) (mapv first res) res)))]
  47. (when (:verbose options) (println "DB contains" (count (d/datoms @conn :eavt)) "datoms"))
  48. (if (:raw options)
  49. (prn results)
  50. (if (zero? (.-status (child-process/spawnSync "which" #js ["puget"])))
  51. (sh ["puget"] {:input (pr-str results) :stdio ["pipe" "inherit" "inherit"]})
  52. (pprint/pprint results)))))
  53. (defn -main [args]
  54. (let [{options :opts args' :args} (cli/parse-args args {:spec spec})
  55. [graph-dir & args''] args'
  56. _ (when (or (nil? graph-dir) (:help options))
  57. (println (str "Usage: $0 GRAPH-NAME [& ARGS] [OPTIONS]\nOptions:\n"
  58. (cli/format-opts {:spec spec})))
  59. (js/process.exit 1))
  60. graph-dirs (cond-> [graph-dir]
  61. (:additional-graphs options)
  62. (into (:additional-graphs options)))]
  63. (doseq [graph-dir graph-dirs]
  64. (query-graph graph-dir args'' options))))
  65. (when (= nbb/*file* (nbb/invoked-file))
  66. (-main *command-line-args*))