Query for attribute value then create aliases for the results as children

Following upon @mwra’s suggestion in today’s meetup, I believe I might have come up with a solution.

  1. A stamp that would take the value of the notes attribute and query for any one of the attribute’s values.
    • Something like $Text.icontainsAnyOf($DT3_Aliases), perhaps.
  2. Then it would add aliases of the results to the note as children.
    • In a way, this would be as if it were working as an agent. The difference is that this would be a one-time-only thing.

I am stuck in the second part. I can create an agent with the query $Text.icontainsAnyOf($DT3_Aliases("αἴσθησις")) and get results for one specific note. But that is still far from the imagined solution above.

Any help would be appreciated.

1 Like

If I’m not mistaken, what you want is all the notes that have the “αἴσθησις” in the $Text or $DT3_Aliases, correct?

In which case your agent should be:

$Text.icontainsAnyOf("αἴσθησις")|$DT3_Aliases.icontainsAnyOf("αἴσθησις");

Will give you all the notes that have “αἴσθησις” appear in the $Text or the $DT3_Aliase. The pipe “|” is your or designator.

Pretty cool! I’d have tried icontainsAnyOf before. Fun. :grinning:

1 Like

In the first part you talk about a stamp but then mention a problem with an agent. How does the agent come into this?

As I recall from the meet-up, you have successfully converted the comma+space delimited list from DEVONthink to a Tinderbox-style semicolon-delimited list. For example reference (but using English list items here!), you got a list from DEVONthink “ant, bee, cow” and stored it in a Tinderbox Set-type attribute $DT3_Alaises. We ‘fixed’ the list formatting with this code:

$DT3_Alaises = $DT3_Alaises.replace(", ",";");

That replaces every comma+space pair of characters with a single semicolon. This turns what a single Set value of all the terms, into a true Tinderbox list.

I also recall you saying you had used the list-format to-$text-and-Explode method to make a note per DEVONthink term in the list. Furthermore you want to connect (whether by a link or in metadata) each of these glossary terms with the note where it occurs. Is this correct? I ask only so we can start to correctly define the process so we can test the steps:

$Text.icontainsAnyOf($DT3_Aliases("αἴσθησις"))

In an agent query, the above will be true, and create an alias in the agent, if any one of the value(s) in the $DT3_Aliases of the note called ‘αἴσθησις’ matched any word in the $text of the note being processed by the agent.

