org-ql 讨论帖

org-ql 非常强大,这两天专门学习了一下它的手册,优点很多。 检索条件全面,可以按照时间、属性、标签来检索。 检索结果可以保存为 org-mode 格式的链接,方便随时调用。 还可以和 dynamic block 结合,将 heading 整理成列表形式,并且可以进行更新。

以下是我整理的部分手册内容:

definition

针对 org-mode 的 query 语法。

project url

https://github.com/alphapapa/org-ql

How to use

安装org-ql

官方推荐用 quelpa 来安装,如果不使用它的话,直接用 straight 的方式也可以。

用法

命令

org-ql 提供 3 种类型的命令,不同命令支持 non-sexp,和 sexp 两种类型的指令:

  1. 直接跳转到对应的内容

    1. org-ql-find

      【仅支持 non-sexp 类型指令】

      1. org-ql-find 在当前打开的 buffer 里进行检索
      2. org-ql-find-in-agenda 在预设好的 (org-agenda-files) 里进行检索
      3. org-ql-find-in-org-directory 在 org-directory 里的文件进行检索
  2. 检索结果将展示成类似 agenda 的视图

    1. org-ql-search

      【支持 non-sexp 和 sexp 两种类型的指令】 该指令提供如下变量:

      1. BUFFERS-FILES

        检索一系列 buffer 或 org 文件

        • buffer 从当前所有打开的 buffer 中进行检索
        • all 检 索所有的 Org 文件类型的 buffer
        • agenda 在 (org-agenda-files) 指定的文件中检索
      2. GROUPS

        从 org-super-agenda-groups 定义的分类中进行检索

      3. NARROW

        :question:When non-nil, don’t widen buffers before searching. Interactively, with prefix, leave narrowed.

      4. SORT

        一个,或一系列 org-ql 用于排序的指令,比如 datepriority

      5. Bindiings 在结果展示 buffer 里使用的键盘快捷键

        • r 当调整了搜索的设置之后,刷新展示的结果
        • v 展示弹出窗口,类似 popups
        • C-x C-s 将检索结果页保存为 org-ql-views
    2. org-ql-views

      选择储存在 org-gl-views 里的视图,并展示出来

      1. Bindings

        • gr 刷新
        • v 以 popup 的方式进行展示
        • C-c C-s 将检索的视图,保存到 org-ql-views
    3. org-ql-view-sidebar

      org-ql-views 的结果以侧边栏的方式展示出来 可用 RETmouse-1 在当前的光标位置展示特定的结果 按下 c 对光标位置展示的结果进行定义

    4. org-ql-view-recent-items

      展示 FILES 里,以 DAYS 的范围内的检索结果。 DAYS 通过 TYPE 参数进行调整,包括 tsts-activets-inactiveclockedcloseddeadlineplannning , =scheduled =

      FILES 默认是 org-agenda-files 指定的文件

  3. 检索结果以树状列表的形式在新开的 buffer 里展示

    1. org-ql-sparse-tree

      Arguments: (query &key keep-previous (buffer (current-buffer)))

      BUFFERS 里检索特定的 QUERY 条件,并以树状列表进行展示

QUERY 检索语法

  1. 原理

    An org-ql query is a Lisp expression which may contain arbitrary expressions, as well as calling certain built-in predicates. It is byte-compiled into a predicate function which is tested with point on each heading in an Org buffer; when it returns non-nil, the heading matches the query. When possible, certain built-in predicates are optimized away to whole-buffer regular expression searches, which are much faster to search for than testing the predicate on each heading.

    • 检索条件采用 < , <= , >= , = 进行比较
  2. Non-sexp 检索语法

    Sexp syntax Non-sexp syntax
    (todo) todo:
    (todo "SOMEDAY") todo:SOMEDAY
    (todo "SOMEDAY" "WAITING") todo:SOMEDAY,WAITING
    (ts :on today) ts:on=today
    (ts-active :from "2017-01-01" :to "2018-01-01") ts-active:from=2017-01-01,to=2018-01-01
    (clocked :on -1) clocked:on=-1
    (heading "quoted phrase" "word") heading:"quoted phrase",word
    (and (tags "book" "books") (priority "A")) tags:book,books priority:A
    (src :lang "elisp" :regexps ("defun")) src:defun,lang=elisp or src:lang=elisp,defun
    (and (tags "space") (not (regexp "moon"))) tags:space !moon
    (priority >= B) priority:A,B
  3. General predicates 一般检索条件

    1. category (&optional categories)
    2. done
    3. effort (&optional effort-or-comparator effort)
    4. habit
    5. heading (&rest strings)
      • 缩写 h
    6. heading-regexp (&rest regexps)
      • 缩写 h*
    7. level (level-or-comparator &optional level)
    8. link (&optional description-or-target &key description target regexp-p)
    9. outline-path (&rest strings)
      • 缩写 olp
    10. outline-path-segment (&rest strings)
      • 缩写 olps
    11. path (&rest regexps)
    12. priority (&rest args)
    13. property (property &optional value)
    14. regexp (&rest regexps)
    15. rifle (&rest strings)
    16. src (&key lang regexps)
    17. tags (&optional tags)
    18. tags-inherited (&optional tags)
    19. tags-local (&optional tags)
    20. tags-all (tags)
    21. tags-regexp (&rest regexps)
    22. todo (&optional keywords)
  4. 根据 headling 的层级进行检索

    • ancestors (&optional query) 一级
    • children (&optional query) 二级
    • descendants (&optional query)
    • parent (&optional query)
  5. 根据未来日期/时间条件进行检索

    检索语句:

    • :from 从当前的 timestamp 开始之后
    • :to 从当前的 timestamp 开始之前
    • :on 仅检索 timestamp 当天
    • :with-time 检索两个 timestamp 期间
    1. 特定时间条件的

      • ts 选取带 timestamp 的 entry
      • ts-activets-a 仅匹配仍在活跃的,带 timestamp 的 entry
      • ts-inactivets-i 仅匹配非活跃的……
    2. 面向历史的

      • clocked 检索结束计时的 entry
      • closed 检索已结束的 entry
    3. 面向未来的

      • deadline 包含 deadline 的 entry,除非提供了特定时间条件
      • planning 包含了 planing 的 timestamp 比如(deadline,scheduled 或 closed)
      • scheduled 包含了 scheduled 的 entry

