Generate explicit hierarchy out of properties

  • Any extra clause should be provided by editing the queries directly.
    • i.e. the datalog code found under the various PageTree.roots.queries and PageTree.children.queries
    • One could also define their own keywords and respective queries.
  • In other words, if you can use a normal query to filter the desired pages (whether through tags or any other condition), the same clauses can be applied to this implementation’s queries, so as to limit its processing to the desired pages only.
  • Examples:
    • only pages that contain a specific tag:
      [?some-tag :block/name "mytag"]
      [?child :block/tags ?some-tag]
      
    • only pages that contain all of specific tags:
      [?child :block/tags ?some-tag]
      [?child :block/tags ?some-other-tag]
      
    • only pages that contain any of specific tags:
      (or
        [?child :block/tags ?some-tag]
        [?child :block/tags ?some-other-tag]
      )
      

tried here with car tag example but probably im doing something wrong:
in the excample below i added [?some-tag :block/name “car”] and
[?child :block/tags ?“some-tag”]
, but result is the same


PageTree.roots.queries.from = `[
    :find (pull ?Root [*])
    :in $ ?prop
    :where
    	[?some-tag :block/name "car"]	-add
    	[?child :block/tags ?"some-tag"]      -add
        [?child :block/properties ?child-props]
        [(get ?child-props ?prop) ?child-from]
        [?Root :block/original-name ?Root-original-name]
        [(contains? ?child-from ?Root-original-name)]
        (not
            [?root :block/page ?Root]
            [?root :block/properties ?root-props]
            [(get ?root-props ?prop) ?root-from]
        )
]`;

the approach im looking is to when i type the {{pagetree from, broader}} it gives me the tree but filtering(AND clause) with the tag of the note (like dynamic variables for it to get the tag of the note)

Sure, that would be nice. But it needs parsing and this implementation doesn’t do any.

?"some-tag" should be without quotes, i.e. ?some-tag . Although it would make more sense to rename both to either ?car-tag or simply ?car .

Mind that PageTree.roots.queries is only for the top-level pages in the hierarchy. Should make the same change to PageTree.children.queries. Likewise, you may want to add similar conditions for ?Root, ?Parent and ?Child. Each one of these entities is considered separately. After you start making some progress with the limiting, you may have to try various combinations until you achieve the exact form of limitation that you need. Not being familiar with the domain of your application, I cannot make good guesses. But all kinds of query-based limitations remain possible.

Also, after changing the code, should either:

  • rerun the code (with a kits button)
  • restart Logseq

thanks bro, i though i could somehow adapt a dynamic variable to the code in the query, to it only render the pagetree related to the tag of the note.

How would identical page names be handled in this approach?

E.g., imagine I would have “OPNsense”

  1. as a general topic page to describe the software, take notes on features etc (“resource” in PARA)
  2. as page with maintenance notes etc. (“area” in PARA) and
  3. documentation page on how it is configured in my infrastructure (also kind of “resource”).

In current “default” Logseq hierarchy paradigm, I would have e.g. (1) “Tech/Topics/OPNsense”, (2) “Tech/Homelab/OPNsense” and (3) “Tech/Docs/OPNsense”.

But with property-based hierarchies, I only see the solution in altering titles, like “OPNsense (Topic)” and “OPNsense (Doc)”. Am I missing something?

The problem of name conflicts is addressed with namespaces. I am not aware of any problem with using namespaces that way (i.e. as surnames). Just avoid using them to model hierarchies.

My problem with namespaces are long, not-nice names. They are long, contain slashes, are hard to read / grasp what they are referring to. Esp. in queries and alike. (Off-topic here, but if there are any suggestions on these issues I would be glad to be pointed to them; have not found any so far…).

