Script: Assign selected tag to matching records based on content and name

I almost never use tags, mainly because it’s generally tedious to assign them and especially as it’s distracting to check what tags already exist.

However @josieduffy had a great idea:

With a tag selected we could search all records whose content contains the tag.

Of course this might be no good idea if you’ve carefully maintained your tags as it blindly assigns the tag to everything that matches. However if you’ve also maintained your databases this carefully then it might be an interesting mix of tags and smart groups.

This script

  • takes one or more selected tags
  • asks in which group it should search
  • gets the tag’s aliases
  • assigns the selected tag to every matching record

If property useGroupSelector is set to false then the current database is used.

If property includeNameInSearch is set to false then only text is searched.

Tag aliases are always included.

Thanks @josieduffy!

-- Assign selected tag to matching records based on content and name

property useGroupSelector : true -- if false the current database is used
property includeNameInSearch : true -- if false only text is searched

tell application id "DNtp"
		set theTagRecords to selected records
		if theTagRecords = {} then error "Please select some tags."
		if useGroupSelector = true then
			set theSearchScope to display group selector "Search and assign Tags to records in:" for current database
			set theSearchScope to root of current database
		end if
		show progress indicator "Tagging... " steps (count theTagRecords) as string with cancel button
		repeat with thisRecord in theTagRecords
			if (location of thisRecord) starts with "/Tags/" then
				set thisRecord_Name to name of thisRecord
				step progress indicator thisRecord_Name
				set thisRecord_Aliases to aliases of thisRecord
				set theNames to {thisRecord_Name} & my tid(thisRecord_Aliases, {", ", "; ", ",", ";"})
				set theNames_readyForQuery to "\"" & my tid(theNames, "\" OR \"") & "\""
				if includeNameInSearch = true then
					set theQuery to "tags!=" & thisRecord_Name & " kind:any {any: name:" & theNames_readyForQuery & " text:" & theNames_readyForQuery & "}"
					set theQuery to "tags!=" & thisRecord_Name & " kind:any text:" & theNames_readyForQuery
				end if
				set theResults to search theQuery in theSearchScope
				repeat with thisResult in theResults
					set tags of thisResult to (tags of thisResult) & thisRecord_Name
				end repeat
			end if
		end repeat
		hide progress indicator
	on error error_message number error_number
		hide progress indicator
		if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
	end try
end tell

on tid(theInput, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	if class of theInput = text then
		set theOutput to text items of theInput
	else if class of theInput = list then
		set theOutput to theInput as text
	end if
	set AppleScript's text item delimiters to d
	return theOutput
end tid

The Assign Tags smart rule script uses all tags (and their aliases) and scans both the title & text by using…

extract keywords from theRecord with existing tags

Sure, the script only assigns selected tags, not all, giving the user the choice what to assign :slight_smile:

There’s also a derivated script Script: Create tag and assign it to matching records based on content and name

WOW this is absolutely amazing!!! thanks for this!!! it is going to change my life.

1 Like

question @pete31 - could you do this the other way? in other words, select a certain number of documents and automatically scan them for any tags in the db that may match?

Do you want to scan the text and directly assign them? Or do you want to only assign some of the matching tags, i.e. first select them from a list?

The latter would be no good idea as the dialog can’t be used properly with many tags. Directly assigning them is no problem.

sorry, i’m not sure i understand your question - my thought is, lets say a DB has 100 documents and 100 tags. I want to select 20 of those documents and have DT3 scan the text for anything matching those 100 tags. All the text, all the tags, only some of the documents. does that make sense?


I think there’s a built in Smart Rule script for this, named “Tags - Assign”, I think.

Please create a Smart Rule with a “Execute script” action, then choose “External script” and try if that’s doing what you want. You can use the Smart Rule with selected records by using the contextual menu.

„text“ should have been „tags“.

@pete31 is correct about the Tags - Assign smart rule script.

However, you don’t have to use a smart rule. You could also use Tools > Batch Process: Execute Script and use the same external script, Tags - Assign.

@josieduffy does the Smart Rule script work for you?

it does! thank you so much! one more question, how does punctuation work when it comes to matching text to tags? for example - if my tag is “barbour county, alabama” and the text of a document says “barbour county alabama” will that be a match? what about the opposite scenario - no punctuation in the tag but punctuation in the text?

I guess both won’t match.

thanks pete! is there a way to ignore punctuation when matching? if not no worries, i just thought i’d check. Thanks!

No. But adding an alias without (or with) the punctuation should work.

but if i add an alias with a comma, won’t it recognize that as two separate tags instead of one whole one?

Ahh, yes, of course.

Mixed things up with this thread Tags with comma.

yes, i really wish there was a workaround for this! would be great if quotation marks worked, or a semi-colon separated tags instead of a colon.

i don’t know who to tag but it seems like it would be right to either be able to use punctuation in aliases or have the tag matching function ignore punctuation?