Sort tasks per tag (often asked, never solved)

I am trying something again, after checking many posts. Nobody seems to be able to solve it.

In the journal I enter this:

Meeting 1 #customer1

  • text
  • TODO action 1
  • TODO action 2

Meeting 2 #customer2

  • text
  • TODO action 3
  • TODO action 4

Meeting 3

  • TODO action 5

Then I want to make a query in the journal template which shows all tasks, grouped by the block header tag. If no tag exists, then all the other tasks can be grouped together. This should result in the output:

#customer1

  • TODO action 1
  • TODO action 2

#customer 2

  • TODO action 3
  • TODO action 4

TODO action 5

In many task-apps this is standard behavior. In Logseq entering all in the Journal is great, but sorting the output you want with many tasks per block header tag.

Currently I use this query, which doesn’t group the tasks per block header tag and doesn’t display the tag:

#+BEGIN_QUERY
{:title "🔨 NOW, TODO or DOING"
:query [:find (pull ?h [*])
:in $ ?start ?today
:where
[?h :block/marker ?marker]
[(contains? #{"NOW" "TODO" "DOING"} ?marker)]
[?h :block/page ?p]
[?p :block/journal? true]
[?p :block/journal-day ?d]
[(>= ?d ?start)]
[(<= ?d ?today)]]
:inputs [:9999d :yesterday]
:result-transform (fn [result]
(sort-by (fn [h]
(get h :block/priority "Z")) result))
:group-by-page? true
:breadcrumb-show? true
:collapsed? false}
#+END_QUERY

Has anyone solved this? I am convinced that it must be possible.

3 Likes

Try this:

#+BEGIN_QUERY
{
:title "Todos grouped by customer"
:query [:find (pull ?b [*])
:where
[?b :block/marker ?m]
[(= ?m "TODO")]]
:group-by-page? false
:result-transform
(fn [result]
(let [by-tag (group-by
(fn [b]
(let [tags (:block/tags b)]
(if (seq tags)
(clojure.string/join ", " tags)
"No Tag")))
result)]
(for [[tag blocks] by-tag]
[:div
[:h3 tag]
(for [b blocks]
[:div (first (:block/content b))])])))
}
#+END_QUERY

Thanks for this. Unfortunately the query gives an error (Logseq version 0.10.13). Can you try again?

Could you list the most relevant ones?

  • What makes you so sure?
  • It most probably isn’t possible.
    • At least not with queries alone.
    • Workarounds could still be possible.
      • e.g. sorting instead of grouping
  • It would help if you were specific about the given error.
  • I formatted the query to avoid errors on copying.
    • It will probably not produce the desired results anyway.
      • It looks like the output of an LLM.

Logseq tags and namespaces are fundamental to Logseq. When logging notes about projects in the Journal and tagging with namespace tags, it must be possible to provide and overview grouped by name-space.

Ιndeed this might work.

This is the output of the query of @menkizar

I hope with this background and additional information, there is someone who can build this formula or already did and is using it. With this, the task management of Logseq again is more complete, closing the gap with more task specialized PKM’s.

1 Like

Try updating your query with the following steps:

  • Replace all (3) ?h with ?b.
    • This is because of an issue with the source code.
  • Add these conditions to get the customer name from the parent-block:
    [?b :block/parent ?parent]
    (or-join [?parent ?ref-name]
      (and
        [?parent :block/refs ?ref]
        [?ref :block/name ?ref-name]
      )
      (and
        (not [?parent :block/refs ?ref])
        [(str "none") ?ref-name]
      )
    )
    
    • Replace "none" with whatever you want to print when no tag exists.
  • Output the customer name by replacing :query line with this:
    :query [:find ?ref-name ?marker (pull ?b [*])
    :keys ref status b
    
  • Extract the output and sort it by customer, by replacing :result-transform lines with this:
    :result-transform (fn [result]
      (map (fn [r]
        (update (:b r) :block/properties (fn [p]
          (assoc p "status" (:status r) "ref" (:ref r) )
        ) )
      ) (sort-by :ref result))
    )
    
  • Switch the query to table view.

Many thanks for this, which resulted in this query:

#+BEGIN_QUERY
{:title "🔨 NOW, TODO or DOING"
:query [:find ?ref-name ?marker (pull ?b [*])
:keys ref status b
:in $ ?start ?today
:where
[?b :block/marker ?marker]
[(contains? #{"NOW" "TODO" "DOING"} ?marker)]
[?b :block/page ?p]
[?p :block/journal? true]
[?p :block/journal-day ?d]
[(>= ?d ?start)]
[(<= ?d ?today)]
[?b :block/parent ?parent]
(or-join [?parent ?ref-name]
(and
[?parent :block/refs ?ref]
[?ref :block/name ?ref-name])
(and
(not [?parent :block/refs ?ref])
[(str "none") ?ref-name]))]
:inputs [:9999d :yesterday]
:result-transform (fn [result]
(map (fn [r]
(update (:b r) :block/properties (fn [p]
(assoc p "status" (:status r) "ref" (:ref r)))))
(sort-by :ref result)))
:view :table
:breadcrumb-show? true
:collapsed? false}
#+END_QUERY

It shows again the tasks but is not yet fully working. There are 2 issues:

  1. I have tasks TEST under namespace Project/meeting1, the result of this query is that it shows TEST 2x in the results. Once for Project and once for Project/meeting1.
  2. I created a task in Project/meeting1 under another block (indent), after which it looses the ref to the namespace.

I hope with this feedback, the query can be fixed?

Need to provide exact examples, like in your opening post. Could also use a separate temporary graph, to further simplify testing.

You are right. Here is the example input and query output:

Input:

All input via the current Journal page:

Journal-date Sept 9th, 2025

Meeting with Peter #Project1/subprojectA

  • Meeting notes
  • WAITING task XYZ for Peter
  • Other Important actions (more indented)
    • TODO task XYZ for Jim
    • TODO task XYZ for Abel

General actions #Project1

  • Information
  • TODO general tasks to not forget

Meeting with Alice #Project2/subprojectA/kickoff

  • Meeting notes
  • TODO task XYZ for Alice

Output in table format, combining open tasks and name-spaces

PROJECT OPEN TASKS:

Status Block/Description Page/Created Ref/Project tag
WAITING task XYZ for Peter Sept 9th, 2025 Project1/subprojectA
TODO ask XYZ for Jim Sept 9th, 2025 Project1/subprojectA
TODO ask XYZ for Abel Sept 9th, 2025 Project1/subprojectA
TODO general tasks to not forget Sept 9th, 2025 Project1
TODO task XYZ for Alice Sept 9th, 2025 Project2/subprojectA/kickoff

Key points:

  • ideally you can also sort on every header in the table, most important on Project Tag.
  • with many projects, this overview continues to look okay
  • ideally you can also change TODO to DONE in the table view
  • tasks inherit the last name-space tag of the block, which is made visible in the table

Hopefully this clarifies the purpose, input and output enough to build the query for this. I think the last query is already very close.

Don’t get carried away with the requirements. This remains a mere workaround.

  • Add these rules:
    :rules [
      [(first-valid-ref-name ?b ?ref-name)
        [?b :block/refs ?ref]
        (not
          [?sub :block/namespace ?ref]
          [?b :block/refs ?sub]
        )
        [?ref :block/name ?ref-name]
      ]
      [(first-valid-ref-name ?b ?ref-name)
        (not [?b :block/refs])
        [?b :block/parent ?parent]
        [?parent :block/page]
        (first-valid-ref-name ?parent ?ref-name)
      ]
    ]
    
    • The presence of :rules requires to alter line :in like this: :in $ ?start ?today %
  • Use the rules in or-join like this:
    (or-join [?parent ?ref-name]
      (first-valid-ref-name ?parent ?ref-name)
      (and
        (not (first-valid-ref-name ?parent ?ref-name))
        [(str "none") ?ref-name]
      )
    )