That’s why I was wondering if I could use the property-based hierarchy approach discussed in this post. But then the problem of identical names arises…

  • I don’t see any obstacle in using properties with a few namespaces when they are actually needed.
  • Namespaces:
    • are not a paradigm in Logseq
      • they are merely a convenience (an abused one)
      • their implementation is relatively basic
      • Logseq’s default hierarchy paradigm is the outliner (i.e. nested blocks)
    • need a slash
      • If that is an issue, should make your own convention on how to separate the parts.
    • should not be long
      • Long namespaces are madness.
    • don’t have to be long
      • 80% of names in a graph don’t need a namespace.
      • 96% of names in a graph don’t need more than one surname.
      • If your own percentages differ a lot, it most probably means that you are mixing irrelevant info in a single graph.
        • If this is your case (it doesn’t seem so), separate the graphs.
  • PARA method:
    • can be implemented in Logseq in more than one ways
    • is not the best method to begin with
      • For sure was not made with Logseq (or graphs) in mind.
  • In my opinion, your examples of namespaces are bad.
    • “Topics” and “Docs” are not namespaces (they are classes).
      • It would make more sense to either:
        • simply tag a block (or an ancestor block) as #topic or #doc
        • make “Topics” and “Docs” children of “OPNsense”
          • Could even keep them inside the same page (default paradigm).
            • Then just collapse their blocks as needed.
  • Follow the links in the opening post, as well as my previous post, to read the discussions on the matter.
    • They contain plenty of material to make your own decision.
    • Please don’t continue here, unless you have technical questions.
    • For non-technical questions, open a new thread under category General.
3 Likes

It would be very helpful to be able to open the links on the right pane with shift-click, and on a new tab with ctrl-click (if the tab plugin is installed), just like with other page links. Is there any way to achieve this?

I agree that it should be like that. There is a dedicated function pageLink that produces the custom links, but I’m not aware of a way to make them equivalent to Logseq’s links.

Is there a way to copy/paste de output of the script into another page?

Context: The output is alqready quite useful for me, but I would like to do some organizational changes (usually organize children living in the same level). However, my output index is very very long and complex. So I want to somehow copy the output and paste it in another page to modify it. Doing it manually is possible but I want to avoid doing it for ALL my maps of content.

Thank you

Follow these steps:

  • Find this line: PageTree.filler(config, div, roots, PageTree.max.depth);
  • Replace it with these lines:
        PageTree.clipboard = [div.textContent.slice(1)];
        PageTree.filler(config, div, roots, PageTree.max.depth);
        navigator.clipboard.writeText(PageTree.clipboard.join("\n"));
    
  • Find this line: const divChild = PageTree.node(button, link);
  • Replace it with these lines:
            const indent = PageTree.max.depth - (depth < 0 ? (PageTree.max.depth / 2) : depth);
            PageTree.clipboard.push("  ".repeat(indent) + "- " + (isItemsNode ? link : "[[" + link.textContent + "]]"))
            const divChild = PageTree.node(button, link);
    
  • Restart.
  • Every time the macro gets executed, the clipboard will get a simple form of the output, which can be pasted somewhere else.
    • With multiple such macros in the same page, only the last one executed will remain in the clipboard.

Thanks. It creates the structure! But they are not links, I mean, I am missing the “[]”. Is it possible to get the square brackets so each block is already a link to each page? Thank you for your help.

ok. I got it. In case it is useful for someone, just need to replace this

PageTree.clipboard.push(" ".repeat(indent) + "- " + (link.textContent || link));

with this:

PageTree.clipboard.push(" ".repeat(indent) + "- " + “[[” + (link.textContent || link) + “]]” );

I have updated my answer with an implementation that covers more cases. Consider using that instead.

1 Like

Did it. Thanks! And btw, since you are quite into this topic, you will most likely know this… Do you know if there is already something (plugins, script…) that does exactly this but without using properties? I mean something that also creates a hierarchical index but based simply on the page links used from parent to the lowest level child page. Like the graph view but text form basically.

Example: Lets say I have these 4 free flying pages where [[MATHS]] is the top parent note

[[MATHS]]

  • Here I mention [[DIFFERENTIATION]]

[[DIFFERENTIATION]]

  • On this other page I mention [[PRODUCT RULE]] and for some reason I mention [[LIMITS]]

[[PRODUCT RULE]]

  • Some interesting info

Desired output:

  • MATHS
    • DIFFERENTIATION
      • PRODUCT RULE
      • LIMITS
  • I’m not aware of another plugin or script.
  • If you want to produce other types of hierarchies, should edit the used queries
    • i.e. the datalog code found under the various:
      • PageTree.roots.queries
        • Here a query that returns the root-node of the hierarchy.
      • PageTree.children.queries
        • Here a query that returns the references or back-references of each node.
          • The combination of keywords is, refs already uses such a query.
            • It is defined under PageTree.children.queries.refs
    • One could also define their own keywords for the respective queries, to be used in the macro.
1 Like