Advanced Query: Blocks excluding certain tag

Hi there :wave:

I’d like to do a query of blocks that don’t contain a certain tag, for example #mytag.

This is the query including only blocks that contain #mytag, which works great. It is also sorted by priority and doesn’t include blocks with priority C:

#+BEGIN_QUERY
  {
    :title [:h2 "TODO with #mytag"]
    :query [:find (pull ?b [*])
    :where
      [?b :block/marker ?m]
      [?b :block/ref-pages ?p]
      [?b :block/priority ?pr]
      [?p :block/name ?n]
      [(= "TODO" ?m)]
      [(contains? #{"mytag"} ?n)]
      (or
        [(= ?pr "A")]
        [(= ?pr "B")])
    ]
  :result-transform (fn [result]
  (sort-by (fn [h]
  (get h :block/priority "Z")) result))
  :collapsed? false
  }
  #+END_QUERY

Now, I want to do the opposite query: return all the blocks that don’t contain #mytag.

I’ve tried modifying the contains part with:

(not [(contains? #{"mytag"} ?n)])
[(not contains? #{"mytag"} ?n)]
[(not= ?n "mytag")]

but none of those work.

I’ve been checking out the Datomic Query docs but I couldn’t find the answer there either.

Any help is really welcomed :slightly_smiling_face:

I guess it has to be a way to do this because this simple query works fine:

{{query (and (task TODO) (or (priority B) (priority A)) (not [[read]])))}}

I wonder if there is an easy way to see that query in datalog syntax.

Ok, it is. Just go to the web (logseq.com), open the console and navigate to the page with the query. It will print out the advanced query in the console.

It turns out the correct answer is to use this:

(not [?b :block/path-refs [:block/name "mytag"]])

This is the full query, working great now:

#+BEGIN_QUERY
{
  :title [:h2 "To Do (A and B)"]
  :query [:find (pull ?b [*])
  :where
    [?b :block/uuid] 
    [?b :block/marker ?marker] 
    [?b :block/priority ?priority] 
    [(contains? #{"TODO"} ?marker)] 
    (or 
      [(contains? #{"B"} ?priority)] 
      [(contains? #{"A"} ?priority)]) 
    (not [?b :block/path-refs [:block/name "mytag"]])
  ]
:result-transform (fn [result]
(sort-by (fn [h]
(get h :block/priority "Z")) result))
:collapsed? false
}
#+END_QUERY

EDIT: It turns out it is also important to use contains? for the TODO and priority, instead of =. If not, the query does not return anything.

3 Likes

EDIT: Nevermind, I solved it. Thank you for sharing this.

For future searcher, the fix is at the bottom. You have to wrap the query.

ORIGINAL POST:

This looks very interesting. Is there an extra step to the conversion, as for me, it doesn’t work. Below is what I put. I am not editing the query at all at this stage.

My simple query is below, which displays x number of todos.

{{query (and (todo todo later) (not [[test]]) ) }}

The console log has this loaded which appears to match.

"Datascript query: " [:find (pull ?b [:db/id :block/uuid :block/type :block/left :block/format :block/refs :block/_refs :block/path-refs :block/tags :block/content :block/marker :block/priority :block/properties :block/pre-block? :block/scheduled :block/deadline :block/repeated? :block/created-at :block/updated-at :block/file :block/parent :block/heading-level {:block/page [:db/id :block/name :block/original-name :block/journal-day]} {:block/_parent ...}]) :where [?b :block/uuid] [?b :block/marker ?marker] [(contains? #{"TODO" "LATER"} ?marker)] (not [?b :block/path-refs [:block/name "test"]])]

However when I put this in as an advanced query it doesn’t load anything.

#+BEGIN_QUERY
[:find (pull ?b [:db/id :block/uuid :block/type :block/left :block/format :block/refs :block/_refs :block/path-refs :block/tags :block/content :block/marker :block/priority :block/properties :block/pre-block? :block/scheduled :block/deadline :block/repeated? :block/created-at :block/updated-at :block/file :block/parent :block/heading-level {:block/page [:db/id :block/name :block/original-name :block/journal-day]} {:block/_parent ...}]) :where [?b :block/uuid] [?b :block/marker ?marker] [(contains? #{"TODO" "LATER"} ?marker)] (not [?b :block/path-refs [:block/name "test"]])]
#+END_QUERY

I have tried to simplify it, to get a working starting point, with no luck.

#+BEGIN_QUERY
[
    :find (pull ?b [*]) 
    :where [?b :block/uuid] [?b :block/marker ?marker] [(contains? #{"TODO" "LATER"} ?marker)]
]
#+END_QUERY

RESULTING CORRECTION:

Looking at the docs, the queries are wrapped with a {:query }

#+BEGIN_QUERY
{
    :title [:h2 "Custom title"]
    :query [ :find (pull ?b [*]) 
      :where [?b :block/uuid] [?b :block/marker ?marker] [(contains? #{"TODO" "LATER"} ?marker)]]
}
#+END_QUERY