V1b4: Universal search popup for items in databases, database, current or preferred group, reading list and favorites, and smart groups

This script consolidates the function of the three below scripts into one universal popup search box for searching items in all/current DBs, specific group designated by the user, and any boolean cmd field (in my case, a “reading list” cmd field and a “favorites” cmd field):

e.g.1 search in current database, with predicates as “name: marquis”

e.g.2 search in a specific group denoted as “p”. “p” denotes a group “papers”, with predicates as “name: Thornton 2012”. Each preferred group-for-search is defined in the script, up to 21 groups that are frequently used for search can be defined.

e.g.3 show all items in “reading list” (not items in the DT’s reading list, but the items with a checked cmd field named “readinglist”).

e.g.4 show a filtered list of items in the “reading list” with predicates “name: thornton”

e.g.5 show a filtered list of items in “favorites” (not items in the DT’s favorites, but the items with a checked cmd field named “favorites”) with predicates as “name: I*” (all favorite items that contain word begins with I). If only “f” is entered, all items in the favorites will be shown.

V1b2+b3 addition:

V1b4 addtion:

1 Like

The script is experimental and is probably for the more experienced and adventurous users. There are options for customisation, such as defining some specific groups that a user needs to conduct a search regularly

In my case, I have three preferred groups that I need to search frequently. I use the letter “p”,“w”, and “e” as the letter of entry to specify the group to search.

I assign a short-cut to the script so that I can search for items anywhere in DT. I no longer need to go to a view window for ad hoc search.

Noted that: (1) the boolean cmd fields of “readinglist” and “favorites” have to be created by the user ( it means that I don’t use the DT’s reading list and favourites anymore), and (2) the user needs to specify the uuid of the custom preferred groups in the script. User can use any letter to denote the frequently used groups except for the five reserved letters “a”,“d”,“i”,“r”,“f”.

I will use the script regularly and may make changes in the future.
Hope the script may be useful, or as a kick-starter for further modification, for some DT users.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

-- By Ngan 2020.02.23
-- Universal search for items in database/databses, perferred group, reading list, and favorites 
--v1b2 2020.02.24
-- add "c" as scope:selectoon
--simplify the search in "f":favorites by 
--(1) use name:< if only one letter is entered
--(2) using name:~ as default
--v1b3 2020.02.25
--simplify  the search in CMD field query
--(1) if only one item is found, open it
--v1b4 2020.02.27
--add "s" for listing all smart groups in current database

-- for theCustomScopeUUID, do not use "a","d","c","s","i","r","f", these 5 letters are reserved for std search prefix
property theCustomScope : {"p", "w", "e"}
property theCustomScopeUUID : ¬
	{"82DB1186-ECFE-4D1A-9E1E-XXXXXXXXXXXX", ¬
		"1A58150E-7F9A-4443-B691-XXXXXXXXXXXX", ¬
		"C0EB6751-85BB-4611-87BE-XXXXXXXXXXXX"}
property cmdRead : "mdreadinglist"
property cmdFavorites : "mdfavorites"
property OpenExternallyForCMDSearch : {".doc", ".xls", ".cmap", ".scap"}

property openCMDDocInCurrentViewWin : false
property useFuzzyForCMDSearch : true
property showSmartGpLocation : true

property theInput : ""
global theDomain, thePredicates
global thePreferGroup


tell application id "DNtp"
	
	set theDesc to "| a | d | c | s | i | r | f |    for: " & return & "| DBs | DB | CurrentGp | SmartGroup | Inbox | Read | Favorites |"
	set theInput to text returned of (display dialog theDesc with title "Universal Search" default answer theInput)
	
	if theInput is "" then return
	set theDomain to text 1 of theInput
	if theDomain is not in (theCustomScope & {"a", "d", "c", "s", "i", "r", "f"}) then
		display alert "The 1st letter is not defined in the script" giving up after 1
		return
	end if
	-- if length of theInput > 1 then
	if length of my trimText(theInput, " ") > 1 then
		set thePredicates to texts 3 thru (length of theInput) of theInput
	else
		set thePredicates to ""
	end if
	if theDomain is in {"r", "f"} then
		my searchCMDList(theDomain, thePredicates)
		
	else if theDomain is "s" then
		my searchSmartGp()
		
	else
		my searchInDB(theDomain, thePredicates)
		
	end if
	
