Blocks with a page link if a string is contained in this block or child blocks

Hi all,

I am struggling with building a query for searching my meeting notes. I could not find a working solution that fits all my needs yet. I am not sure if this is even possible. I want to post this question here because I am sure your understanding of queries in Logseq is way better than mine.

What I want to achieve:
I make meeting notes in my journal as child blocks under e.g. #Meeting1. When searching my meeting notes, I want to find:

  • the blocks #Meeting1
  • that contain one or more searchString anywhere in this block or in child blocks
  • I also want to see the page (or the date of the page), that block is on, sorted by date

What I tried:
Simple query
{{query (and [[Meeting1]] "SearchString")}}

This query finds all blocks, that are under a block that references #Meeting1 and contains SearchString. The results are not ordered by date. When I switch to table view, I can sort by pagename (format: Wednesday, 05.03.2025). But this only sorts by the first letter of the weekday. Also, this shows the block where the SearchString appears, not the #Meeting1 block.

{{query (and [[Meeting1]] "SearchString" "SearchString2")}}

If I add a second SearchString2, the results only show blocks that contain both search strings. But I also want to see the block that has both search strings somewhere in its children.

Advanced query
I believe I can solve this by creating an advanced query. I managed to build one that finds the blocks that are linked to the page Meeting1.

#+BEGIN_QUERY
{:title "Testquery"
    :query [:find (pull ?h [*])
            :in $ ?current
            :where
            [?h :block/refs ?r]
            [?r :block/name ?current]]
    :inputs ["[[Meeting1]]"]
    :collapsed? false}
#+END_QUERY

And I found a way to build a query that finds a string in the block content.

#+BEGIN_QUERY
{:title "Testquery String"
 :query [:find (pull ?b [*])
         :in $ ?string
         :where
         [?b :block/content ?c]
         [(not= ?string "")]
         [(re-pattern ?string) ?q]
         [(re-find ?q ?c)]]
 :inputs ["searchString"]
 :collapsed? false}
#+END_QUERY

I don’t know how to combine the two to limit the results from the first query to all blocks that contain a searchString itself or one of its children.

I hope you can give me a hint.

Thank you very much in advance.

Gregor

Something like this:

#+BEGIN_QUERY
{:title "Testquery"
 :inputs ["[[Meeting1]]" "searchString1" "searchString2"]
 :query [:find ?date (pull ?b [*])
     :in $ ?current ?string1 ?string2 %
     :keys date b
     :where
         [?b :block/refs ?r]
         [?r :block/name ?current]
         (or-join [?b ?c1]
             [?b :block/content ?c1]
             (and
                 (descendant ?d ?b)
                 [?d :block/content ?c1]
             )
         )
         [(clojure.string/includes? ?c1 ?string1)]
         (or-join [?b ?c2]
             [?b :block/content ?c2]
             (and
                 (descendant ?d ?b)
                 [?d :block/content ?c2]
             )
         )
         [(clojure.string/includes? ?c2 ?string2)]
         [?b :block/page ?p]
         [?p :block/journal-day ?date]
 ]
 :result-transform (fn [results] (map :b (sort-by :date results)))
 :rules [
     [(descendant ?d ?b)
         [?d :block/parent ?b]
     ]
     [(descendant ?d ?b)
         [?t :block/parent ?b]
         (descendant ?d ?t)
     ]
 ]
}
#+END_QUERY

@mentaloid thanks for your suggestion. This query looks quite complicating. Unfortunately, I cant get it working in my case:

Here is my example meeting note

on Journal Page "Wednesday, 05.03.2025"
*[[Meeting1]]
  * Teaching
    * Subject 1 wont be teached anymore
  * Administration
    * How to order something

When I use your query with Input as follows, I dont get any results.

:inputs ["[[Meeting1]]" "Teaching" "Subject"]

The result should be the block [[Meeting1]] on Journal Page 05.03.2025.

Did it work with you?`

That’s typical of Datalog, especially with composite requirements like yours.

Yes, I tried your example and it returns the result:

  • Ensure that you properly copy the query, without any formatting.
    • It is also characteristic of Datalog to return nothing for a tiny mistake.
  • Ensure that your blocks are in a genuine journal page, not one that is just named like a date.
  • Try a test with a new linked reference, different than Meeting1 (preferably one without capital letters).
  • Try simpler cases of blocks (e.g. put everything in the same block) and smaller versions of the query (e.g. without sorting etc.)
    • If you manage to get any result other than zero, we can further investigate from there.
1 Like

Your query works perfectly fine, thank you very much. :slight_smile: For my first test I somehow did not put the [[Meeting1]] block on a journal page…

I wonder, if it is possible to make the query more flexible concerning the input values? I am thinking of a input list (strings = ["string1" "string2" "string3"...]) with a variable number of values. This would make sense, if the following command could be inside a for-loop, testing the requirement for each string in a list:

for each string : strings do 
 [(clojure.string/includes? ?c1 ?string)]

This would also save the second block, since all input strings (string1 and string2) are tested in the for-loop, right?

(or-join [?b ?c2]
             [?b :block/content ?c2]
             (and
                 (descendant ?d ?b)
                 [?d :block/content ?c2]
             )
         )
         [(clojure.string/includes? ?c2 ?string2)]

Can the query be this flexible?

  • Queries support true scripting only after retrieving their raw results:
    • Datalog cannot directly iterate an input list.
      • Some cases can be covered with hacks, but not yours.
      • Nevertheless, adding multiple strings can become easy-enough with the help of a rule.
    • Clojurescript can properly iterate a list after Datalog has retrieved all the candidate blocks.
  • In any case, such a query would gradually become slow.
    • Searching whole contents for arbitrary strings in random places is a genuinely slow process.
      • It is not much different than using the searching feature for the whole graph.
    • This requirement of yours indicates that most of your search strings should rather be proper tags and looked-up as such.
      • When tags are in place, combinations become both possible and performant.

Thank you for the explanations. I was able to modify your suggested query for my needs. I can now search all children blocks under my meeting notes on journal pages for strings and return the meeting notes blocks sorted by date.

I used (and) and (or) clauses to define how to search for the 4 input strings. In my case I am fine with beeing able to search my meeting notes for blocks that contain “2 strings” or “2 other strings”.

#+BEGIN_QUERY
{:title "Testquery"
 :inputs ["[[Meeting1]]" "String1" "String2" "String3" "String4"]
 :query [:find ?date (pull ?b [*])
     :in $ ?current ?string1 ?string12 ?string2 ?string22 %
     :keys date b
     :where
         [?b :block/refs ?r]
         [?r :block/name ?current]
         (or-join [?b ?c1]
             [?b :block/content ?c1]
             (and
                 (descendant ?d ?b)
                 [?d :block/content ?c1]
             )
         )
         (or
            (and
               [(clojure.string/includes? ?c1 ?string1)] 
               [(clojure.string/includes? ?c1 ?string12)]
            )
            (and
               [(clojure.string/includes? ?c1 ?string2)] 
               [(clojure.string/includes? ?c1 ?string22)]
            )
         )
         [?b :block/page ?p]
         [?p :block/journal-day ?date]
 ]
 :result-transform (fn [results] (map :b (sort-by :date results)))
 ;; Definition of a function to find all children blocks of a block
 :rules [  
     [(descendant ?d ?b)
         [?d :block/parent ?b]
     ]
     [(descendant ?d ?b)
         [?t :block/parent ?b]
         (descendant ?d ?t)
     ]
 ]
}
#+END_QUERY

Example meeting notes on a journal page

Output of the query