Hello community,
I’m trying to figure out how to improve my advanced queries by considering page properties. I have a large number of pages and some page names are only used as page/block property names. Is there a way I can extract these names?
For example, I have the following properties for a page named “Paperwork”
tags:: Project
project-type:: [[Fun]]
status:: [[Completed]]
completion-date:: [[2024/04/10]]
Let’s say the page tags
is only used as property names but the page completion-date
might be used as a regular tag by other blocks. How do I extract tags
but not completion-date
? Generally, I would like to match page names that are only used as block/page property names.
Please understand we cannot search for property pages directly.
That is to say, there is no list of properties to access directly.
So there’s two options:
- Get all pages that are not referenced… which will potentially get more pages than just properties.
- Do something complex, but I just crashed my test graph
![:joy: :joy:](https://discuss.logseq.com/images/emoji/twitter/joy.png?v=12)
The first option would simply be:
#+BEGIN_QUERY
{:title "Pages that are not referenced"
:query [:find (pull ?p [*])
:where
[?p :block/name _]
(not [?b :block/refs ?p])
]
}
#+END_QUERY
Something that you may find useful, is a simple list of all the property names:
#+BEGIN_QUERY
{:title "Property names"
:query [:find ?props
:where
[?p :block/name _]
[?p :block/properties ?props]
]
:result-transform (fn [props] (sort (map (fn [s] (subs (str s) 1)) (distinct (flatten (map (fn [i] (keys i)) props))))))
}
#+END_QUERY
- To also get properties from blocks, remove line
[?p :block/name _]
I fixed Logseq crashing and made something that possibly works?!
It may take a bit to execute as it gathers a large dataset and does the logic in the result-transform.
So not sure how useful it is going to be in the long run.
It also gives back any system properties.
#+BEGIN_QUERY
{:title [:b "Ongedefinieerde Properties"]
:query [:find ?name ?prop ; return both the pages and the properties
:keys page prop ; bind the pages and properties to a key for use in result-transform
:where
[?b :block/properties ?prop] ; get the properties of all blocks
(not [(empty? ?prop)])
[?p :block/original-name ?nm] ; get the original name of all pages
(not [?p :block/journal? true]) ; exclude journal pages, can remove if you need to check those as well
[(str ":" ?nm) ?name] ; change the page name to the convention of a property key
[_ :block/refs ?p] ; check that there are references to the page
]
:result-transform (fn [rows]
(def pagelist (set (for [r rows] (get r :page) ) ) ) ; make a set for all pagenames
(def keymap (distinct (flatten (map keys (for [r rows] (get r :prop) ) ) ) ) ) ; make a new map of only the unique property keys and flatten it so it is no longer a set in a set
(def proplist (for [r keymap] (str r) )) ; convert the values in the set to a string
(sort (remove pagelist proplist) ) ; subtract the pagelist from the proplist and sort the result
)
:view (fn [rows] (for [r rows] (str/replace (str r ". ") ":" "") ) ) ; add a . to the end of each item and remove the : from the name. This results in a single line as icon. file. etc.
}
#+END_QUERY
(PS. can change the view clause, this is taken from a query I use myself to check my properties against a list of property definitions that I made. Just tweaked it to use pages instead.)
3 Likes
Wow. Thank you again for the quick response. This is actually very close to what I’m looking for. Now, could you enlighten me on what I should do to match all pages that are not properties? Basically, I would like to exclude ?props from all page names. Thank you.
Thank you for the suggested solution. To be frank, I can follow everything until result-transform
and then I’m lost
But it is just me in this learning phase. result-transform
currently still seems like magic to me. Do you think what I’m trying to do might be too complicated at this moment? I would like to find all tags that are not used as page/block properties.
What follows is:
- a combination of the above suggestions
- The power of the community.
- closer to what you want
- Not sure anymore, as your later descriptions contradict the earlier ones.
- fast by avoiding needless combinations
- The used technique is worthy in itself.
#+BEGIN_QUERY
{:title [:b "Unreferenced property names"]
:query [:find ?name ?prop ; return both the pages and the properties
:keys page prop ; bind them to keys for use in result-transform
:where
(or-join [?name ?prop]
(and ; this group produces the ?prop
[(str "") ?name] ; empty string as switch for performance
[?b :block/properties ?prop] ; get all the properties
[?b :block/name _] ; limit to properties of pages, remove for including blocks
(not [(empty? ?prop)])
)
(and ; this group produces the ?name
[(set) ?prop] ; empty set as switch for performance
[?p :block/original-name ?name] ; get the original name of all pages
(not [?p :block/journal? true]) ; exclude journal pages, remove for including them
[_ :block/refs ?p] ; check that there are references to the page
)
)
]
:result-transform (fn [rows]
;; make a set for all pagenames
(def pagelist (set (for [r rows] (get r :page) ) ) )
;; make a new map of only the unique property keys as strings
(def proplist (map (fn [s] (subs (str s) 1) ) ; drop the colon
(distinct (flatten (map keys (for [r rows] (get r :prop) ) ) ) )
) )
;; subtract the pagelist from the proplist and sort the result
(sort (remove pagelist proplist) )
)
}
#+END_QUERY
2 Likes
Thank you and I apologize for the contradicting description. I thought it would be something as easy as a “not” clause but it seems like it takes more than that. I really appreciate your help.