end tell


on searchInDB(theDomain, thePredicates)
	local theScope
	tell application id "DNtp"
		if theDomain is not in {"a", "d", "c", "i"} then
			set thePreferGroup to get record with uuid (item (my indexOfOneItem(theDomain, theCustomScope)) of theCustomScopeUUID)
			set root of viewer window 1 to thePreferGroup
			set theScope to " scope:selection "
			
		else if theDomain is "d" then
			set theScope to " scope:database "
			
		else if theDomain is "a" then
			set theScope to " scope:databases"
			
		else if theDomain is "c" then
			set theScope to " scope:selection"
			
		else if theDomain is "i" then
			set theScope to " scope:inbox"
		end if
		
		set thePredicates to thePredicates & theScope
		set search query of viewer window 1 to thePredicates
		-- set selection of viewer window 1 to {item 1 of search results of viewer window 1}
		set index of viewer window 1 to 1
		
	end tell
end searchInDB


on searchCMDList(theDomain, thePredicates)
	local theList, l, theName, theLink, thePath
	tell application id "DNtp"
		set theScope to " scope:all Databases "
		if theDomain is "r" then
			set thePredicates to thePredicates & " " & theScope & cmdRead & "==1"
		else if theDomain is "f" then
			if length of thePredicates > 2 then
				set thePredicates to "name:~" & thePredicates & " " & theScope & cmdFavorites & "==1"
			else
				set thePredicates to "name:<" & thePredicates & " " & theScope & cmdFavorites & "==1"
				
			end if
		end if
		if useFuzzyForCMDSearch then
			set theList to search thePredicates comparison (fuzzy)
		else
			set theList to search thePredicates
		end if
		
		if theList is {} then
			display alert "No Items is Found" giving up after 1
			return
		else if length of theList is 1 then
			set theLink to uuid of theList's item 1
			set theRecord to get record with uuid theLink
		else
			set l to {}
			repeat with each in theList
				set the end of l to name of each
			end repeat
			set item1 to l's item 1
			set l to my sortlist(l)
			set theName to (choose from list l with prompt {"Choose Document"} default items item1 with empty selection allowed) as string
			if theName is not "false" and theName is not "" then
				repeat with each in theList
					if theName = (each's name as string) then
						set theLink to each's uuid
					end if
				end repeat
				set theRecord to get record with uuid theLink
			else
				return
			end if
			
			
		end if
		------
		
		repeat with each in OpenExternallyForCMDSearch
			if filename of theRecord contains each then
				set thePath to (path of theRecord as string)
				do shell script "open " & quoted form of thePath
				return
			end if
		end repeat
		
		
		if class of theRecord is in {parent, smart group} then
			-- if not openCMDDocInCurrentViewWin then open window for record root of current database
			-- if class of think window 1 is not viewer window then open window for record root of current database
			set the root of viewer window 1 to theRecord
			set the index of viewer window 1 to 1
		else
			if openCMDDocInCurrentViewWin then
				set the root of think window 1 to (parent 1 of theRecord)
				set selection of think window 1 to {theRecord}
				set the index of viewer window 1 to 1
			else
				open tab for record theRecord
			end if
		end if
		
		
	end tell
end searchCMDList

on searchSmartGp()
	local ls, lsn, lsu, l, theGpNm, theGpUUID
	set {ls, lsn, lsu, l} to {{}, {}, {}, {}}
	tell application id "DNtp"
		set ls to every smart group of current database
		set ls to my sortObjlist(ls)
		
		if showSmartGpLocation then
			repeat with each in ls
				set end of lsn to {(name of each & "  | Where: " & location of each) & " |"}
				set end of lsu to {uuid of each}
			end repeat
		else
			repeat with each in ls
				set end of lsn to {name of each}
				set end of lsu to {uuid of each}
			end repeat
		end if
		
		set theGpNm to (choose from list lsn with prompt {"Choose Smart Group"} with empty selection allowed) as string
		
		set theIdx to my indexOfOneItem(theGpNm, lsn)
		set theGp to get record with uuid (item theIdx of lsu as string)
		set root of viewer window 1 to theGp
	end tell
end searchSmartGp


on indexOfOneItem(theItem, theList)
	-- credits Emmanuel Levy
	set {oTIDs, AppleScript's text item delimiters} to {AppleScript's text item delimiters, return}
	set theList to return & theList & return
	set AppleScript's text item delimiters to oTIDs
	try
		
		-1 + (count (paragraphs of (text 1 thru (offset of (return & theItem & return) in theList) of theList)))
	on error
		0
	end try
end indexOfOneItem

on sortlist(theList)
	set theIndexList to {}
	set theSortedList to {}
	repeat (length of theList) times
		set theLowItem to ""
		repeat with a from 1 to (length of theList)
			if a is not in theIndexList then
				set theCurrentItem to item a of theList as text
				if theLowItem is "" then
					set theLowItem to theCurrentItem
					set theLowItemIndex to a
				else if theCurrentItem comes before theLowItem then
					set theLowItem to theCurrentItem
					set theLowItemIndex to a
				end if
			end if
		end repeat
		set end of theSortedList to theLowItem
		set end of theIndexList to theLowItemIndex
	end repeat
	return theSortedList
end sortlist

on sortObjlist(theList)
	tell application id "DNtp"
		set theIndexList to {}
		set theSortedList to {}
		repeat (length of theList) times
			set theLowItem to ""
			repeat with a from 1 to (length of theList)
				if a is not in theIndexList then
					set theCurrentItem to item a of theList
					if theLowItem is "" then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					else if (name of theCurrentItem) comes before (name of theLowItem) then
						set theLowItem to theCurrentItem
						set theLowItemIndex to a
					end if
				end if
			end repeat
			set end of theSortedList to theLowItem
			set end of theIndexList to theLowItemIndex
		end repeat
		return theSortedList
	end tell
end sortObjlist

on trimText(theText, theTrim)
	if theText is "" then return ""
	set AppleScript's text item delimiters to the theTrim
	set the item_list to every text item of theText
	set AppleScript's text item delimiters to the ""
	set theText to the item_list as string
	set AppleScript's text item delimiters to ""
	return theText
end trimText

Three modifications are posted in the original script:

(1) Add “c” for search-in-current-selection. I.e. “scope: selection”

(2) Speed up the search in “f” (cmd field “favourites”) by
–(A) use “name:<” as default predicate for one-letter query (for “f” only).

E.g. All items in the “favorites” with a name beginning with the letter “p” will be listed

–(B) using “name:~” as the default predicate (for “f” only).

E.g. All items in the “favorites” with a name that contains “pap” will be listed

(3) Speed up the search for all CMD field queries. If there is only one item found in the query of “r” the reading list or “f” the favorites, the item will be opened directly.

1 Like

Thank you for posting this and several other scripts. I have been playing around with them and enjoying it.

You must know this, but perhaps it is worth mentioning that you can add where the tab should open.

This is what I use generally:

open tab for theRecord in think window 1

Since you use multiple screens, however, I guess new document windows are preferable.

Yeah, never like tabbed view in any app… spoiled by desktop since day 1.

Same thing here. Due to a health condition, however, I frequently have content myself with the (outrageous) 15" of real-state the Macbook provides me (in order to work laying down). I can’t wait for a future with a 27" Macbook option :laughing:

Thanks again for posting it. Keep them coming. All of them will be useful.

You could buy an iMac 27" and attach some handles :smiley:

I use one on my desk and love it :upside_down_face: I thought about attaching the handles to make it more versatile but found out that it would be somewhat troublesome since they no longer make the iMac compatible with the Vesa mount :confused:

By creating the two cmd fields for “favourites” and “reading list”, and followed by creating two global smart groups, I literally have moved the two categories of info into the lower half of the Navigate bar and can read those items in the view pane. It’s quite convenient (for me).

Screenshot 2020-02-26 at 13.28.23

Sidecar with iPad + this:

1 Like

v1b4 updated:
(1) Add one more “s” for list all smart groups in the current database. No predicate is accepted. Just type “s” and enter, all smart groups in the current database will be listed.

(2) I have smart groups with identical name, so I have added an option “property showSmartGpLocation : true” to show or not to show the location of the smart groups in the list.

thx @ngan for the script super useful!

best

Z