@logseq/cli
Advanced tools
+1
-1
| { | ||
| "name": "@logseq/cli", | ||
| "version": "0.1.0-alpha.1", | ||
| "version": "0.1.0", | ||
| "description": "Logseq CLI", | ||
@@ -5,0 +5,0 @@ "bin": { |
+9
-2
@@ -15,3 +15,3 @@ ## Description | ||
| All commands excepts for `search` can be used offline or on CI. The `search` command and any command that has an api-query-token option require the [HTTP Server](https://docs.logseq.com/#/page/local%20http%20server) to be turned on. | ||
| All commands can be used offline or on CI. The `search` command and any command that has an api-query-token option require the [HTTP API Server](https://docs.logseq.com/#/page/local%20http%20server) to be turned on. | ||
@@ -58,3 +58,3 @@ Now let's use it! | ||
| # Search your current graph and print results one per line like grep | ||
| # Search your current graph and print highlighted results one per line like grep | ||
| $ logseq search woot -a my-token | ||
@@ -66,2 +66,9 @@ Search found 100 results: | ||
| # Search a local graph | ||
| $ logseq search woot page | ||
| Search found 23 results: | ||
| Node page | ||
| Annotation page | ||
| ... | ||
| # Query a graph locally using `d/entity` id(s) like an integer or a :db/ident | ||
@@ -68,0 +75,0 @@ # Can also specify a uuid string to fetch an entity |
@@ -72,8 +72,8 @@ (ns logseq.cli | ||
| :fn (lazy-load-fn 'logseq.cli.commands.search/search) | ||
| :desc "Search current DB graph" | ||
| :args->opts [:search-terms] :coerce {:search-terms []} :require [:search-terms] | ||
| :desc "Search DB graph" | ||
| :args->opts [:graph :search-terms] :coerce {:search-terms []} :require [:graph] | ||
| :spec cli-spec/search} | ||
| {:cmds ["query"] :desc "Query DB graph(s)" | ||
| :fn (lazy-load-fn 'logseq.cli.commands.query/query) | ||
| :args->opts [:graph :args] :coerce {:args []} :no-keyword-opts true :require [:graph :args] | ||
| :args->opts [:graph :args] :coerce {:args []} :no-keyword-opts true :require [:graph] | ||
| :spec cli-spec/query} | ||
@@ -80,0 +80,0 @@ {:cmds ["export-edn"] :desc "Export DB graph as EDN" |
@@ -67,4 +67,10 @@ (ns logseq.cli.commands.query | ||
| (defn- local-datalog-query [db query*] | ||
| (let [query (into query* [:in '$ '%]) ;; assumes no :in are in queries | ||
| res (d/q query db (rules/extract-rules rules/db-query-dsl-rules))] | ||
| ;; Remove nesting for most queries which just have one :find binding | ||
| (if (= 1 (count (first res))) (mapv first res) res))) | ||
| (defn- local-query | ||
| [{{:keys [graph args graphs properties-readable]} :opts}] | ||
| [{{:keys [graph args graphs properties-readable title-query]} :opts}] | ||
| (let [graphs' (into [graph] graphs)] | ||
@@ -75,9 +81,17 @@ (doseq [graph' graphs'] | ||
| query* (when (string? (first args)) (common-util/safe-read-string {:log-error? false} (first args))) | ||
| ;; If datalog query detected run it or else default to entity lookups | ||
| results (if (and (vector? query*) (= :find (first query*))) | ||
| ;; assumes no :in are in queries | ||
| (let [query' (into query* [:in '$ '%]) | ||
| res (d/q query' @conn (rules/extract-rules rules/db-query-dsl-rules))] | ||
| results (cond | ||
| ;; Run datalog query if detected | ||
| (and (vector? query*) (= :find (first query*))) | ||
| (local-datalog-query @conn query*) | ||
| ;; Runs predefined title query. Predefined queries could better off in a separate command | ||
| ;; since they could be more powerful and have different args than query command | ||
| title-query | ||
| (let [query '[:find (pull ?b [*]) | ||
| :in $ % ?search-term | ||
| :where (block-content ?b ?search-term)] | ||
| res (d/q query @conn (rules/extract-rules rules/db-query-dsl-rules) | ||
| (string/join " " args))] | ||
| ;; Remove nesting for most queries which just have one :find binding | ||
| (if (= 1 (count (first res))) (mapv first res) res)) | ||
| :else | ||
| (local-entities-query @conn properties-readable args))] | ||
@@ -84,0 +98,0 @@ (when (> (count graphs') 1) |
| (ns logseq.cli.commands.search | ||
| "Search command" | ||
| (:require [clojure.pprint :as pprint] | ||
| (:require ["fs" :as fs] | ||
| [clojure.pprint :as pprint] | ||
| [clojure.string :as string] | ||
| [datascript.core :as d] | ||
| [logseq.cli.text-util :as cli-text-util] | ||
| [logseq.cli.util :as cli-util] | ||
| [logseq.cli.text-util :as cli-text-util] | ||
| [logseq.db.common.sqlite-cli :as sqlite-cli] | ||
| [promesa.core :as p])) | ||
@@ -26,19 +29,40 @@ | ||
| (defn search | ||
| [{{:keys [search-terms api-query-token raw limit]} :opts}] | ||
| (-> (p/let [resp (cli-util/api-fetch api-query-token | ||
| "logseq.app.search" | ||
| [(string/join " " search-terms) {:limit limit}])] | ||
| (defn- format-results | ||
| "Results are a list of strings. Handles highlighting search term in results and printing options like :raw" | ||
| [results search-term {:keys [raw api?]}] | ||
| (println "Search found" (count results) "results:") | ||
| (if raw | ||
| (pprint/pprint results) | ||
| (let [highlight-fn (if api? | ||
| highlight-content-query | ||
| #(string/replace % search-term (highlight search-term)))] | ||
| (println (string/join "\n" | ||
| (->> results | ||
| (map #(string/replace % "\n" "\\\\n")) | ||
| (map highlight-fn))))))) | ||
| (defn- api-search | ||
| [search-term {{:keys [api-query-token raw limit]} :opts}] | ||
| (-> (p/let [resp (cli-util/api-fetch api-query-token "logseq.app.search" [search-term {:limit limit}])] | ||
| (if (= 200 (.-status resp)) | ||
| (p/let [body (.json resp)] | ||
| (let [{:keys [blocks]} (js->clj body :keywordize-keys true)] | ||
| (println "Search found" (count blocks) "results:") | ||
| (if raw | ||
| (pprint/pprint blocks) | ||
| (println (string/join "\n" | ||
| (->> blocks | ||
| (map :block/title) | ||
| (map #(string/replace % "\n" "\\\\n")) | ||
| (map highlight-content-query))))))) | ||
| (format-results (map :block/title blocks) search-term {:raw raw :api? true}))) | ||
| (cli-util/api-handle-error-response resp))) | ||
| (p/catch cli-util/command-catch-handler))) | ||
| (defn- local-search [search-term {{:keys [graph raw limit]} :opts}] | ||
| (if (fs/existsSync (cli-util/get-graph-dir graph)) | ||
| (let [conn (apply sqlite-cli/open-db! (cli-util/->open-db-args graph)) | ||
| nodes (->> (d/datoms @conn :aevt :block/title) | ||
| (filter (fn [datom] | ||
| (string/includes? (:v datom) search-term))) | ||
| (take limit) | ||
| (map :v))] | ||
| (format-results nodes search-term {:raw raw})) | ||
| (cli-util/error "Graph" (pr-str graph) "does not exist"))) | ||
| (defn search [{{:keys [graph search-terms api-query-token]} :opts :as m}] | ||
| (if api-query-token | ||
| (api-search (string/join " " (into [graph] search-terms)) m) | ||
| (local-search (string/join " " search-terms) m))) |
@@ -31,2 +31,4 @@ (ns logseq.cli.spec | ||
| :desc "Make properties on entity queries show property values instead of ids"} | ||
| :title-query {:alias :t | ||
| :desc "Invokes local query on :block/title"} | ||
| :api-query-token {:alias :a | ||
@@ -37,3 +39,2 @@ :desc "Query current graph with api server token"}}) | ||
| {:api-query-token {:alias :a | ||
| :require true | ||
| :desc "Api server token"} | ||
@@ -40,0 +41,0 @@ :raw {:alias :r |
@@ -546,5 +546,2 @@ (ns logseq.db.frontend.property | ||
| :hide? true}} | ||
| :logseq.property/created-by {:title "Node created by(deprecated)" | ||
| :schema {:type :string | ||
| :hide? true}} | ||
| :logseq.property/created-by-ref {:title "Node created by" | ||
@@ -551,0 +548,0 @@ :schema {:type :entity |
@@ -84,2 +84,4 @@ (ns logseq.db.sqlite.export | ||
| (defonce ignored-properties [:logseq.property/created-by-ref :logseq.property.embedding/hnsw-label-updated-at]) | ||
| (defn- buildable-properties | ||
@@ -108,27 +110,26 @@ "Originally copied from db-test/readable-properties. Modified so that property values are | ||
| (build-pvalue-entity-default db ent-properties pvalue options'))))))] | ||
| (let [ignored-properties [:logseq.property/created-by-ref]] | ||
| (->> (apply dissoc ent-properties ignored-properties) | ||
| (map (fn [[k v]] | ||
| [k | ||
| ;; handle user closed value properties. built-ins have idents and shouldn't be handled here | ||
| (if (and (not (db-property/logseq-property? k)) | ||
| (or (:block/closed-value-property v) | ||
| (and (set? v) (:block/closed-value-property (first v))))) | ||
| (let [find-closed-uuid (fn [val] | ||
| (or (some #(when (= (:value %) (db-property/property-value-content val)) | ||
| (:uuid %)) | ||
| (get-in properties-config [k :build/closed-values])) | ||
| (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))] | ||
| (if (set? v) | ||
| (set (map #(vector :block/uuid (find-closed-uuid %)) v)) | ||
| [:block/uuid (find-closed-uuid v)])) | ||
| (cond | ||
| (de/entity? v) | ||
| (build-pvalue-entity db (d/entity db k) v properties-config options) | ||
| (and (set? v) (every? de/entity? v)) | ||
| (let [property-ent (d/entity db k)] | ||
| (set (map #(build-pvalue-entity db property-ent % properties-config options) v))) | ||
| :else | ||
| v))])) | ||
| (into {}))))) | ||
| (->> (apply dissoc ent-properties ignored-properties) | ||
| (map (fn [[k v]] | ||
| [k | ||
| ;; handle user closed value properties. built-ins have idents and shouldn't be handled here | ||
| (if (and (not (db-property/logseq-property? k)) | ||
| (or (:block/closed-value-property v) | ||
| (and (set? v) (:block/closed-value-property (first v))))) | ||
| (let [find-closed-uuid (fn [val] | ||
| (or (some #(when (= (:value %) (db-property/property-value-content val)) | ||
| (:uuid %)) | ||
| (get-in properties-config [k :build/closed-values])) | ||
| (throw (ex-info (str "No closed value found for content: " (pr-str (db-property/property-value-content val))) {:properties properties-config}))))] | ||
| (if (set? v) | ||
| (set (map #(vector :block/uuid (find-closed-uuid %)) v)) | ||
| [:block/uuid (find-closed-uuid v)])) | ||
| (cond | ||
| (de/entity? v) | ||
| (build-pvalue-entity db (d/entity db k) v properties-config options) | ||
| (and (set? v) (every? de/entity? v)) | ||
| (let [property-ent (d/entity db k)] | ||
| (set (map #(build-pvalue-entity db property-ent % properties-config options) v))) | ||
| :else | ||
| v))])) | ||
| (into {})))) | ||
@@ -135,0 +136,0 @@ (defn- build-export-properties |
@@ -136,2 +136,3 @@ (ns logseq.db.sqlite.util | ||
| :logseq.kv/graph-local-tx | ||
| :logseq.kv/remote-schema-version]))) | ||
| :logseq.kv/remote-schema-version | ||
| :logseq.kv/graph-text-embedding-model-name]))) |
1230945
0.15%156
4.7%