Functions / Macros

  1. Agenda-like views

    1. org-ql-block

      需要与 org-agenda-custom-commands 进行结合 例子:

      (setq org-agenda-custom-commands
      	  '(("ces" "Custom: Agenda and Emacs SOMEDAY [#A] items"
      		 ((org-ql-block '(and (todo "SOMEDAY")
      							  (tags "Emacs")
      							  (priority "A"))
      						((org-ql-block-header "SOMEDAY :Emacs: High-priority")))
      		  (agenda)))))
      
  2. Listing / acting-on results

    1. org-ql-select

      (buffers-or-files query &key action narrow sort) BUFFERS-OR-FILES is a one or a list of files and/or buffers.

      QUERY is an org-ql query sexp (quoted, since this is a function).

      ACTION is a function which is called on each matching entry with point at the beginning of its heading. It may be:

      element or nil: Equivalent to org-element-headline-parser.

      element-with-markers: Equivalent to calling org-element-headline-parser, with markers added using org-ql–add-markers. Suitable for formatting with org-ql-agenda–format-element, allowing insertion into an Org Agenda-like buffer.

      A sexp, which will be byte-compiled into a lambda function.

      A function symbol.

      If NARROW is non-nil, buffers are not widened (the default is to widen and search the entire buffer).

      SORT is either nil, in which case items are not sorted; or one or a list of defined org-ql sorting methods (date, deadline, scheduled, closed, todo, priority, or random); or a user-defined comparator function that accepts two items as arguments and returns nil or non-nil.

      例子:

        ;; Return list of to-do headings in inbox file with tags and to-do keywords:
      (org-ql-select "~/org/inbox.org"
        '(todo)
        :action #'org-get-heading)
      ;; => ("TODO Practice leaping tall buildings in a single bound  :personal:" ...)
      
      ;; Without tags and to-do keywords:
      (org-ql-select "~/org/inbox.org"
        '(todo)
        :action '(org-get-heading t t))
      ;; => ("Practice leaping tall buildings in a single bound" ...)
      
      ;; Return WAITING heading elements in agenda files:
      (org-ql-select (org-agenda-files)
        '(todo "WAITING")
        :action 'element)
      ;; => ((headline (:raw-value "Visit the moon" ...) ...) ...)
      
      ;; Since `element' is the default for ACTION, it may be omitted:
      (org-ql-select (org-agenda-files)
        '(todo "WAITING"))
      ;; => ((headline (:raw-value "Visit the moon" ...) ...) ...)
      
    2. org-ql-query

      (&key (select 'element-with-markers) from where order-by narrow)

      org-ql-select 类似,但它的检索语法和 SQL 类似

      SELECT corresponds to the org-ql-select argument ACTION.

      FROM corresponds to the org-ql-select argument BUFFERS-OR-FILES.

      WHERE corresponds to the org-ql-select argument QUERY.

      ORDER-BY corresponds to the org-ql-select argument SORT, which see.

      NARROW corresponds to the org-ql-select argument NARROW.

  3. Custom predicates 自定义检索条件

    org-ql/defpred.org

    1. org-ql-defpred

      (name args docstring &key body preambles normalizers)

      org-ql–predicate-NAME. NAME may be a symbol or a list of symbols: if a list, the first is used as NAME and the rest are aliases. A function is only created for NAME, not for aliases, so a normalizer should be used to replace aliases with NAME in queries (keep reading).

      ARGS is a cl-defun-style argument list. DOCSTRING is the function’s docstring.

      BODY is the body of the predicate. It will be evaluated with point on the beginning of an Org heading and should return non-nil if the heading’s entry is a match.

      PREAMBLES and NORMALIZERS are lists of pcase forms matched against Org QL query sexps. They are spliced into pcase forms in the definitions of the functions org-ql–query-preamble and org-ql–normalize-query, which see. Those functions are redefined when this macro is expanded, unless variable org-ql-defpred-defer is non-nil, in which case those functions should be redefined manually after defining predicates by calling org-ql–define-query-preamble-fn and org-ql–define-normalize-query-fn.

      NORMALIZERS are used to normalize query expressions to standard forms. For example, when the predicate has aliases, the aliases should be replaced with predicate names using a normalizer. Also, predicate arguments may be put into a more optimal form so that the predicate has less work to do at query time. NOTE: Normalizers are applied to a query repeatedly until the query is fully normalized, so normalizers should be carefully written to avoid infinite loops.

      PREAMBLES refer to regular expressions which may be used to search through a buffer directly to a potential match rather than testing the predicate body on each heading. (Naming things is hard.) In each pcase form in PREAMBLES, the pcase expression (not the pattern) should be a plist with the following keys, each value of which should be an expression which may refer to variables bound in the pattern:

      :regexp Regular expression which searches directly to a potential match.

      :case-fold Bound to case-fold-search around the regexp search.

      :query Expression which should replace the query expression, or query if it should not be changed (e.g. if the regexp is insufficient to determine whether a heading matches, in which case the predicate’s body needs to be tested on the heading). If the regexp guarantees a match, this may be simply t, leaving the query expression with no work to do, which improves performance.

      For convenience, within the pcase patterns, the symbol predicate-names is a special form which is replaced with a pattern matching any of the predicate’s name and aliases. For example, if NAME were:

      (heading h)

      Then if NORMALIZERS were:

      ((`(,predicate-names . ,args) `(heading ,@args)))

      It would be expanded to:

      ((`(,(or 'heading 'h) . ,args) `(heading ,@args)))

Dynamic block

org-ql 可以和 org-mode 内置的 Daynamic Blocks 功能搭配使用。

:query An Org QL query expression in either sexp or non-sexp form.

:columns A list of columns, including heading, todo, property, priority, deadline, scheduled, closed. Each column may also be specified as a list with the second element being a header string. For example, to abbreviate the priority column: (priority “P”). For certain columns, like property, arguments may be passed by specifying the column type itself as a list. For example, to display a column showing the values of a property named milestone, with the header being abbreviated to M: ((property “milestone”) “M”).

:sort One or a list of Org QL sorting methods (see org-ql-select).

:take Optionally take a number of results from the front (a positive number) or the end (a negative number) of the results.

:ts-format Optional format string used to format timestamp-based columns.

例子:

#+BEGIN: org-ql :query "todo: priority:A,B" :columns (todo (priority "P") ((property "agenda-group") "Group") deadline heading) :sort (deadline priority) :take 7 :ts-format "%Y-%m-%d %H:%M"
| Todo | P | Group | Deadline         | Heading                               |
|------+---+-------+------------------+---------------------------------------|
| TODO | A |       | 2017-07-07 00:00 | Take over the world                   |
| TODO | B |       | 2017-07-10 00:00 | Renew membership in supervillain club |
| TODO | A | plans | 2017-07-15 00:00 | Take over the universe                |
| TODO | B |       | 2017-07-21 00:00 | Internet                              |
| TODO | A | bills | 2017-08-01 00:00 | Spaceship lease                       |
| TODO | A |       |                  | Skype with president of Antarctica    |
| TODO | B |       |                  | Take over Mars                        |
#+END:

Links

支持将 org-ql 与 org-mode 的 links 结合,在点击之后,直接展示 org-ql 检索的结果 你可以将当前 org-ql 检索结果的视图用 org-store-link 命令储存起来,然后再用 org-insert-link 将保存的结果取出,并记录在你想记录的位置

例子:

[[org-ql-search:todo:NEXT priority:A]]
[[org-ql-search:(and (todo "NEXT") (priority "A"))]]

Tips

org-ql view 的 buffer 可以通过 emacs 的 bookmark 命令标记为书签

example

14 个赞

初步上手 org-ql 建议使用 org-ql-serach 命令,因为它提供了交互式命令菜单,在一开始尝试 org-ql 的功能时会比较舒服,降低上手的门槛。

在用 org-ql-search 检索了相关内容之后,这些检索条件可以保存到 org-ql-view 中,下一次调用,可以直接输入 =M-x= org-ql-view,直接唤出,很方便。

org-ql有类似于table的那种嵌入org文件里随时更新的视图吗

还未测试,但从手册里看到,是有类似的,你可以直接搜索本网页「dynamic block」看看。

这是手册上的例子:

实际上,org-mode 的 dynamic block 直接就可以满足你的要求。直接按 C-c C-c 就可以更新。 而且 dynamic block 不必放在任意 org 文件里。

#+title: 书籍阅读记录
#+filetags: :bookreading:
#+COLUMNS: %25ITEM(书名) %10AUTHOR(Author) %10CATEGORY(Category) %15ADDED(Added) %15STAR(Star) %50NOTELINK(Notelink)

#+BEGIN: columnview :hlines 1 :id "file:~/我的坚果云/org-roam/book/book20220704152021-书籍阅读记录.org" :skip-empty-rows t
| 书名                           | Author              | Category | Added            | Star       | Notelink                       |
|--------------------------------+---------------------+----------+------------------+------------+--------------------------------|
| 岩田先生:企业家传奇社长如是说 | HOBO 日刊 ITOI 新闻 | 企业家   | <2022-07-04 Mon> | ⭐️⭐️⭐️⭐️⭐️ | [[id:E23B33EF-C3A7-45DC-9A7A-43E5CBBA2C47][岩田先生:企业家传奇社长如是说]] |
#+END:

orgorg-roam 分属不同的目录,org 分管 agendaorg-roam 分管笔记,如须 org-ql 只在 org-roam 笔记里查找,要怎么设置?

感谢。

你定义一个 load-path 看看行不行。简单来讲,就是你定义一个 org-ql 去检索的文件夹。

一个参考例子:

https://1.anagora.org/querying-org-roam

这篇文章里的代码:

 (org-ql-query
    :select 'element-with-markers
    :from '("~/commonplace/gift-economies-build-community.org")
     :where '(and (heading "Epistemic status"))

:joy:这效果很好呀,是时候重新拿起org-mode做任务管理了

可否利用org-ql refile,比如将一个大org文档的同一个tag的headings聚集生成另一个新的org文档,来分割大文档?

我见过别人好像有做个类似的实现。但我自己不知道。

你其实可以用 org-agenda 的搜索指令来直接找到特定 tag 标记的 heading

嗯嗯,找到特定heading的话相对比较容易,方法也挺多。现在有个需求是想按照tag把一个大文件分割成不同的小文件,不仅仅是heading,还包括heading下面的内容。

你可以试一下组合命令,org-ql 还只是检索,如果你是真要分割,估计要结合 org-refile 来实现。

我看到 emacs 邮件列表里有人提到使用 org-ql 来快速构建 org-agenda (Re: Asynchronous org-agenda-redo),这在有大量的 org-mode 文件时很有用,请问有类似的实现吗?

请问我想获取查询到标题以及标题下的内容应该怎么做呢?

标题以下的内容是指?

想请问大佬org-ql有办法实现递归搜索一个文件夹下的所有.org文件吗,为了层次清晰,我把笔记分类放在一个文件夹下又分了好多层文件夹,我现在用org-ql-search搜索的时候,好像只能搜索当前文件夹下的.org文件,不能搜索更深层的.org文件

我建议你到 github 上提一个 issue,估计会有好心的外国人帮你。我水平有限……

这不是它最最基本的用法么? rifle: 就可以.

添加org-ql-view为org-mode link

在使用org-ql的时候,我发现官方说明里推荐的形如

[[org-ql-search:todo:NEXT priority:A]]
[[org-ql-search:(and (todo "NEXT") (priority "A"))]]

的链接,实际体验存在一定的问题:

  1. 如果通过官方推荐的 org-store-link 方法存储链接,在Windows上会因为汉字的编码造成无法匹配汉字标签;
  2. 重启emacs之后也无法直接点击链接启动,会提示“No Match”(仅在未启动过org-ql时出现此问题)

所以可以保存常用的org-ql-view搜索,然后添加以下代码,通过形如 [[qlview: 个人事项]] 的org-link快速启动org-ql-view指定结果

(org-add-link-type
 "qlview" 'my/org-ql-view)
(defun my/org-ql-view (name)
  "View saved org-ql-view"
  (org-ql-view name))

编辑:org-ql-search也能同样设置默认情况,匹配搜索。添加以下代码,通过形如 [[qlsearch: todo:TODO tags:个人]] 的org-link快速启动org-ql-search搜索指定query内容

(org-add-link-type
 "qlsearch" 'my/org-ql-search)
(defun my/org-ql-search (query)
  "Search query in default mode"
  (org-ql-search (org-agenda-files) query :narrow nil :super-groups '((:auto-tags)) :sort nil))
1 个赞