Can someone explain query hack (question for the experts around!)

Hi everybody. I have a question about this particular query hack that I have been using for quite a while. This is something of an advance esoteric query.

This is the hack I use within my exceptions.

//@tag_name/ancestor::*

When I use it with the following,

(task//@tag_name/ancestor::* union @tag_name///*)

It removes the projects from the results that have projects that only have tasks with that particular tag. For example in the following example,

task except (task//@private/ancestor::* union @private///*) using the following file

First project:
	- One
	- Two
	- Three
Second project: @private
	- One
	- Two
	- Three
Third project:
	- One
	- Two @private
	- Three
Fourth project:
	- One @private
	- Two @private
	- Three @private

This is the result I get

First project:
	- One
	- Two
	- Three
Third project:
	- One
	- Three

Can someone explain why? I would like to know because I have a script that uses evaluateItemPath and that hack doesn’t work there.

For extra points, is there a query using evaluateItemPath that returns the same results?

I do notice that I get same results in your demo outline with this query:

task except @private///*

That reads as:

  1. Find all tasks in outline
  2. Remove all items tagged @private and the descendants of those items.
  3. This leaves you with a list of tasks (then TaskPaper inserts ancestors, the projects for display)

I’m not sure about inserting ancestors in this case as TaskPaper does for visible display. Seems like it should be possible, but it’s a logic test and at a point my brain fails those. Instead you can just do it manually. See this script, the buildMatchItemsList is performing the logic of inserting the ancestors.

function TaskPaperContextScript(editor, options) {

    let buildMatchItemsList = function(itemPathFilter, item) {
        let matchedList = [];
        let matchedSet = new Set();
        let outline = item.outline;
        
		let items = outline.evaluateItemPath(itemPathFilter, item, {
            root: item
        });
		
        for (let i = 0; i < items.length; i++) {
            let each = items[i];
            if (each !== item && item.contains(each)) {
                var ancestor = each.parent;
                var ancestorInsertIndex = matchedList.length;
                while (ancestor !== item) {
                    if (matchedSet.has(ancestor)) {
                        break;
                    } else {
                        matchedList.splice(ancestorInsertIndex, 0, ancestor);
                        matchedSet.add(ancestor);
                    }
                    ancestor = ancestor.parent;
                }
                matchedList.push(each);
                matchedSet.add(each);
            }
        }
        return matchedList;
    };

    return buildMatchItemsList("test", editor.outline.root).length
}

Application("TaskPaper").documents[0].evaluate({
    script: TaskPaperContextScript.toString()
})
2 Likes

Wow. Part of my problem is that I tend to learn by brute force and then a couple years later I forget what in the world I was doing and why I was doing it.

I had to do it that way, because in my own stupidity I had over complicated things. This is one of the queries that I run ALL the TIME!

(@type/..* union task///*) except (( //@private/..* union @private///*) union (//@unanswered/..* union @unanswered///*) union (archive///* union @done///*))

And since I used (@type/..* union task///*) I had to then add an exception ( //@private/..* union @private///*) later on. Hahaha. Talk about learning some lessons the hard way. I created the example above and tried to simplify things for this question and doing you realized that I had blown it. Someone should do a podcast about this. Hahaha.

Anyway. Thank you very much. I really appreciate it. Now I know that
(@type/..* union task///*) except (( //@private/..* union @private///*)

does exactly the same as
task except @private///*

Maybe there is a reason why I did it the complicated way to begin with, but now I don’t remember.