Hi there, is there a way to conduct a wildcard search, e.g. $Capabilities.comtains (*Password). I want to search for a specific word not a complete phrase.
Yes, both String.contains() and String.icontains() allow regular experssion (regex) search. However, if testing List or Set type data you can only match to whole list items - see more. So, weâll assume $Capabilities is a String-type and we want a case-insensitive match to the sub-string âpasswordâ.
$Capabilities.icontains("password")
Here regex are not needed as we have a literal string and .icontains()
deals with case. We donât need to worry about the other parts of the string. Only if you need to deal with placement of the term (e.g. at the end of the string, or in combo with other character, or if a back-reference is needed would you need a regex. This does the same as above but uses more CPU cycles to do so there is nogain:
$Capabilities.icontains(".*password.*")
The .*
regex just means âzero or more characters of any typeâ so would still match if the only characters in the target were âpasswordâ. In fact, I suspect when you use the first term that is how the app deals with it so my observation on CPU use may be moot! The main point re regex (contains, icontains, replace, etc.) is if you can use a more precise operatorânot the case hereâsuch as ==
or !=
then try to do so.
If youâve several real phrases to test, as in yuo âpassordâ above, in an agent you can set $MyString as a Key Attributes in the agent and add your search term there. Then use the query:
$Capabilities.icontains($MyString(agent))
The search term is not quoted as weâre telling Tinderbox to replace our literal or regex match string with the string stored in the $MyString belonging to the agent. Change the agentâs $MyString, you change the query. Neat!
Does that help?
This is perfect. You had me up to the $MyString part. I donât understand how to do that with an agent.
With an agent? The secret is use of a designator, this case âagentâ. Letâs unwrap the layers. If you use the query:
$Capabilities.icontains($MyString)
instead of checking $Capabilities in each note against a string like âpasswordâ, the query reads the value $MyString in each note it checks and uses that string value for the icontains()
test. Cool, except now if you want to check 1,000 notes, youâd have to set the first string test value in every note. Then set the next test string in al those notes etc. Way too much work. But if we use:
$Capabilities.icontains($MyString(agent))
for each note checked the value of $MyString in the agent is used as the test value. So, set it once in the agent and all notes get the same test: change the agentâs $MyString, you get a different test, etc.
How you set the agentâs $MyString value is up to you - there are so many ways: as a Key Attribute for the agent, via Get Info, QuickStamp, etc. Choose one you like.
It really is that simple.
Thanks. Finally getting it. One question. What if you wanted to search variations fo a test, e.g. âPasswordâ and/or âPosswardâ. Is there a way to do this with the above method, or do you need to create individual agents with the different test criteria youâre looking to test?
One approach is to query both possibilities
$Text.contains(âpasswordâ) | $Text.contains(âlösenordâ)
Another approach might be to devise a regular expression that will match your typos:
$Text.icontains(âp.ssw.*rdâ)
This will match âpasswordâ and âPossweerdâ but not âputridâ.
Ya, that makes sense. Iâll plan around with these ideas with the sting attribute idea Mark shared above. Thanks.
Hey there, sorry to ask, again, but Iâm unable to get the wildcard search to work.
If I have the following in $Citation â(Wunderman Thompson, 2019)â and all I remember is âWundermanâ what would be the wildcard query to find notes with this citation?
*
Iâve tried combinations of $Citation.icontains(âWun"); or $Citation.icontains(".**Wun.â); or $Citation.icontains(âWunâ);
Clearly, Iâm missing something VERY simple.
You didnât provide the context, but over here In a small test file an agent searching for
$Citation.icontains("Wun");
Successfully finds the relevant note.
Smart quotes can cause failure sometimes. Make sure you use straight quotes.
Iâm, I tried that. I tried that and it is just not working for me. $Citation is a string variable. When I under the above sadly nothing comes back.
Here is my agent:
Here are a list of notes that have the Citation,
Again, I want to pull these notes from an agent using the wildcard.
The example query in the image does not contain a wildcard, does it?
Anyway, I cannot reproduce â but we here do not have the file. Perhaps if you sent it to @eastgate for review?
I too canât replicate this based on the info supplied. IOW, the issue is likely an overlooked, undescribed, aspect of the file in question. Thus, it would be useful to see a test TBX file that reputably shows the error. The test case only needs 2 notes (a positive and negative match) and an agent) as it seems likely some unstated element is at play.
Taking another tack, you could try:
$Citation.beginsWith("(Wun");
N.B. this operator olny allows literal string matches, so no regex.
Got it, figured out the problem.
I had my $Citation attribute as a âsetâ not as a âstringâ. I created a test file as you suggested and it worked, which led me to figure out my error.
In my live file I changed $Citation from Set to String and it worked.
I would love to figure out how to search using partial words and wildcards on sets, however. For example, looking for âPrivâ to capture any time that has âPrivacyâ in the $Tags set.
Maybe on our next Saturday call we can go over wildcard searches, including using operators like
*, .*, etc.
Thanks.
Michael
.icontains
when used with lists or sets does not take regular expressions (i.e., âwildcardsâ). Just strings.
Set and list comparison requires literals, not regular expressions, to void confusing partial matches. If you want to use regular expressions with set and list comparison, coerce the set or list to a string:
Rule: MyString = $MyList
AgentQuery: $MyList.contains(âFrac.*sâ)
Shouldnât that query be:
$MyString.contains("Frac.*s")
Iâm not sure but, could this not be done on the fly without needing to pre-store the string-ified list? IOW:
$MyList.format(" ").contains("Frac.*s")
So if $MyList olds the values âFranticâ, âFractalâ and âFramesâ, the string tested by .contains()
becomes âFrantic;Fractal;Framesâ.
Indeed, using the test doc to check @satikusalaâs problem, if I make $Citation into a Set and use a query $Citation.format(â;â).contains(âWunâ), it works. Here is the test: Set-check.tbx (79.2 KB). The agent sets the tested noteâs $Text simply to prove the target string being tested by the chained .contains
is a string. You donât have to use a semi-colon concatenation (i.e. the default list item delimiter) but if choosing a not letter character donât choose one with a regex meaning.
Expectation management: cClearly, if one scales this to using the method on 000s of notes each with large numbers of list items and using a complex regex, performance may be less snappy. Iâd assume users working at this scale would expect such an effect, but I still think it warrants noting for those as yet only working in small docs where the issue is moot.
Waaay tooo much fun guys. Thanks. Iâd like to add one more learning to this; Mark showed me this a couple of weeks ago.
You can use a designator âagentâ so that the query will pull the search team from the $MyString. Works great. This is easier than messing with the query string. For those interested in playing with this, here is my test file WildcardSearch.tbx (133.4 KB).
Mark and Paul, I do have a question though.
Iâm trying to learn more about regular expression search so I went here: Appendix 2: Regular Expressions.
The Boost link referenced is broken: http://www.boost.org/libs/regex/doc/syntax_perl.html.
I can seem to find the correct replacement link. Can you point me in the right direction?
Also, stupid question but if I wanted to read up more on the basic Tinderbox code syntax? What language is it? JavaScript? Perl? Others?
Tinderbox code is sui generis, with a generous hat tip to C.
I donât recommend reading a dry manual on regular expressions. Rather, get an app such as Expressions or one of a number of sites you can find via a web search that lets you try out expressions with sample data. Best way to learn regular expressions is hands on. Youâre familiar with the drill.
Further to the last, as regex comes in many âflavoursâ of implementation, in tools like the above use documentation of the Perl-style choice. I donât believe there is a canonical reference as such. the basic regex forms seem common to most flavours. Itâs only at the more exotic level (that few use) where differences of implementation come into play. Ergo, most general regex references will get you started.
After some years of use, I will say that if you can bear to do so, you get much more out of regex by taking time to see what they do and not simple copy/paste sections of âmagicâ code. Regex are very precise, and in a manner often more granular in execution than our assumptions in writing them. Donât worry about the odd-looking syntax but just remember âgarbage in garbage outâ. If it doesnât work, esp in a spall localised test (I.e. no/less scope for edge cases) it likely means you havenât yet found the edge case.