Check Understanding: `(and ...)` Does Not Behave Logically Inside `(or ...)` - always `true`

Based on my mental model for queries, this query should return nothing by definition:

#+BEGIN_QUERY
  {
          :title "🔨 NOW"
          :query [
                  :find (pull ?h [*])
                  :where
                          [?h :block/marker ?marker]
                          [(contains? #{"NOW" "DOING"} ?marker)]
                          [?h :block/page ?p]
                          [?p :block/journal? true]
                          [(or 
                                (and
                                      true
                                      false
                                )
                          )]
          ]
  }
#+END_QUERY

However, it returns every block in my journal that has a “NOW” or “DOING” marker; implying that (and true false) === true, a logical absurdity.

I have validated my use of (or ...) and true/false with the following:

#+BEGIN_QUERY
  {
          :title "🔨 NOW"
          :query [
                  :find (pull ?h [*])
                  :where
                          [?h :block/marker ?marker]
                          [(contains? #{"NOW" "DOING"} ?marker)]
                          [?h :block/page ?p]
                          [?p :block/journal? true]
                          [(or 
                                false
                                true
                          )]
          ]
  }
#+END_QUERY

Which returns all of the NOW or DOING blocks, as expected (false OR true === true).

#+BEGIN_QUERY
  {
          :title "🔨 NOW"
          :query [
                  :find (pull ?h [*])
                  :where
                          [?h :block/marker ?marker]
                          [(contains? #{"NOW" "DOING"} ?marker)]
                          [?h :block/page ?p]
                          [?p :block/journal? true]
                          [(or 
                                false
                                false
                          )]
          ]
  }
#+END_QUERY

This query returns nothing, as expected, because false OR false === false.

This kind of checking is not working at all with (and ...). The inclusion of the (and ...) in this predicate clause must make the entire predicate clause always return true, which is very confusing for me.

It is indeed strange that this:

(or 
      (and
            true
            false
      )
)

returns true, given that in Clojure it returns false.
However, why do you use or with a single argument?

However, why do you use or with a single argument?

I am using or with a single argument as a technical proof of concept. My use case will eventually use a parent (or ...) clause, so I’m trying to understand a toy example that is as close to my use case as possible. We could place a superfluous false argument after the (and ...) with no change in the result.

Based on my reading, (and ...) is not valid outside of an (or ...) because AND is the default sort of logic :where clauses use. I’m not certain that this is correct and I don’t have a source for it.

Looks to me like a bug. To bypass it, consider moving the and expression in a variable, e.g.:

:where
        [?h :block/marker ?marker]
        [(contains? #{"NOW" "DOING"} ?marker)]
        [?h :block/page ?p]
        [?p :block/journal? true]
        [(and
              true
              false
        ) ?conjunction]
        [(or 
              ?conjunction
        )]

I don’t yet trust my mental model for queries, but I think your proposed solution only works in the case that ?conjunction is true, otherwise the clause that sets the value of ?conjunction will return false, and the entire query is then guaranteed to return no rows regardless if another component of the (or ...) is true.

Here’s an illustrative example of what I mean:

#+BEGIN_QUERY
  {
          :title "🔨 NOW"
          :query [
                  :find (pull ?h [*])
                  :where
                          [?h :block/marker ?marker]
                          [(contains? #{"NOW" "DOING"} ?marker)]
                          [?h :block/page ?p]
                          [?p :block/journal? true]
                          [(and true true) ?conjunction]
                          [(and true false) ?second_conjuction]
                          [(or 
                                ?conjunction
                                ?second_conjunction
                                true
                          )]
          ]
  }
#+END_QUERY

This query always returns nothing.

Your last query works for me, but only after adding the missing n in the first ?second_conjuction.

1 Like

Good eye! It’s a shame we don’t get console errors (or other feedback) for that sort of thing. I can confirm that the (or ...) block appears to work logically with your proposed solution.

Actually there is some error:

Insufficient bindings: #{?second_conjunction}
not bound in [(or ?conjunction ?second_conjunction true)]

but I get it only after removing character :hammer:

Where do you see the error? I have the dev console open, using version 0.9.11 on Windows. My dev console just tells me about some search requests but is otherwise very quiet.

Both in the main pane (left) and the dev console (right):


Before removing character :hammer:, it was as quiet as yours.

Strange, I don’t get this feedback anywhere. I had gotten feedback about queries before when using #+BEGIN_QUERY blocks, but not for this one.

There’s an additional wrinkle; your solution works for true and false, but slightly less trivial cases do not work logically:

#+BEGIN_QUERY
  {
          :title "🔨 NOW"
          :query [
                  :find (pull ?h [*])
                  :where
                          [?h :block/marker ?marker]
                          [(contains? #{"NOW" "DOING"} ?marker)]
                          [?h :block/page ?p]
                          [?p :block/journal? true]
                          [?p :block/journal-day ?d]
                          [(and
                                true
                                (= true false) ;; always going to be true
                          ) ?conjunction]
                          [(or 
                                ?conjunction false
                          )]
                  ]
  }
#+END_QUERY

The (= false true) appears to always evaluate as true. I’ve tried other variations on this, I can’t seem to find any kind of function that’ll return false.

If you add a false to the (and ...), the whole thing will evaluate as false, which is logical. However any individual clauses more complex than true and false are always true.

I’ve opened a GitHub issue: https://github.com/logseq/logseq/issues/9938

Seems to be the same issue, susceptible to the same workaround: [(= true false) ?equality]

According to Datomic Queries:

So this is by design.

1 Like

Oh dear. With no error message, no warning, no indication at all? How very absurdly obtuse!

Well, I suppose that solves the problem for me entirely though, thank you for your help. I feel sorry for the poor saps that come after us; they will certainly waste about as much time on this as I did.

Good luck you poor saps, your attempts to search for this will be in vain.

I feel the confusion is partly because you try to do “programming” things in a “database” language.
Datalog is a data manipulation tool, not a standalone programming language.
It’s a nuance for sure and many programming language “things” are possible, but it is not the core of datalog. They are added specifically for datascript (the implementation of datalog that Logseq uses)

Therefore not actually using data can yield unexpected results.
All “problems” I ever ran into was when trying to do things other than database fetching.

So if we look at the (or (and x y)) that is a very weird statement on it’s own.
At the very least we would expect (or (and x y) z), which you mention doesn’t matter.
What I think does matter is that you’re not using actual statements here.

When working with actual statements, then the (and ) works as expected, but only within an (or ) with mulitple statements.
You can even have (or (and (or ) ) ) statements.

So I’m not sure what you’re trying to get at, but it behaves perfectly normally within the idea/workings of Datalog.

Here’s an extreme example from my own Logseq.

#+BEGIN_QUERY
{:title [:h4 "📝 Journal"]
 :query [:find (pull ?b [*])
  :in $ ?dag ?now ?o ?eo ?m ?em ?a
  :where
   [?p :block/journal-day ?dag]
   [?b :block/page ?p]
   (or-join [?b ?c]
     [?b :block/content ?c]
     (and
       [?b :block/properties ?prop]
       [(get ?prop :laxeermiddel) ?v]
       [(str "lax: " ?v) ?c]
     )
     (and
       [?b :block/properties ?prop]
       [(get ?prop :vitaminepil) ?v]
       [(str "vit: " ?v) ?c]
     )
     (and
       [?b :block/properties ?prop]
       [(get ?prop :🔋🌅) ?v]
       [(str "e-o: " ?v) ?c]
     )
     (and
       [?b :block/properties ?prop]
       [(get ?prop :🔋🌄) ?v]
       [(str "e-m: " ?v) ?c]
     )
     (and
       [?b :block/properties ?prop]
       [(get ?prop :🔋🌌) ?v]
       [(str "e-a: " ?v) ?c]
     )
   )
   (or
     [(= ?c "lax: nee")]
; Trackers bijwerken
     [(= ?c "e-o: ")]
     (and
       [(<= ?o ?now)] ; na 12:30
       [(= ?c "e-m: ")]
     )
     (and
       [(<= ?m ?now)] ; na 17:00
       (or
         [(= ?c "e-a: ")]
         [(= ?c "vit: nee")]
       )
     )
    (and
       [(<= ?o ?now ?eo)] ; 12:30-13:00
       [(clojure.string/starts-with? ?c "e-o: ")]
     )
     (and
       [(<= ?m ?now ?em)] ; 17:00-17:30
       [(clojure.string/starts-with? ?c "e-m: ")]
     )
     (and
       [(<= ?a ?now)] ; na 20:30
       [(clojure.string/starts-with? ?c "e-a: ")]
     )
     [(clojure.string/includes? ?c "📜 Notities")]
; Logging weergeven
     (and 
       [(<= ?now ?eo)] ; voor 13:00
       [(clojure.string/includes? ?c "🌅 Ochtend")]
     )
     (and 
       [(<= ?o ?now ?em)] ; 12:30-17:30
       [(clojure.string/includes? ?c "🌄 Middag")]
     )
     (and 
       [(<= ?m ?now)] ; na 17:00
       [(clojure.string/includes? ?c "🌌 Avond")]
     )
   )
 ]
 :inputs [:today :right-now-ms :today-1230 :today-1300 :today-1700 :today-1730 :today-2030]
 :breadcrumb-show? true
 :group-by-page? true
}
#+END_QUERY

And one with the or - and - or in it.

#+BEGIN_QUERY
{:title [:h4 "📓 Dagboek"]
 :query [:find (pull ?b [*])
  :where
   [?w :block/name "weekplanning"]
   [?pb :block/page ?w]
   [?pb :block/refs ?p]
   [?fb :block/parent ?pb]
   [?fb :block/content ?fc]
   [?p :block/journal? true]
   [?b :block/page ?p]
   [?b :block/content ?c]
   [?r :block/name "📓dagboek"]
   [?d :block/page ?p]
   [?d :block/refs ?r]
   [?o :block/parent ?d]
   [?o :block/content ?open]
   (or-join [?c ?d ?open ?fc]
     [(= ?c "O:")]
     (and
       [(clojure.string/includes? ?fc "Ochtend")]
       (not [(clojure.string/includes? ?fc "| |")])
       [(clojure.string/includes? ?c "Ochtend")]
       [(= ?open "O:")]
     )
     (and
       [(clojure.string/includes? ?fc "Middag")]
       (not [(clojure.string/includes? ?fc "| |")])
       (or
         [(= ?c "M:")]
         [(clojure.string/includes? ?c "Middag")]
       )
       [(= ?open "M:")]
       (not
         [?e :block/parent ?d]
         [?e :block/content ?entry]
         [(= ?entry "O:")]
       )
     )
     (and
       [(clojure.string/includes? ?fc "Avond")]
       (not [(clojure.string/includes? ?fc "| |")])
       (or
         [(= ?c "A:")]
         [(clojure.string/includes? ?c "Avond")]
       )
       [(= ?open "A:")]
       (not
         [?e :block/parent ?d]
         [?e :block/content ?entry]
         [(= ?entry "M:")]
       )
     )
   )
 ]
 :breadcrumb-show? true
 :group-by-page? true
}
#+END_QUERY