It took me four days to piece together all the information scattered around so I am posting an annotated version of my code for reference.
- This example searches for blocks with a given property and then sorts the results according
to some other properties. - The data is sorted according to two different properties, one in a reversed order.
- The custom
:view
template is a simple ordered list to show that you do not always have to make tables. - The code is as concise and readable as it can get.
#+BEGIN_QUERY
{
; The `:title` key defines the heading for the output.
; In this case, `[:h2 ...]` creates a second-level heading, and `[:i ...]` italicizes "English."
; If you don't specify a title, you'll get an empty line instead.
; Note how adjacent strings are concatenated.
:title [:h2 "My " [:i "English"] " Books"
]
; The `:query` key contains the main logic to retrieve data.
; `:find` specifies what to retrieve; you can use `(pull ?b [*])` to pull _all_ the properties of `?b`.
; `?b` is an arbitrary name; you could just as easily name it `?whatever`.
:query [:find (pull ?b [*])
; `:where` defines the condition(s) for selecting entities.
:where
; In this case, we use a Logseq-specific shorthand to get all entities with the `:language` block property set to "English."
(property ?b :language "English")
]
; `:result-transform` allows you to modify the query results before displaying them.
:result-transform (fn [results]
; Here, we sort the results twice:
; First, by the `:year` property in reverse order (`>` reverses the order).
; Then, by the `:title` property.
; Note that sorting rules must be written inside out, i.e., in reverse order.
; `->>` is called a "thread-last macro." It converts nested function calls into a linear flow of function calls, improving readability.
(->> results
(sort-by (fn [result]
(get-in result [:block/properties :title])) ) ; `get-in` is used to get the values out of nested maps.
(sort-by (fn [result]
(get-in result [:block/properties :year])) >)
))
; `:group-by-page? false` makes it easier to handle the results in a for loop.
:group-by-page? false
; The `:view` key defines how the results will be displayed.
:view (fn [results]
; In this case, we create an ordered list, but you can just as easily create tables or anything you want.
; First we write the code we only need once.
[:ol
; Now we can initiate a for loop for our results.
(for [result results]
; We can simplify our lives by saving everything we need into variables first.
(let [properties (:block/properties result)
title (:title properties)
year (:year properties)]
; Now we can write the rest of the template in a more readable manner.
[:li [:i title] " was released in " year "."]
))
; Now we are out of the loop and can add some more code here if needed.
]
)
}
#+END_QUERY
Here is a picture to show what information the example code takes in and how it renders the results.