Advanced Query Question: Find page with 2 specific blocks

More than a couple. Should definitely read much more and practice a lot.

  • Nobody grasped the syntax just by guessing.
  • The advanced queries doc is merely a starting point.
  • Should experiment with simpler queries in tiny test-graphs.

I’m going to answer your individual points in an unprecise but simplified way:

  1. All parts that look like ?name are arbitrarily-named variables.
    • They don’t relate to the page content.
      • They apply to a database maintained by Logseq separately from the markdown files.
    • They are not built-in, so there cannot be a list of them.
      • The names are chosen by the users, the computer doesn’t care.
    • Same-named variables represent:
      • the same single value at any given time
      • but potentially multiple values during the same execution of the same query
    • They are not defined, they are placeholders.
  2. The :where clauses:
    • combine:
      • with an implicit top-level AND
      • sequentially from top to bottom
    • apply before the :find clause
      • They apply to the entries of the database.
        • The first clause filters the whole database, the second clause filters the results of the first clause and so on.
      • Technically speaking, all :find, :keys and :where are at the same level.
        • However, they apply in this order: :where:find:keys
  3. Easier reading.
  4. It is sequential.
    • The same results can be achieved in different line-order (and also in multiple ways).
    • Different orders (and/or ways) can differ in their performance.

Here is a rough but thorough description of what goes on at a middle level:

  • Begin with :where
    • Gather (the IDs of) all blocks which have properties.
      • This is equivalent to filtering-out all blocks without properties.
      • Name those blocks ?details and their properties ?details-props
        • At this point, variable ?details holds a relatively big number of IDs.
    • Then keep only those ?details-blocks that have property material::
      • And name the value ?material
      • At this point, variable ?details holds less IDs than in the beginning.
    • Then keep only if that property’s value is “Aluminium”.
      • This clause cannot be placed earlier than where the variable ?material appears.
      • At this point, variable ?details holds even less IDs than in the beginning.
    • Then gather all (the IDs of) the pages that contain the blocks with ?details-IDs.
      • Name those pages ?page
      • At this point, variable ?page holds relatively many IDs.
        • But not more than the number of ?details-blocks.
    • Then keep only the ?page-pages that have blocks.
      • They all have, because they already contain ?details-blocks.
      • We still need to do this step, in order to consider all other blocks in ?page-pages.
        • Name those blocks ?summary
          • At this point:
            • variable ?summary holds a relatively big number of IDs.
            • ?details-blocks is a subset of ?summary-blocks
              • because although they both belong to the same ?page-pages:
                • ?details-blocks got further filtered by property
                • while ?summary-blocks are not filtered yet
      • Alternatively, it is possible to:
        • begin with ?summary-blocks and deal with ?details-blocks later
          • The relative performance of that approach depends on the actual graph.
        • move the two [?... :block/page ?page] clauses to the bottom
          • That would be less performant.
        • even swap the two [?... :block/page ?page] clauses with each-other
          • That would be worse, though fully valid.
    • Then from ?page-pages keep only the ones that contain blocks with properties.
      • They all do, because they already contain ?details-blocks.
      • We still need to do this step, in order to consider all other blocks with properties.
        • Name those properties ?summary-props
          • At this point, ?details-props is a subset of ?summary-props
    • Then keep only those that have property type::
      • And name the value ?type
      • At this point:
        • variable ?summary holds less IDs than earlier
        • thus variable ?page holds fewer IDs than earlier
        • thus variable ?details also shrinks (if it was to be used again)
    • Then keep only if that property’s value is “Product”.
      • This clause cannot be placed earlier than where the variable ?type appears.
      • At this point:
        • variable ?summary holds even less IDs (if it was to be used again)
        • thus variable ?page holds much fewer IDs
        • thus variable ?details shrinks further (if it was to be used again)
  • Continue with :find
    • Return all the fields of the remaining ?page-pages.
    • If no pages remain, return nothing.
1 Like