{"id":17003842,"url":"https://github.com/cldwalker/logseq-query","last_synced_at":"2025-04-07T17:11:53.163Z","repository":{"id":42441510,"uuid":"454676260","full_name":"cldwalker/logseq-query","owner":"cldwalker","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-30T04:59:46.000Z","size":114,"stargazers_count":221,"open_issues_count":6,"forks_count":9,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-06T18:53:02.851Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cldwalker.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-02-02T07:11:11.000Z","updated_at":"2025-03-23T17:31:18.000Z","dependencies_parsed_at":"2023-02-16T04:05:14.655Z","dependency_job_id":null,"html_url":"https://github.com/cldwalker/logseq-query","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cldwalker%2Flogseq-query","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cldwalker%2Flogseq-query/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cldwalker%2Flogseq-query/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cldwalker%2Flogseq-query/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cldwalker","download_url":"https://codeload.github.com/cldwalker/logseq-query/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247694877,"owners_count":20980733,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T04:32:52.136Z","updated_at":"2025-04-07T17:11:53.131Z","avatar_url":"https://github.com/cldwalker.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Description\n\n`lq`, short for `logseq-query`, is a commandline tool for querying\n[logseq](https://logseq.com/) knowledge graphs. `lq` makes it easy to define\ncustom datalog queries and rules and invoke them from the commandline. Rules and\nqueries are just [EDN data](https://github.com/edn-format/edn) and can be\ncomposed to make complex queries easy to read and write.\n\n## Setup\n\nInstall `lq` from npm:\n\n`npm install logseq-query -g`\n\n## Usage\n\n_Note_: This section assumes basic familiarity with datalog queries. For a\nprimer on them, see http://www.learndatalogtoday.org/. For the visual learners,\n[check out the demo](https://www.youtube.com/watch?v=h8bEwKHY4rI)!\n\n\n`lq` knows about your local logseq graphs in `~/.logseq`. For example:\n\n```sh\n$ lq graphs\n\n|        :name |                                                                                           :path |\n|--------------+-------------------------------------------------------------------------------------------------|\n|       lambda |                   /Users/me/.logseq/graphs/logseq_local_++Users++me++code++work++lambda.transit |\n| logseq-notes |             /Users/me/.logseq/graphs/logseq_local_++Users++me++code++priv++logseq-notes.transit |\n|   test-notes | /Users/me/.logseq/graphs/logseq_local_++Users++me++code++repo++logseq-query++test-notes.transit |\nTotal: 3\n```\n\n`lq` runs queries against one of these graphs. To specify a default graph (and\nto avoid having to specify one on every query), add this to your lq config with\nyour `GRAPH`:\n\n```sh\necho '{:default-options {:graph \"GRAPH\"}}' \u003e ~/.lq/config.edn\n```\n\nLet's look at some query and rule commands:\n\n```sh\n# List queries including ones you define\n$ lq queries\n\n|           :name | :namespace |          :parent |                                                      :desc |\n|-----------------+------------+------------------+------------------------------------------------------------|\n|  content-search |         lq |                  |                         Full text search on :block/content |\n|    has-property |         lq |                  |                       List blocks that have given property |\n|        property |         lq |                  | List all blocks that have property equal to property value |\n|    property-all |         lq |                  |                       List all blocks that have properties |\n| property-counts |         lq | :lq/property-all |                            Counts for all block properties |\n| property-search |         lq |                  |                               Full text search on property |\n|            task |         lq |                  |                      Todos that contain one of the markers |\nTotal: 7\n\n# Pull up query-specific help\n$ lq q content-search -h\nUsage: lq q [OPTIONS] content-search QUERY\nFull text search on :block/content\n...\n\n# Queries run by their :name\n$ lq q content-search foo\n...\n\n# If two queries have the same :name, invoke their full name i.e. :namespace/:name\n$ lq q lq/content-search foo\n\n# Queries can be exported and used in logseq! Here we copy that to osx's clipboard\n$ lq q content-search -e | pbcopy\n\n# List rules including ones you define\n$ lq rules\n\n|             :name | :namespace |                                                             :desc |\n|-------------------+------------+-------------------------------------------------------------------|\n|     page-property |     logseq | Pages that have property equal to value or that contain the value |\n|     block-content |     logseq |                   Blocks that have given string in :block/content |\n|         namespace |     logseq |                                                                   |\n|              page |     logseq |                                                                   |\n|      has-property |     logseq |                                   Blocks that have given property |\n|     all-page-tags |     logseq |                                                                   |\n| has-page-property |     logseq |                                    Pages that have given property |\n|          priority |     logseq |                                                                   |\n|           between |     logseq |                                                                   |\n|              task |     logseq |                                 Tasks that contain one of markers |\n|         page-tags |     logseq |                                                                   |\nTotal: 11\n```\n\n### Queries\n\nThe `q` command runs one of the named queries from the previous section as well as any user-defined queries.\n\nLet's try one of the default queries, `property`, which finds blocks/lines with a specific property value:\n```sh\n$ lq q property type digital-garden\n[{:block/uuid #uuid \"620e8da6-e960-4e81-918e-4678db577794\", :block/properties {:url \"https://note.xuanwo.io/#/page/database\", :type \"digital-garden\", :desc \"Great to see one with active use of type\"}, :block/left #:db{:id 3436}\n...\n```\n\nQuery results print as [EDN](https://github.com/edn-format/edn) by default. This\nallows tools like [babashka](https://github.com/babashka/babashka) to transform\nresults easily e.g.\n\n```sh\n$ lq q property type digital-garden | bb '(-\u003e\u003e *input* (map #(-\u003e % :block/properties :url)))'\n(\"https://note.xuanwo.io/#/page/database\" \"https://kvistgaard.github.io/sparql/\" \"https://zettelkasten.sorenbjornstad.com/\")\n```\n\n`lq` provides useful transformations with the following options:\n\n* `-c`: Prints the count of the results\n* `-p`: Colorizes and pretty prints results with [puget](https://github.com/greglook/puget), assuming `puget` is available as a command\n* `-C`: Prints only the contents of block results. This is useful to search\n  through query results easily, which is not possible with logseq\n  ```sh\n  $ lq q content-search babashka -C |grep blog\n  desc:: another #babashka script for a custom blog but this time with netlify\n  ...\n  ```\n* `-t`: Prints results in a table. If it's a block, it will print the :block/properties e.g.\n\n  ```sh\n  $ lq q property type digital-garden -t\n  |  :id |                                     :url |          :type |                                    :desc |\n  |------+------------------------------------------+----------------+------------------------------------------|\n  | 3407 |   https://note.xuanwo.io/#/page/database | digital-garden | Great to see one with active use of type |\n  | 6674 |     https://kvistgaard.github.io/sparql/ | digital-garden |        sparql tutorials made with logseq |\n  | 6600 | https://zettelkasten.sorenbjornstad.com/ | digital-garden | \"remnote employee, made with tiddlywiki\" |\n  ...\n  ```\n* `--tag-counts`: Prints tag counts sorted most to least for any query returning blocks\n  ```sh\n  $ lq q content-search babashka --tag-counts\n  ([\"todo\" 5]\n   [\"done\" 3]\n   [\"eng\" 2]\n   ...\n  ```\n\nFor more options, see `lq q -h`.\n\n### Short queries\n`lq` provides short queries through the `sq` command. This command allows you to specify\nas little or as much of a query from the commandline.\n\nSome examples:\n\n```sh\n# A single where clause can be specified as is\n$ lq sq '(block-content ?b \"github.com/\")'\n...\n\n# For multiple where clauses, wrap it in a vector\n$ lq sq '[(block-content ?b \"github.com/\") (task ?b #{\"DONE\"})]'\n...\n\n# Queries without a :find default to `(pull ?b [*])`. This can be overridden with an explicit :find\n$ lq sq '[:find ?b :where (block-content ?b \"github.com/\") (task ?b #{\"DONE\"})]'\n...\n\n# To print what the full query looks like\n$ lq sq '[:find ?b :where (block-content ?b \"github.com/\") (task ?b #{\"DONE\"})]' -e\n```\n\nThe `sq` command supports most of the `q` options. For the full list of\navailable options, see `lq sq -h`.\n\n### Create a query\n\nWhere `lq` shines is in how easy it is to define new queries. Referencing [this\nsection](#queries.edn), a query is a map entry in `queries.edn` where the map\nkey is its name and the value is a map with `:query` and `:desc`.edn`. Let's add\nthe query from the last section:\n\n```sh\n# Copies the last command's output to clipboard in osx\n$ lq sq '[:find ?b :where (block-content ?b \"github.com/\") (task ?b #{\"DONE\"})]' -n | pbcopy\n```\n\nIn `queries.edn`, paste the clipboard and add a `:desc`:\n\n```clojure\n;; cldwalker is my namespace but feel free to choose your own e.g. github username\n:cldwalker/github-tasks\n{:query\n [:find\n  (pull ?b [*])\n  :where\n  (block-content ?b \"github.com/\")\n  (task ?b #{\"DONE\"})]\n :desc \"Find github tasks\"}\n```\n\nThis query can now be run as `lq q github-tasks`!\n\nTo make this query more useful, let's give this query arguments and transform\nthem with `:in` and `:args-transform` keys respectively. For example:\n\n```clojure\n:cldwalker/github-tasks\n{:query\n [:find\n  (pull ?b [*])\n  ;; $ and % are needed for all our queries at the beginning and end respectively\n  ;; and refer to database and rules\n  :in $ ?markers %\n  :where\n  (block-content ?b \"github.com/\")\n  (task ?b ?markers)]\n :args-transform (fn [args]\n                   (set (map (comp clojure.string/upper-case name) args)))\n :desc \"Find github tasks\"}\n```\n\nThis query can now be called with arguments e.g. `lq q github-tasks todo doing`.\n\nIt's worth noting that queries can use any of the rules that come with `lq` e.g.\n`block-content` as well as _any_ you define. Just _use_ the rules and `lq`\nwill figure out how to pull the rules into your query.\n\n### Create a rule\n\nDatalog rules allow you to bundle multiple where clauses behind one clause. They\nare a great way to compose functionality, leverage datalog's terse power and\nmake queries more readable. Referencing [this section](#rules.edn), a rule is a\nmap entry in `rules.edn` where the key is its name and the value is a map with\n`:rule` and `:desc` keys. For example, to reuse the `github-tasks` query in\nother queries:\n\n```clojure\n;; cldwalker is my namespace but feel free to choose your own e.g. github username\n:cldwalker/github-task\n{:rule\n [(github-task ?b ?markers)\n  (block-content ?b \"github.com/\")\n  (task ?b ?markers)]\n:desc \"Github tasks\"]}\n```\n\nWith this rule defined, use it in a short query to find github tasks that\ncontain the word logseq e.g.\n\n```sh\nlq sq '[(github-task ?b #{\"TODO\"}) [?b :block/content \"logseq\"]]'\n```\n\n## Config\n\nlq has three optional config files under `~/.lq/`. Config files allow you to\nadd functionality to `lq`.\n\n* [config.edn](#config.edn) - General configuration\n* [queries.edn](#queries.edn) - Define custom queries\n* [rules.edn](#rules.edn) - Define custom rules\n\n_Note_: This tool is alpha and there may be breaking changes with configuration\nuntil it stabilizes.\n\nFor examples of these configs, see [mine](https://github.com/cldwalker/dotfiles/tree/master/.lq).\n\n### config.edn\n\nThis is the main config file. It is a map with the following keys:\n\n* `:default-options` (map): Provides default values for options to `q` and `sq` commands.\n\n### queries.edn\n\nThis file defines custom queries similar to [logseq's advanced queries](https://docs.logseq.com/#/page/advanced%20queries). Queries are maps with the following keys:\n\n* `:query` (vector): A logseq/datascript datalog query. Any lq rules can be used\n  in a query\n* `:desc` (string): A brief description of the query\n* `:parent` (keyword): Refer to an existing query in order to inherit its key\n  values. The most common use case is to apply different result-transforms on\n  the same query\n* `:result-transform` (fn): Fn to transforms query results. Same as logseq\n* `:default-args` (vector): Default arguments to pass to query if none are given\n* `:args-transform` (fn): Fn to transform arguments\n* `:usage` (string): Argument string to print for help. Useful when args are transformed\n\n### rules.edn\n\nThis file defines custom [datalog\nrules](https://docs.datomic.com/on-prem/query/query.html#rules). Rules allow you\nto group `:where` clauses in a query. Rules are maps with the following keys:\n\n* `:rule` (vector) - A datalog rule\n* `:desc` (string) - A brief description of the rule\n\n## Motivation\n\nThis project aims to empower logseq users to access and transform their\nknowledge in fine-grained ways from the commandline. This project is also a\ngreat place to experiment with querying. Since this is a commandline tool,\nhopefully this inspires folks to script their logseq graphs and try useful\nthings with them e.g. querying across graphs, joining graphs with external data\nsources, running queries in CI, etc.\n\n## Development\n\n### REPL\n\nInteracting via a REPL is possible if this repository is cloned. Then\n`nbb-logseq repl` to start a repl and `lq bb socket-repl PORT` to start a socket\nrepl to connect your editor to. `cldwalker.logseq-query.tasks` ns is for non\nquery fns and `cldwalker.logseq-query.datascript` is for query fns.\n\n### Testing\n\nRun all tests with `nbb-logseq -cp src:test:resources test/test_runner.cljs`.\n\nEnd to end query tests are in `cldwalker.logseq-query.queries-test`. These tests\nquery against the logseq graph `test-notes`. Each query/test has its own pages\nand is isolated from others thanks to `datascript.core/filter`. To add a new\ntest:\n* Add a new test page and relevant data to the graph, with logseq!\n* With logseq \u003e= 0.6.3, run the command `Save current graph to disk` to save the\n  graph to ~/.logseq.\n* Run `bb copy-test-db` to copy the logseq db under `test/`.\n\n## Contributing\n\nI'm not seeking major contributions to this project though discussion and issues\non github are always welcome. I may be interested in a query or rule\ncontribution if it's general enough. For those contributions, I would want a test for\nthe new functionality. See [testing](#testing) for more.\n\n## License\nSee LICENSE.md\n\n## Credits\n* 🪵 [Logseq](https://github.com/logseq/logseq) - For being the fastest,\n  user-friendliest triples editor I've seen yet\n* 🔥 [Nbb](https://github.com/babashka/nbb) - Opening up blazing ClojureScript\n  CLIs to the NodeJS ecosystem\n* 📀 [Datascript](https://github.com/tonsky/datascript) - For bringing a modern,\n  open-source datalog to the frontend and backend\n\n## Additional Links\n* [Datalevin](https://github.com/juji-io/datalevin#babashka-pod) - another datalog\ndb that can be scripted with babashka\n* [Zsh autocompletion for lq](https://github.com/cldwalker/dotfiles/blob/f5f474f9a323bd9bebe3410152d8ca4967caee87/.zsh/completions.zsh#L63-L69)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcldwalker%2Flogseq-query","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcldwalker%2Flogseq-query","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcldwalker%2Flogseq-query/lists"}