0fc118ce918050c1e46d4e94572923b2bc924a31
tdreszer
  Tue Apr 5 21:35:42 2011 -0700
Cleaned out some ifdefs and made track search name and description fields work like file search
diff --git src/hg/hgTracks/searchTracks.c src/hg/hgTracks/searchTracks.c
index 51bf26a..63268f6 100644
--- src/hg/hgTracks/searchTracks.c
+++ src/hg/hgTracks/searchTracks.c
@@ -82,30 +82,85 @@
 const struct track *a = ((struct track *) aa->val);
 const struct track *b = ((struct track *) bb->val);
 return strcasecmp(a->longLabel, b->longLabel);
 }
 
 static void findTracksSort(struct slRef **pTrack, enum sortBy sortBy)
 {
 if (sortBy == sbHierarchy)
     slSort(pTrack, gCmpTrackHierarchy);
 else if (sortBy == sbAbc)
     slSort(pTrack, gCmpTrack);
 else
     slReverse(pTrack);
 }
 
+#define SUPPORT_QUOTES_IN_NAME_SEARCH
+#ifdef SUPPORT_QUOTES_IN_NAME_SEARCH
+// TODO replace with tdb version moved from hgFileSearch to search.c lib code.
+static boolean matchToken(char *string, char *token)
+{
+// do this with regex ? Would require all sorts of careful parsing for ()., etc.
+if (string == NULL)
+    return (token == NULL);
+if (token == NULL)
+    return TRUE;
+
+if (!strchr(token,'*') && !strchr(token,'?'))
+    return (strcasestr(string,token) != NULL);
+
+char wordWild[1024];
+safef(wordWild,sizeof wordWild,"*%s*",token);
+return wildMatch(wordWild, string);
+}
+
+static boolean doesNameMatchTrack(struct track *track, struct slName *wordList)
+// We parse str and look for every word at the start of any word in track description (i.e. google style).
+{
+if (track->shortLabel == NULL || track->longLabel == NULL)
+    return (wordList != NULL);
+
+struct slName *word = wordList;
+for(; word != NULL; word = word->next)
+    {
+    if (!matchToken(track->shortLabel,word->name)
+    &&  !matchToken(track->longLabel, word->name))
+        return FALSE;
+    }
+return TRUE;
+}
+
+static boolean doesDescriptionMatchTrack(struct track *track, struct slName *wordList)
+// We parse str and look for every word at the start of any word in track description (i.e. google style).
+{
+if (track->tdb->html == NULL)
+    return (wordList != NULL);
+
+if (strchr(track->tdb->html,'\n'))
+    strSwapChar(track->tdb->html,'\n',' ');   // DANGER: don't own memory.  However, this track search function will use html for no other purpose
+
+struct slName *word = wordList;
+for(; word != NULL; word = word->next)
+    {
+    if (!matchToken(track->tdb->html,word->name))
+        return FALSE;
+    }
+
+return TRUE;
+}
+
+#else///ifndef SUPPORT_QUOTES_IN_NAME_SEARCH
 
 // XXXX make a matchString function to support "contains", "is" etc. and wildcards in contains
 
 //    ((sameString(op, "is") && !strcasecmp(track->shortLabel, str)) ||
 
 static boolean isNameMatch(struct track *track, char *str, char *op)
 {
 return str && strlen(str) &&
     ((sameString(op, "is") && !strcasecmp(track->shortLabel, str)) ||
     (sameString(op, "is") && !strcasecmp(track->longLabel, str)) ||
     (sameString(op, "contains") && containsStringNoCase(track->shortLabel, str) != NULL) ||
     (sameString(op, "contains") && containsStringNoCase(track->longLabel, str) != NULL));
 }
 
 static boolean isDescriptionMatch(struct track *track, char **words, int wordCount)
@@ -135,30 +190,31 @@
                     found = TRUE;
                     break;
                     }
                 }
             if(found)
                 numMatches++;
             else
                 break;
             }
         if(numMatches == wordCount)
             return TRUE;
         }
     }
 return FALSE;
 }
