I use (nested) bullets to keep notes; a given top level item may have arbitrarily deeply nested subitems. I use a tag in the top level bullet to identify what that whole sublist hierarchy is about.
What I’d like is to search my notes for list items with tag X (in the top-level list item) and extract that list item and all its sublist items regardless of depth of nesting.
Things I have tried
The nearest I’ve gotten is a modified version from another post:
dataview
LIST item.children.text
FROM #reflection
SORT file.cday ASC
FLATTEN file.lists as item
WHERE contains(item.tags, "#reflection")
Two problems: (a) the top level list item isn’t selected, and (b) only 1 level of sublist, rather than all levels, is selected.
I have spent a couple of person-days now googling and reading but failing to understand how I can get the result I’d like.
I’ve found a potential way, but it is kind of a hack, so I’m not sure on how reliable it is, but the following code seems to do the trick. In theory you should be able to adjust the LIST query within either variant of the dataviewjs script and get all list items with children.
Full example of queries listing all sub-items of lists
Place the following in a file of its own, and use that as a starting point for your other variant.
---
Tags: f78291
---
questionUrl:: http://forum.obsidian.md/t//78291
- zero
- first
- one
- second
- two a
- [ ] two task
- third
- three one
- three one 1
- three one 2
- three two
- three two 1
- [ ] three task 2
- [x] three two 3
- three three
## Table version
```dataviewjs
const dvF = dv.evaluationContext.functions
// Create a new DQL function
dvF.listChildren = (ctx, children, offset = "" ) => {
let result = ""
for (const child of children) {
result += offset + "- " +
(child.task ? `[${ child.status ?? " "}] ` : "" ) +
child.text + "\n"
if (child.children.length)
result += dvF.listChildren(ctx, child.children, offset + " ")
}
return result
}
const result = await dv.query(`
TABLE item.text as Item, listChildren(item.children) as "Sub-items"
WHERE file = this.file
FLATTEN file.lists as item
WHERE !item.parent
`)
if ( !result.successful ) {
dv.paragraph("~~~~\n" + result.error + "\n~~~~")
return
}
dv.table(result.value.headers, result.value.values)
// Cleanup after our new function
delete dvF.listChildren
```
## List version
```dataviewjs
const dvF = dv.evaluationContext.functions
// Create a new DQL function
dvF.listChildren = (ctx, children, offset = "" ) => {
let result = ""
for (const child of children) {
result += offset + "- " +
(child.task ? `[${ child.status ?? " "}] ` : "" ) +
child.text + "\n"
if (child.children.length)
result += dvF.listChildren(ctx, child.children, offset + " ")
}
return result
}
const result = await dv.query(`
LIST WITHOUT ID item.text + "\n" + listChildren(item.children)
WHERE file = this.file
FLATTEN file.lists as item
WHERE !item.parent
`)
if ( !result.successful ) {
dv.paragraph("~~~~\n" + result.error + "\n~~~~")
return
}
dv.list(result.value.values)
// Cleanup after our new function
delete dvF.listChildren
```
The way the script above works is by temporarily adding a new function to the DQL queries, listChildren(). This is then used recursively to build up the corresponding markdown to list all children and sub-children.
Disclaimer 1: Tasks display as task, but they’re non-functioning, and don’t even think about asking me to get them to be working
Disclaimer 2: The adding of the function modifies parts of internal structures of Dataview, so I’m not sure if or what will happen in any future upgrade of Dataview. It might break, but it shouldn’t hurt any data, since we’re primarily listing stuff.
In the variant above, I delete the extra function at the end of the script, but just for the fun of it I tried not deleting it, and then I could do the following:
```dataviewjs
const dvF = dv.evaluationContext.functions
// Create a new DQL function
dvF.listChildren = (ctx, children, offset = "" ) => {
let result = ""
for (const child of children) {
result += offset + "- " +
(child.task ? `[${ child.status ?? " "}] ` : "" ) +
child.text + "\n"
if (child.children.length)
result += dvF.listChildren(ctx, child.children, offset + " ")
}
return result
}
```
```dataview
LIST WITHOUT ID item.text + "\n" + listChildren(item.children)
WHERE file.lists AND file.name != "ForumGold"
LIMIT 5
FLATTEN file.lists as item
WHERE !item.parent
```
The variant above would display all lists with sub-items from the five first files having any lists in your vault.
In other words, if you don’t delete the extra function it remains defined for all other queries, so we’ve effectively defined an extra DQL function “globally”. It’ll not last over an restart, then you need to run the dataviewjs script again, but it seems like it can be used in other notes too…
Disclaimer 4: You’re definitively using at your own risk, if you define and leave such an extra function alive in your vault! I find it extremely cool that we can do this, but I’m not sure if or what consequences this can have.
Dangerous idea: I wonder if a similar approach could be used to produce <img> element in tables based upon properties or fields…
Wow! That’s impressive.
And yeah, I agree that there’s several risks in defining a new global DQL function.
I appreciate the effort you’ve put into this, and I’ll certainly keep your code somewhere for future reference.
But for now, I think I’ll look for something a little less risky.