Advanced query for pages that are only referenced in certain pages

Hello, I’m relatively new to Logseq (~2 months) but I have been reading a lot about Clojure and Datomics in general. Here I have a somewhat complicated use case that I hope to ask for help.

I’m writing an advanced query to manage my pages. I’m stuck on the following case: I’m trying to filter out all page names that are only referenced by certain pages where the page name starts with ‘@’. I have coded all pretty much all other rules but this one. This is how far I have got.

- #+BEGIN_QUERY
  {:title [:h2 "Query Results"]
   :query [:find ?page_name
           :in $ ?ignored_pages
           :where
  
           ; Filter based on page
           ;
           ; Page cannot be a journal
           [?p :block/journal? false]
  
           ; Page does not have the tags attribute
           (not [?p :block/tags])
  
           ; Filter based on name
           ; 
           ; Get page name
           [?p :block/name ?page_name]
  
           ; Page name is not included in the ignored list
           (not [(contains? ?ignored_pages ?page_name)])
  
           ; Page name does not start with @
           (not [(clojure.string/starts-with? ?page_name "@")])
           
           ; Page name does not include undersore
           (not [(clojure.string/includes? ?page_name "_")])
  
           ; Page name does not include dash
           (not [(clojure.string/includes? ?page_name "-")] )
  
           ; Page name does not start with a number to prevent dates
           (not [(clojure.string/starts-with? ?page_name 0)])
           (not [(clojure.string/starts-with? ?page_name 1)])
           (not [(clojure.string/starts-with? ?page_name 2)])
           (not [(clojure.string/starts-with? ?page_name 3)])
           (not [(clojure.string/starts-with? ?page_name 4)])
           (not [(clojure.string/starts-with? ?page_name 5)])
           (not [(clojure.string/starts-with? ?page_name 6)])
           (not [(clojure.string/starts-with? ?page_name 7)])
           (not [(clojure.string/starts-with? ?page_name 8)])
           (not [(clojure.string/starts-with? ?page_name 9)])
  
           ]
   :inputs [ #{
  
    ; System default
    "a" "b" "c" "contents" "canceled" "cancelled" "card" "done" "doing"
    "favorites" "later" "now" "todo"
  
  
    } ]
   :result-transform (fn [result] (sort result))
   :view (fn [tags]
          [:div
          (for [tag (flatten tags)]
          [:a.tag.mr-1 {:href (str "#/page/" (clojure.string/replace (clojure.string/escape tag) "/" "%2F"))}
          (str "#" tag)]
          )]
          )
  }
  #+END_QUERY

Basically, there are some automatically generated pages that start with ‘@’ from Zotero and Zotero will also generate a large number of tags and pages for authors. I would like to filter them out. My idea is that, if a tag/page is only referenced by pages whose name start with ‘@’, it means this tag is only used by Zotero and therefore, I don’t need to keep track of it. But so far, I don’t see to find a valid solution. I have tried to look into some sort of iterative process, similar to a for loop, but I’m very much limited by my knowledge on queries.

Any pointers would help.
Thank you

1 Like

Welcome.

  • If I understand your requirement, it is equivalent to keeping pages that are either:
    • not referenced by other pages
    • referenced by at least one page that its name doesn’t begin with ‘@’
  • That could be expressed with something like this:
    (or-join [?p]
      (not [?other :block/refs ?p])
      (and
        [?other :block/refs ?p]
        [?other :block/name ?other_name]
        (not [(clojure.string/starts-with? ?other_name "@")])
      )
    )
    
2 Likes

Thank you very much. After adopting your suggestion, and a generous tip from Mastodon, I came up with the following that solves my problem.

- #+BEGIN_QUERY
  {:title [:h2 "Query Results"]
   :query [:find ?page_name
           :in $ ?ignored_pages
           :where
  
           ; Filter based on page
           ;
           ; Page cannot be a journal
           [?p :block/journal? false]
  
           ; Page does not have the tags attribute
           (not [?p :block/tags])
  
           ; Filter based on name
           ; 
           ; Get page name
           [?p :block/name ?page_name]
  
           ; Page name is not included in the ignored list
           (not [(contains? ?ignored_pages ?page_name)])

           ; Define a regex pattern to find:
           ; 1. Page name does not start with @
           ; 2. Page name does not include undersore
           ; 3. Page name does not include dash
           ; 4. Page name does not start with a number to prevent dates
           ;
           [(re-pattern "^[^0-9@_-][^_-]*") ?regex]
           [(re-matches ?regex ?page_name)]

           ; I want to keep pages that are either:
           ; 1. not referenced by any other pages.
           ;    These are perhaps the forgotten tags/pages.
           ; 2. referenced by at least one page that its name doesn’t begin with ‘@’.
           ;    If only referenced by pages starting with '@', it might be auto-generated.
           ;    So I will simply not manage anto-generated tags/pages.
           ;
           (or-join [?p]
               (not [?other :block/refs ?p])
               (and
                   [?matched_blocks :block/refs ?p]
                   [?matched_blocks :block/page ?matched_pages]
                   [?matched_pages :block/name ?matched_page_names]
                   (not [(clojure.string/starts-with? ?matched_page_names "@")])
               )
           )

           ]
   :inputs [ #{
  
    ; System default
    "a" "b" "c" "contents" "canceled" "cancelled" "card" "done" "doing"
    "favorites" "later" "now" "todo" "wait" "waiting"

    ; Page names that do not need to be shown
    "academic pages" "deadline overview" "page overview" "summary of the day"
    "Project"
  
    } ]
   :result-transform (fn [result] (sort result))
   :view (fn [tags]
          [:div
          (for [tag (flatten tags)]
          [:a.tag.mr-1 {:href (str "#/page/" (clojure.string/replace (clojure.string/escape tag) "/" "%2F"))}
          (str "#" tag)]
          )]
          )
  }
  #+END_QUERY

It was Lemmy, actually :wink:

Good to see that you got a solution - and that, once you remove the comments, it’s not even that huge.

1 Like