+#endif///ndef SUPPORT_QUOTES_IN_NAME_SEARCH
 
 static int getFormatTypes(char ***pLabels, char ***pTypes)
 {
 char *crudeTypes[] = {
     ANYLABEL,
     "bam",
     "psl",
     "chain",
     "netAlign",
     "maf",
     "bed",
     "bigBed",
     "ctgPos",
     "expRatio",
     "genePred",
@@ -233,84 +289,115 @@
     struct track *track = (struct track *) hashFindVal(trackHash, tsList->itemId);
     if (track != NULL)  // It is expected that this is NULL (e.g. when the trix references trackDb tracks which have no tables)
         {
         refAdd(&tracks, track);
         }
     }
 return tracks;
 }
 
 static struct slRef *advancedSearchForTracks(struct sqlConnection *conn,struct group *groupList, char **descWords,int descWordCount,
                                              char *nameSearch, char *typeSearch, char *descSearch, char *groupSearch, struct slPair *mdbPairs)
 // Performs the advanced search and returns the found tracks.
 {
 int tracksFound = 0;
 struct slRef *tracks = NULL;
-int numMetadataNonEmpty = slCount(mdbPairs);
+int numMetadataNonEmpty = 0;
+struct slPair *pair = mdbPairs;
+for (; pair!= NULL;pair=pair->next)
+    {
+    if (!isEmpty((char *)(pair->val)))
+        numMetadataNonEmpty++;
+    }
 
     if(!isEmpty(nameSearch) || typeSearch != NULL || descSearch != NULL || groupSearch != NULL || numMetadataNonEmpty)
         {
         // First do the metaDb searches, which can be done quickly for all tracks with db queries.
-        struct hash *matchingTracks = newHash(0);
+        struct hash *matchingTracks = NULL;
 
         if (numMetadataNonEmpty)
             {
-
             struct mdbObj *mdbObj, *mdbObjs = mdbObjRepeatedSearch(conn,mdbPairs,TRUE,FALSE);
             if (mdbObjs)
                 {
                 for (mdbObj = mdbObjs; mdbObj != NULL; mdbObj = mdbObj->next)
+                    {
+                    if (matchingTracks == NULL)
+                        matchingTracks = newHash(0);
                     hashAddInt(matchingTracks, mdbObj->obj, 1);
+                    }
                 mdbObjsFree(&mdbObjs);
                 }
+           if (matchingTracks == NULL)
+                return NULL;
             }
 
+    #ifdef SUPPORT_QUOTES_IN_NAME_SEARCH
+        // Set the word lists up once
+        struct slName *nameList = NULL;
+        if (nameSearch)
+            nameList = slNameListOfUniqueWords(cloneString(nameSearch),TRUE); // TRUE means respect quotes
+        struct slName *descList = NULL;
+        if (descSearch)
+            descList = slNameListOfUniqueWords(cloneString(descSearch),TRUE);
+    #endif///def SUPPORT_QUOTES_IN_NAME_SEARCH
+
         struct group *group;
         for (group = groupList; group != NULL; group = group->next)
             {
             if(groupSearch == NULL || sameString(group->name, groupSearch))
                 {
                 if (group->trackList != NULL)
                     {
                     struct trackRef *tr;
                     for (tr = group->trackList; tr != NULL; tr = tr->next)
                         {
                         struct track *track = tr->track;
                         char *trackType = cloneFirstWord(track->tdb->type); // will be spilled
-                        if((isEmpty(nameSearch) || isNameMatch(track, nameSearch, "contains")) &&
-                           (isEmpty(typeSearch) || (sameWord(typeSearch, trackType) && !tdbIsComposite(track->tdb))) &&
-                           (isEmpty(descSearch) || isDescriptionMatch(track, descWords, descWordCount)) &&
-                          (!numMetadataNonEmpty || hashLookup(matchingTracks, track->track) != NULL))
+                #ifdef SUPPORT_QUOTES_IN_NAME_SEARCH
+                        if((isEmpty(nameSearch) || doesNameMatchTrack(track, nameList))
+                        && (isEmpty(descSearch) || doesDescriptionMatchTrack(track, descList))
+                #else///ifndef SUPPORT_QUOTES_IN_NAME_SEARCH
+                        if((isEmpty(nameSearch) || isNameMatch(track, nameSearch, "contains"))
+                        && (isEmpty(descSearch) || isDescriptionMatch(track, descWords, descWordCount))
+                #endif///ndef SUPPORT_QUOTES_IN_NAME_SEARCH
+                        && (isEmpty(typeSearch) || (sameWord(typeSearch, trackType) && !tdbIsComposite(track->tdb)))
+                        && (matchingTracks == NULL || hashLookup(matchingTracks, track->track) != NULL))
                             {
                             if (track != NULL)
                                 {
                                 tracksFound++;
                                 refAdd(&tracks, track);
                                 }
                             else
                                 warn("found group track is NULL.");
                             }
                         if (track->subtracks != NULL)
                             {
                             struct track *subTrack;
                             for (subTrack = track->subtracks; subTrack != NULL; subTrack = subTrack->next)
                                 {
                                 trackType = cloneFirstWord(subTrack->tdb->type); // will be spilled
-                                if((isEmpty(nameSearch) || isNameMatch(subTrack, nameSearch, "contains")) &&
-                                   (isEmpty(typeSearch) || sameWord(typeSearch, trackType)) &&
-                                   (isEmpty(descSearch) || isDescriptionMatch(subTrack, descWords, descWordCount)) &&
-                                   (!numMetadataNonEmpty || hashLookup(matchingTracks, subTrack->track) != NULL))
+                        #ifdef SUPPORT_QUOTES_IN_NAME_SEARCH
+                                if((isEmpty(nameSearch) || doesNameMatchTrack(subTrack, nameList))
+                                && (isEmpty(descSearch) || doesDescriptionMatchTrack(subTrack, descList))
+                        #else///ifndef SUPPORT_QUOTES_IN_NAME_SEARCH
+                                if((isEmpty(nameSearch) || isNameMatch(subTrack, nameSearch, "contains"))
+                                && (isEmpty(descSearch) || isDescriptionMatch(subTrack, descWords, descWordCount))
+                        #endif///ndef SUPPORT_QUOTES_IN_NAME_SEARCH
+                                && (isEmpty(typeSearch) || sameWord(typeSearch, trackType))
+                                && (matchingTracks == NULL || hashLookup(matchingTracks, subTrack->track) != NULL))
                                     {
                                     if (track != NULL)
                                         {
                                         tracksFound++;
                                         refAdd(&tracks, subTrack);
                                         }
                                     else
                                         warn("found subtrack is NULL.");
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
@@ -559,31 +646,31 @@
 jsIncludeFile("ui.dropdownchecklist.js",NULL);
 // This line is needed to get the multi-selects initialized
 hPrintf("<script type='text/javascript'>$(document).ready(function() { $('.filterBy').each( function(i) { $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true });});});</script>\n");
 
 struct group *group;
 char *groups[128];
 char *labels[128];
 int numGroups = 1;
 groups[0] = ANYLABEL;
 labels[0] = ANYLABEL;
 char *nameSearch = cartOptionalString(cart, TRACK_SEARCH_ON_NAME);
 char *typeSearch = cartOptionalString(cart, TRACK_SEARCH_ON_TYPE);
 #ifdef FILES_SEARCH
 char *fileTypeSearch = cartOptionalString(cart, FILE_SEARCH_ON_FILETYPE);
 #endif///def FILES_SEARCH
-char *descSearch=FALSE;
+char *descSearch = NULL;
 char *groupSearch = cartOptionalString(cart, TRACK_SEARCH_ON_GROUP);
 boolean doSearch = sameString(cartOptionalString(cart, TRACK_SEARCH), "Search") || cartUsualInt(cart, TRACK_SEARCH_PAGER, -1) >= 0;
 struct sqlConnection *conn = hAllocConn(database);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
 int tracksFound = 0;
 struct trix *trix;
 char trixFile[HDB_MAX_PATH_STRING];
 char **descWords = NULL;
 int descWordCount = 0;
 boolean searchTermsExist = FALSE;
 int cols;
 char buf[512];
 
 enum searchTab selectedTab = simpleTab;
 char *currentTab = cartUsualString(cart, TRACK_SEARCH_CURRENT_TAB, "simpleTab");
@@ -594,32 +681,34 @@
     freez(&nameSearch);
     }
 else if(sameString(currentTab, "advancedTab"))
     {
     selectedTab = advancedTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
     }
 #ifdef FILES_SEARCH
 else if(sameString(currentTab, "filesTab"))
     {
     selectedTab = filesTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
     }
 #endif///def FILES_SEARCH
 
+#ifndef SUPPORT_QUOTES_IN_NAME_SEARCH
 if(descSearch)
     stripChar(descSearch, '"');
+#endif///ndef SUPPORT_QUOTES_IN_NAME_SEARCH
 trackList = getTrackList(&groupList, -2); // global
 makeGlobalTrackHash(trackList);
 
 // NOTE: This is necessary when container cfg by '*' results in vis changes
 // This will handle composite/view override when subtrack specific vis exists, AND superTrack reshaping.
 parentChildCartCleanup(trackList,cart,oldVars); // Subtrack settings must be removed when composite/view settings are updated
 
 getSearchTrixFile(database, trixFile, sizeof(trixFile));
 trix = trixOpen(trixFile);
 slSort(&groupList, gCmpGroup);
 for (group = groupList; group != NULL; group = group->next)
     {
     groupTrackListAddSuper(cart, group);
     if (group->trackList != NULL)
         {
@@ -831,30 +920,31 @@
     {
     char *tmp = cloneString(descSearch);
     char *val = nextWord(&tmp);
     struct slName *el, *descList = NULL;
     int i;
     while (val != NULL)
         {
         slNameAddTail(&descList, val);
         descWordCount++;
         val = nextWord(&tmp);
         }
     descWords = needMem(sizeof(char *) * descWordCount);
     for(i = 0, el = descList; el != NULL; i++, el = el->next)
         descWords[i] = strLower(el->name);
     }
+
 if (doSearch && selectedTab==simpleTab && descWordCount <= 0)
     doSearch = FALSE;
 
 if(doSearch)
     {
     // Now search
     struct slRef *tracks = NULL;
     if(selectedTab==simpleTab)
         tracks = simpleSearchForTracksstruct(trix,descWords,descWordCount);
     else if(selectedTab==advancedTab)
         tracks = advancedSearchForTracks(conn,groupList,descWords,descWordCount,nameSearch,typeSearch,descSearch,groupSearch,mdbSelects);
 #ifdef FILES_SEARCH
     else if(selectedTab==filesTab && mdbSelects != NULL)
         fileSearchResults(database, conn, mdbSelects, fileTypeSearch);
 #endif///def FILES_SEARCH