How to make a condition optional?

I have a simple query that should print block ids, uuids and refs. The problem is that if a block doesn’t have a ref, it gets filtered out.

I know it can be done with or-join, but I’m not sure how to use it properly.

This query returns an empty result set (as I don’t have any refs in blocks?):

#+BEGIN_QUERY
{
  :query [
    :find ?b ?u ?r
    :keys c1 c2 c3
    :where
      [?b :block/uuid ?u]
      (or-join [?b ?r]
        (and
          [?b :block/refs ?r]))
  ]
  :view (fn [rows] [
    :table [
      :thead [
        :tr
          [:th "block"]
          [:th "uuid"]
          [:th "refs"]
      ]
    ] [
      :tbody (for [r rows] [
        :tr
          [:td (get-in r [:c1])]
          [:td (str (get-in r [:c2]))]
          [:td (get-in r [:c3])]
      ])
    ]
  ])
}
#+END_QUERY

UPD. I created a few links in my blocks, but it hasn’t changed anything. Still no results.

3 things.

  1. An or-join needs to “or” something. In other words it needs 2 conditions of which either is true.
    a. in your case you need a (not [?b :block/refs])
  2. The and clause within an or-join also has to have multiple clauses, else it doesn’t add anything.
  3. The ?r is only relevant within one of your or conditions. Therefore it should not be bound with the rest of the query.

So the where should be:

[?b :block/uuid ?u]
(or-join [?b]
   [?b :block/refs ?r]
   (not [?b :block/refs])
)

Some complex or-joins from my website you could study:
https://siferiax.github.io/#/page/logseq%2Fquery%20tests

  • :green_circle: Deadline <=today unless scheduled in the future
  • :white_check_mark: Unlisted planned tasks
    I also have some fun ones in my blog. Needless to say I use them a lot :smiley:

In all honesty though… you don’t need that or-join at all… because you’d just be querying all blocks regardless of whether they do or don’t have a reference.

Comes back to how you return the result…

Considering it… yeah just disregard the above… the problem is with your :find for what you wish to do. Just pull the blocks as a whole.

Well, I’m trying to investigate how information are stored in the db.

For example, currently I’m learning block/refs, then will have a look at block/page-refs etc.

Honestly? That’s irrelevant in a way.
Also :block/page-refs isn’t really a thing.
You can find the db schema here:

And you can click on any bullet to show it’s data when you have developer mode enabled.

Those two things should be enough to develop queries.

1 Like

Oh and to be clear. Can just do :find (pull ?b [*]), this will give you all the attributes. You can then make tables with :view where you specify what should go in each column.
(get-in r [:c1]) becomes (get-in r [:db/id])

#+BEGIN_QUERY
{:title "Blocks"
 :query [
        :find (pull ?b [*])
        :where
        [?b :block/uuid ?u]
 ]
 :view (fn [rows] [
    :table [
      :thead [
        :tr
          [:th "block"]
          [:th "uuid"]
          [:th "refs"]
      ]
    ] [
      :tbody (for [r rows] [
        :tr
          [:td (get-in r [:db/id])]
          [:td (str (get-in r [:block/uuid]))]
          [:td (get-in r [:block/refs])]
      ])
    ]
  ])
}
#+END_QUERY

In my query I still need to have the ?r variable in the or-join, because I’m querying refs.

However, I’m not sure where should I place it in the second OR item.

:query [
    :find ?b ?u ?r
    :keys c1 c2 c3
    :where
      [?b :block/uuid ?u]  
      (or-join [?b ?r]          ; so I added ?r here
        [?b :block/refs ?r]
        (not [?b :block/refs])  ; where to place ?r now?
      )
  ]

Can just do :find (pull ?b [*]) , this will give you all the attributes. You can then make tables with :view where you specify what should go in each column.

That’s cool, thank you! Less typing. I didn’t know what (pull ?b [*]) was doing exactly.

1 Like

Wow! I had dev mode disabled. Let me investigate what it shows!

1 Like

In a nutshell it says “give me all attributes for this entity”.
Logseq will then render the output a certain way, unless we override it.
To be very precise, it is a pattern… According to datatomic:
“Pull is a declarative way to make hierarchical (and possibly nested) selections of information about entities. Pull applies a pattern to a collection of entities, building a map for each entity.”

@Siferiax I need you help again.

So I look at the Dev-info for a block:

If I understand correctly, its ID is 50.

Now when I searching for it:

#+BEGIN_QUERY
{:title "Blocks"
 :query [
        :find (pull ?b [*])
        :where
        [?b :block/name]
        [(= ?b 50)]
 ]
}
#+END_QUERY

I receive “No matched result”.

The same is if I use UUID instead:

#+BEGIN_QUERY
{:title "Blocks"
 :query [
        :find (pull ?b [*])
        :where
        [?b :block/uuid ?u]
        [(= (str ?u) "6616ad65-7b46-4adf-a884-14927393a793")]
 ]
}
#+END_QUERY

Ideas?

Firstly, you have a query which asks, give me entities that

  • have attribute :block/name
  • and have an entity id of 50

You graph does not have such an entity, and as such you will not get a result.

Secondly, please refer to chapter:
https://docs.datomic.com/pro/query/query.html#expression-clauses
At the bottom: “Expression clauses do not nest”
When you don’t nest your expression clauses that second query does work.

#+BEGIN_QUERY
{:title "Blocks"
 :query [
        :find (pull ?b [*])
        :where
        [?b :block/uuid ?u]
        [(str ?u) ?strid]
        [(= ?strid "6617b178-d015-4409-8e34-27bfb9585adf")]
 ]
}
#+END_QUERY

1 Like

Thank you! :pray:

It all makes sense now.

This DEV-thing rocks!

1 Like