Explanation of unexpected query



Okay, here is the scenario. I keep a list that I want to start sharing with a group once a week through email. I found that there were things that I didn’t want to share, so I just added a @private tag and a query that said something like,

* except @private///*

That works, but at one point I wanted to also exclude the parents of the items tagged with @private that have no other items. I figure that the first part was simple. I could just do something like,

* except @private/ancestor::*

But it doesn’t work. If I just try @private/ancestor::*, I get exactly what I wanted, but that is not the case when I add the * except. I am missing something?


By pure brute force, I figured that what I wanted was something like,

* except ( //@private/..* union @private///*)

Although /..* and /ancestor::* produce almost the same results, The former did exactly what I wanted. I still don’t understand why I have to add the union @private///* when my reasoning says that since I am excluding the parent, there is no need to exclude the particular child. Mmmmm…


Hi, I think maybe this is what you want:

except @private/ancestor::*///*

You need to add the ///* because otherwise you’ll just be excepting the ancestors … but since the children are still matched then the ancestors will be reinserted. Same reason that you needed to include ///* in your original except @private///*. Hope that helps, let me know if it still doesn’t make sense! :slight_smile:


In theory it makes sense now. It also kind of makes sense why it is done this way, and my example is a perfect case for this. I am including my explanation (even when I don’t seem to understand the thing in its totality). Here is my example.

	Person 1:
		This is a private message you will not want to share with the world @private
		Task set 1:
			- Task 1
			- Task 2
			- Task 3
			- Task 4 @private
	Person 2:
		This is a private message you will not want to share with the world @private
		Task set 2:
			- Task 5 @private
			- Task 6
	Person 3:
		This is a private message you will not want to share with the world @private
		Task set 3:
			- Task 7 @private
			Subtask set 1:
				- Task 8 @private
			Subtask set 2:
				- Task 9
			Subtask set 3: @private
				- Task 10

Now. I would like to share that with the people involved, but I only want them to get the info that is not private. The first thing I did was to do a query like,

* except @private///*

That works okay, but you will notice that it doesn’t work. Now, this is not a mistake or a bug. The problem is that the Subtask set 1 appears there because the project itself is not tag as private. Although this project is collapsed, the reality is that the private task is there, just not visible. My pet peeve was that I just wanted to copy and paste that into an email and I couldn’t do it as it is (Because it would send those particular tasks that fell into that unique case).

** UPDATE: To get this to work, you NEED to use the “Edit > Copy Displayed” option or you will still copy everything you don’t want to show!! I am still leaving this post here in case it is helpful to someone**

If you have been reading the thread and Jesse’s response, you will learn why it is necessary to include the extra slash ///*. Because even though a condition is met, when something it is still matched, some elements will be reinserted. This is not by mistake, but by design. Because I can then do something like this.

* except @private/..*

And get all the parents of the tasks, notes, projects, etc, that are tagged as private. This within itself will not work, because the parent would be re-inserted later because some ancestors are still matched. But if I mix it like this,

* except ( //@private/..* union @private///*)

It does some type of Voodoo magic. What ends up happening with this statement is the following.

It presents all the notes, tasks, projects, etc, and their children when they are NOT private; AND the amazing this is that it will also not show the parents if the only tasks inside are marked as private. Try it. If any of the children are not private, then the project shows. If all the children are private, then the parent doesn’t even appears. WIN!!!

In other words, using ..*, you can find projects that have no children. This could be very useful in a very many different ways. If you understand and can do a better job, please add a comment so that others may get it too. Hope that this helps somebody.


Now I am trying to understand what is the difference between @private/ancestor::* and @private/…* . You can see in my example bellow that if you try something like,

* except ( //@private/..* union @private///*)

It works, but the following doesn’t. Why?

* except ( //@private/ancestor::* union @private///*)


If you wanted to stay with that simple search then I think you could use Edit > Copy Displayed to copy out the public text that you want to share without getting any of the folded private text.


Hahaha. That would have saved me some headaches. At the same time, it led me to explore and understand the query a lot better :slight_smile:



Been scratching my head on this for the afternoon and think I’ve tracked it down due to a bug in TaskPaper. In the next release I think both will give the same results in your example outline.

To clear up a bit (I think maybe you already know this) ..* and ancestor::* are not the same. ..* is a shortcut for parent::*. Parent matches “parent” and “ancestor” matches all ancestors…

By that logic your first example query should always return the same (or more) results… when in fact it’s returning fewer results in your example. That’s due to a bug, which I think is fixed for next release now.