So, if $DT3_Aliases is ‘ant;bee;cow’, $Text of “Oh to be a bee” would be a match (on the value bee) but a $Text of “The birds and bees” would not give a match (‘bees’ contains the substring ‘bee’ but is not an exact match for ‘bee’

A stamp that would take the value of the note’s attribute

Do you mean the $DT3_Aliases value(s) of the note being stamped? In which case you use

$Text.icontainsAnyOf($DT3_Aliases)

As we want the $DT3_Aliases of this note and not the note called ‘αἴσθησις’

and query for any one of the attribute’s values.

$Text.icontainsAnyOf($DT3_Aliases)

As explained above, this text will give a true test result if any whole word in $Text matches (here case-insensitively) any whole list value in $DT3_Aliases.

Then it would add aliases of the results to the note as children.

A stamp can’t do this, and furthermore the above .icontainsAnyOf() test simply tells you if one or more words have been matched; either the same $Text word several times, or different $Text words. The test doesn’t tell you which actual list values were matched. Thus this won’t tell you what aliases to make.

I think at this point, the sensible thing is to develop proof the process in a smaller test document. that way if you mess up, no great harm is done. Indeed if you do all you new tests on a copy of the text document you simply throw away bad tests and start over. I also noted (in the meet-up) that the DEVONthink derived list of terms (the comma separated list) has many items. For initial testing, 5 or 10 terms would suffice (rather than 50 or 100).

I supsect that what you really want to do is is add an alias to a glossary term note as a child of a research note for every glossary term in that note. So if a note’s $Text contains 3 discrete glossary terms, you would expect to end up with 3 aliases as children of the note? Is that correct?

Sorry for all the detail and questions, but I recall you say your thesis deadline is looming and we’d like to help you get this problem fixed. :slight_smile:

1 Like

Thank you very much, @mwra and @satikusala. I put together a file illustrating what I had in mind, perhaps this will help make things clearer. Then you can tell me if this is actually possible or not.

Example.tbx (251.8 KB)

The glossary has a few terms and each term has a number of aliases. Collected beneath each term, there are the parts of the text where any one of theses aliases occur.

Bernard, here you go: Bernardo_Example.tbx (254.5 KB)

1 Like

This is fun, see here: Example-4.tbx (301.7 KB)

while I write up what’s happening.…

1 Like

First, some tidying. We don’t want the full original text getting in the way, so either delete it or, as here, store it elsewhere (see ‘Source note’).

Next we make promote all the exploded notes one level and get rid of the ‘exploded notes’ container which we now don’t need.

Next, some prototypes:

  • pPGlossaryTerm. This is used for all glossary terms (so the ‘Glossary of terms’ container sets it via its $OnAdd for any future new terms. The prototype’s edict, disabled in the prototype itself (important!) run in the notes using the edit and stores the term’s $Name in the attribute in $MyGlossaryString (you can come up with a better name - it was really to indicate the purpose). The ‘why’ of this in a moment.
  • pSourceTerm. Again set (for any new items via the parent container’s $OnAdd). Again the prototype has its edict disabled. But notes using it will (via their aliases under glossary terms) populate $GlossaryTerms with all the glossary terms occurring in that text.

Next, the agent …

1 Like

The agent uses this query:

descendedFrom("Primary sources") & $Text.icontainsAnyOf($theAliases($MyGlossaryString(agent)))

I wrote the first part before I’d made a prototype for source notes. You could use $Prototype=="pSourceNote" as a an alternative first query term. More important is the second terms that tell the agent what glossary term’s ‘aliases’ to use.

Because the glossary term hold the name of that term in $MyGlossaryString, I can set that attribute in the agent’s Displayed Attributes and the attribute’s value box the pop-up list…

…shows all glossary terms. Pick one, here it is ‘color’, and that is what the agent uses. Thus the code $theAliases($MyGlossaryString(agent)) becomes the input string ‘color’ in this case, and the query finds source noted with that term’s ‘aliases’ in the text.

Now the fun part of making the aliases. For that agent action, I’ll put some line breaks in the code for clarity (the code works with/without the breaks):

if(inside($MyGlossaryString(agent))==false){
   $Container = "/Glossary of terms/"+$MyGlossaryString(agent);
};

For each note that matches the glossary term we ask, is this a child of that term. A note with an alias already under the ‘color’ term will be ‘inside’ both the “Primary sources” (the original note) and the “color” container (the aliases). So we have an alias already and we don’t want to add a new one each time the agent runs. BUT… if there is not yet an alias under the “color” container, the query will be false and we move the alias under the “color” note (the agent replaces the one in the agent but we’re not worried about that). Thus we ‘create’ (well, move) an alias under a term once and once only for each note.

Now, the alias is under ‘color’, the alias sees the $MyGlossaryString of its container and its edict adds that value to the aliases $GlossaryTerms. The original and alias share this attribute, so the original note now magically reports the glossary terms it matches! As $GlossaryTerms is a set, each relevant term name is only listed once.

Lastly, some reflections on the process…

1 Like

I’m not sure the ‘alias’ term within DEVONthink is pertinent here as we now have aliases meaning two different thing in your TBX. I’d have chosen something like $GlossaryTermSynonyms instead of theAliases as it makes the meaning clear (and adheres to the Tinderbox CamelCase styles of attribute naming.

You will notice I’ve used edicts here and not rules. You don’t want the code running all the time, especially as the amount of data in your document grows. We don’t run the edict in the prototype itself as we don’t want thinks like ‘pGlossaryTerm’ turning up as a glossary term value. Using prototype naming like I have also makes it clear if the term did slip in that it is an accident. Be aware you can run an individual notes (alias) edict from the action Inspector edict tab. Of, if you use File menu → Update Agents Now (yes, agents!) it also triggers all edicts to run once.

I’d keep the agent off when not using it; as the doc grows all the tasks get bigger so there’s no point running code when you don’t need to… If off and it has lots of child aliases, I’d consider deleting the latter (they’ll be remade if/when you turn the agent back on.

I’ve added in a couple of ‘test’ source notes simply so as to make it easier for me to test code. Nothing special about them otherwise.

One last point to consider. Do you need the source text as both $Name and $Text of a note? If you like it in the $Name, then I suggest modifying the hove to query for $Name rather and $Text and delete the note text. More unwanted clutter/data size gone from your document!

I think that’s it done (as is my Sunday!). Still an interesting challenge and hopefully it helps you meet your looking deadlines. Do ask if anything doesn’t make sense.

I’m pinging @eastgate here, in case I’m suggesting anything that will scale badly (i.e. not work so well as your volume of notes grows).


† Those are just guidelines. The reason I suggest following them is is help spot what is and is not an attribute name and where you have a typo as attribute names are case sensitive. As you need to press the shift key for a capital, a lack of one is likely a typo! These little things all help avoid code errors, but if you like all or part lowercase attribute names, carry on. This is a suggestion, not a rule!

1 Like

Done! sorry for any typos I’ve missed.

1 Like

Gentlemen, thank you very much!

@mwra, the file works like a charm. Precisely what I had in mind, but even better. I appreciate all the advice about how to keep things in order.

2 Likes

No problem. Hope it goes smoothly now, and do come back if stuck. :slight_smile:

1 Like

Enjoyed our Zoom conversation this morning. So much more than just Tinderbox…we also got to discuss metacognition philosophy, Artistotle, pandoc, BBEdit, command-line knowledge transformation, and more. Great way to start a Sunday.

1 Like