[tw5] Filter tiddlers with exactly one tag.

I wish to filter tiddlers with a technical_todo tag into “tiddlers which I have already tagged with another tag”, and “tiddlers which have yet to be tagged with further tags”.

A filter like [tag[technical_todo]] will return all tiddlers with the tag technical_todo, regardless of whether these tiddlers have additional tags. I can always futher filter out tags using more steps with [!tag[foo]] (and this is of course useful too).

But for the sake of organizing “otherwise-untagged” tiddlers, is there a shorthand to only output those tiddlers with exactly the one tag I’m requesting? Perhaps by counting the number of tags each output tiddler from [tag[technical_todo]] can get me those with only a single technical_todo tag?

1 Like

G’day,

Give the attached tiddler a spin. (Download and then drag the file into TiddlyWiki.com.)

FilterExample.json (314 Bytes)

Even though the “tags” field contains a list of tiddler titles, it is also just a simple text value and you can use the fields:tags[...] filter to look for a specific literal value in the field, without treating it as a list.

<$list filter="[field:tags[technical_todo]]"><<currentTiddler>><br></$list>
will give the results you want, without a lot of messy “counting” and such.

enjoy,
-e

1 Like

That is short, sweet and nice. I do enjoy.

However, am I correct in saying that this only works if tags are one word or CamelCase words?

(I always use spaces in my multi-word tags because I find that easier to read. And they wind up double-bracketed by TiddlyWiki in the tags fields.)

<$list filter="[field:tags[technical_todo]]"><<currentTiddler>><br></$list>

I’d like to add: this works beautifully for my use case as described. I’d like to extend the use case however, and I’m not sure this filter works as-is.

I have 10 tiddlers tagged with both playground and rust ([tag[rust]tag[playground]] returns them all, but pretend that I could have tiddlers with more than just two tags). In advanced search, [field:tags[rust playground]] returns 3 matches, and [field:tags[playground rust]] returns 7 matches. Meaning that Tiddlywiki knows the order that I put tags in even though they are sorted when displayed in the browser. Is there a way to return “exactly these 2 or 3 etc” tags without having to try all permutations of tags?

Let’s assume you have FOUR tags: “rust”, “playground”, “two words”, and “test”

Try this filter:
[enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]]

Notes:

  • If a tag contains a space, it is stored with square brackets, e.g., [[two words]]
  • enlist{!!tags} gets the individual tags as separate items, and any square brackets are removed (but “two words” will still be ONE tag)
  • next, sort them alphabetically, in ascending order
  • then join the tags with a comma as a delimiter
  • and match with literal text containing commas

Let me know how it goes.

enjoy,
-e

I tried to experiment with [enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]] and uploaded the results. [enlist{!!tags}sort[]join[,]] returns the same thing as [enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]] from my testing, so I’m not exactly sure what the match is for.

I’m not sure how to combine it with another filter to actually return a list of “these tiddlers with exactly these 4 tags”. Is there a way to do “these tiddlers with exactly these 4 tags and nothing else” filter without using a variable (so I can put it into advanced search)?

Test Tiddler.json (375 Bytes)

The filter I provided assumes that it is contained within a tiddler that has tags. The purpose of the match[playground,rust,test,two words] filter syntax is to test the result of the filter to see if it has all the desired tags.

However… you can’t use $:/AdvancedSearch to return “a list of tiddlers with exactly these 4 tags”, because that needs two nested $list widgets, where the outer $list gets all the tiddler titles, and the inner $list checks each tiddler for the matching set of tags. Thus, to list all tiddlers that have the desired tags, you can put the following into a tiddler:

<$list filter="[all[]]">

<$list filter="[enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]]" variable="has_matching_tags">
<$link/><br>
</$list>
</$list>

Note the use of the variable in the inner $list. This preserves the value of “currentTiddler” that is assigned by the outer $list so that it can be output by the $link widget.

-e

2 Likes

[tag[playground]tag[rust]tag[test]tag[two words]]

The OP goal was to find tiddlers that have all the specified tags, but ONLY those tags and no others.
The problem is that the filter you suggest will also match tiddlers that have other tags in addition to the four tags specified.

-e

<$list filter="[all[]]">

<$list filter="[enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]]" variable="has_matching_tags">
<$link/><br>
</$list>
</$list>

So, this list does what I want and thus answers the question. However, I think I misunderstand something fundamental about lists and filters.

Filters (typically) take in a set of tiddlers and return a set of tiddlers as their output, per the documentation.

In the case of enlist{!!tags}sort[]join[,] by itself, the filter output will be “the current tiddler’s tags treated as tiddlers, regardless of whether they exist or not”. And running match[playground,rust,test,two words] as a subsequent step for a single tiddler will either return no match, or will return “playground,rust,test,two words” (the match docs say “potentially including duplicates”, but I couldn’t get that to trigger for e.g. {{{a [[b c]] [[b c]] +[match[b c]]}}}).

I just learned that list widgets can be nested; why does the above list return "the titles of the 10 tiddlers with the appropriate tags rather than printing the string “playground,rust,test,two words” 10 times, since the output of [enlist{!!tags}sort[]join[,]match[playground,rust,test,two words]] for a given tiddler will be either no match or “playground,rust,test,two words”?

Based on our discussion so far, I’ve created a tiddler for doing “exact tag matches”. Unfortunately, it seems to fall over tags with multiple words/spaces in it. How might I make this searching tiddler more robust in the presence of “tags with spaces”, since I also use spaces to delimit tags in the edit-text?

Exact Tag Search.json (535 Bytes)

Because the inner $list widget uses variable=“has_matching_tags”, the results of the filter does not change the value of currentTiddler as set by the outer $list widget and <$link/> outputs the titles of the matched tiddlers instead of the text “playground,rust,test,two words”

-e

The problem is the use of
<$set name="tagList" filter="[enlist{$:/temp/tagSearch}sort[]join[,]]">

Let’s say you entered “foo [[bar baz]]” (i.e., two tags, “foo” and “bar baz”).

Then, you might expect the results of the above $set widget to be “foo,bar baz”. However, because this string contains a space, the $set widget automatically encloses the output in double square brackets. Thus, the actual result is “[[foo,bar baz]]”. Then, when you use match<tagList> later on, it expects those square brackets to be part of the literal string match.

The solution is to use $vars with a “filtered transclusion” for the value, instead of $set, like this:
<$vars tagList={{{ [enlist{$:/temp/tagSearch}sort[]join[,]] }}}>
Then, tagList will be “foo,bar baz”, as you intended.

Alternatively, you can use $set, by adding “select=0” as an extra parameter, like this:
<$set name="tagList" filter="[enlist{$:/temp/tagSearch}sort[]join[,]]" select=0>
This tells the $set widget to only return the first list item (i.e., item #0), without adding any square brackets.

-e

I am satisfied with the <$vars tagList={{{ [enlist{$:/temp/tagSearch}sort[]join[,]] }}}> solution. I may have other questions to make my custom search tiddler look nicer, but that can be a separate thread if needed. Tyvm again for the help!