How to query for indirect page links with certain depth?

Hello, is it somehow possible to use advanced queries to get all indirect/transitive links for a page?

image

Example content for page “Knowledge Graph”:

uses:: [[Graph (datastructure)]]

Query [[Graph (datastructure)]] would return all direct links, Knowledge Graph here. I’d like to have Logseq to be returned as well. As result can grow quickly, some attribute like max depth would be useful to provide.

This may help you along for your answer:
https://qwxlea.github.io/#/page/62b0345a-34d4-4d0c-a847-8a6489f5fdb6
This searches recursively.
In this case specifically for TODO items.
Let me know if you need more help/have further questions. I can get to them when I have a bit more time.

1 Like

would you consider using the logseq-graph-analysis plugin (or the page graph) ?
I often lookup indirect links with it (search then toggle filter, set n-hops to 1)

1 Like

Wow, this is tricky. I think I have understood that recursive query roughly, but needed to have a look at yet another advanced query tutorial only for this construct :sweat_smile:

That would be great! Much appreciated.

Thanks for the hint! I tried that plugin directly before. Overall the features seem to be great. Though I am not satisfied with overall graph widget performance (vanilla and plugin), even on idle. My next thought then was: “Why not get near nodes directly via query engine?”

Vanilla page graph (the one in the sidebar) would be an interesting alternative, but to my knowledge, it only can display direct neighbors and has no depth-level slider.

So this… in theory… works…

#+BEGIN_QUERY
{
 :query [:find (pull ?p [*])
         :in $ %
         :where
         [?c :block/name "bewegen"]
         (pr ?p ?c)
       ]
 :rules [
         [(pr ?p ?c)
               [?c :block/tags ?p]
          ]
         [(pr ?p ?c)
               [?c :block/tags ?t]
               (pr ?p ?t)
         ]
           ]
}
#+END_QUERY

There is no max depth though and it will return just a long list of pages.
I don’t know how helpful that is tbh.
For example in my graph I have:

  • areas
    • fysieke gezondheid
      • bewegen

The above query returns fysieke gezondheid and areas. It is basically a slight modification of the query I send earlier.
However it isn’t capped. I know you could manually cap it along the lines of:

#+BEGIN_QUERY
{
 :query [:find (pull ?gp [*])
         :where
         [?c :block/name "bewegen"]
         [?c :block/tags ?p]
         (or [?c :block/tags ?gp]
               [?p :block/tags ?gp])
       ]
}
#+END_QUERY

Sorry that I do not have a better answer than this.

Also this assumes the use of tags:: for linking pages.
So bewegen would have tags:: [[fysieke gezondheid]]
And fysieke gezondheid in turn would have tags:: [[areas]]

EDIT: Wait… you were asking for the reverse lol… I’ll go edit the queries.
EDIT: Changed it up a bit :slight_smile: should find parents/grandparents/etc. now.

1 Like

Thank you so much! I certainly will have something to start and play around now.
I also begin to question, whether this will be practical or rather academical :smiley:.

My hidden end goal was to consider both incoming and outcoming connections . And filter by multiple page links. But again same conclusion as above :wink: . The best probably would be a wizard query builder or a more performant graph widget.

1 Like

I have now created a simple recursive query, that collects adjacent nodes in the graph. But if I don’t pay attention with the notes, it will result in an infinite loop and freeze Logseq lol. Example of pages A, B, C, D referencing each other in a cycle:

A -> B
^    |
|    v
D <- C

Already visited pages would need to be saved in a collection to not re-process them. Is it somehow possible within Logseq Datomic query dialect to create a temporary list or set and append elements to this list? Checking for existence then should be possible with contains?.

In a total naive way, I tried this:

[#{} ?mylist]
[(conj ?mylist ?p)]
# or
[(clojure.core/conj ?mylist ?p)]

, and one encountered problem is that conj seems to be not available:

“Unknown predicate 'conj in [(conj ?mylist ?p)]”

“Unknown rule 'conj in (conj ?mylist ?p)”