21e19f9c3bb9592c558c3984ed78cae6c13feb4f
tdreszer
  Wed Mar 23 15:13:44 2011 -0700
Fixed bug on name/description search and fleshed out wild card and quoted strings in description search
diff --git src/hg/hgFileSearch/hgFileSearch.c src/hg/hgFileSearch/hgFileSearch.c
index d44d3b8..3850764 100644
--- src/hg/hgFileSearch/hgFileSearch.c
+++ src/hg/hgFileSearch/hgFileSearch.c
@@ -29,101 +29,125 @@
 #define TRACK_SEARCH_ON_NAME     "tsName"
 #define TRACK_SEARCH_ON_GROUP    "tsGroup"
 #define TRACK_SEARCH_ON_DESCR    "tsDescr"
 #define TRACK_SEARCH_SORT        "tsSort"
 
 #define SUPPORT_COMPOSITE_SEARCH
 #ifdef SUPPORT_COMPOSITE_SEARCH
     //#define USE_TABS
 #endif///def SUPPORT_COMPOSITE_SEARCH
 
 #ifdef SUPPORT_COMPOSITE_SEARCH
 // make a matchString function to support "contains", "is" etc. and wildcards in contains
 
 //    ((sameString(op, "is") && !strcasecmp(track->shortLabel, str)) ||
 
+#define DESCRIPTION_MATCH_ON_EACH_WORD
+#ifdef DESCRIPTION_MATCH_ON_EACH_WORD
 static boolean isDescriptionMatch(struct trackDb *tdb, 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 (tdb->html == NULL)
     return (wordList != NULL);
 
 struct slName *word = wordList;
 for(; word != NULL; word = word->next)
     {
-    if (!wildMatch(word->name, tdb->html))
+    char wordWild[256];
+    safef(wordWild,sizeof wordWild,"*%s*",word->name);
+    if (!wildMatch(wordWild, tdb->html))
         return FALSE;
     }
 return TRUE;
 }
+#endif///def DESCRIPTION_MATCH_ON_EACH_WORD
 
-static struct trackDb *tdbFilterOn(struct trackDb **pTdbList, char *name, char *description, char *group)
-// returns tdbs that pach supplied criterion, leaving unmatched in list passed in
+static struct trackDb *tdbFilterBy(struct trackDb **pTdbList, char *name, char *description, char *group)
+// returns tdbs that match supplied criterion, leaving unmatched in list passed in
 {
+#ifdef DESCRIPTION_MATCH_ON_EACH_WORD
 // Set the word list up once
 struct slName *wordList = NULL;
 if (description)
-    wordList = slNameListOfUniqueWords(cloneString(description));
+    wordList = slNameListOfUniqueWords(cloneString(description),TRUE); // TRUE means respect quotes
+#endif///def DESCRIPTION_MATCH_ON_EACH_WORD
 
 struct trackDb *tdbList = *pTdbList;
 struct trackDb *tdbRejects = NULL;
 struct trackDb *tdbMatched = NULL;
+char nameWild[256];
+if (name)
+    safef(nameWild,sizeof nameWild,"*%s*",name);
+#ifndef DESCRIPTION_MATCH_ON_EACH_WORD
+char descWild[512];
+if (description)
+    safef(descWild,sizeof descWild,"*%s*",description);
+#endif///ndef DESCRIPTION_MATCH_ON_EACH_WORD
+
 while (tdbList != NULL)
     {
     struct trackDb *tdb = slPopHead(&tdbList);
 
-    if (name && (!wildMatch(name,tdb->shortLabel) && !wildMatch(name,tdb->longLabel)))
+    if (name && (!wildMatch(nameWild,tdb->shortLabel) && !wildMatch(nameWild,tdb->longLabel)))
         slAddHead(&tdbRejects,tdb);
     else if (group && differentString(tdb->grp,group))
         slAddHead(&tdbRejects,tdb);
+#ifdef DESCRIPTION_MATCH_ON_EACH_WORD
     else if (description && !isDescriptionMatch(tdb, wordList))
+#else///ifndef DESCRIPTION_MATCH_ON_EACH_WORD
+    else if (description && (tdb->html == NULL || !wildMatch(descWild,tdb->html)))
+#endif///ndef DESCRIPTION_MATCH_ON_EACH_WORD
         slAddHead(&tdbRejects,tdb);
     else
         slAddHead(&tdbMatched,tdb);
     }
 //slReverse(&tdbRejects); // Needed?
 //slReverse(&tdbMatched); // Needed?
 
 *pTdbList = tdbRejects;
 
+//warn("matched %d tracks",slCount(tdbMatched));
 return tdbMatched;
 }
 
 static boolean mdbSelectsAddFoundComposites(struct slPair **pMdbSelects,struct trackDb *tdbsFound)
 // Adds a composite mdbSelect (if found in tdbsFound) to the head of the pairs list.
+// If tdbsFound is NULL, then add dummy composite search criteria
 {
 // create comma separated list of composites
 struct dyString *dyComposites = dyStringNew(256);
 struct trackDb *tdb = tdbsFound;
 for(;tdb != NULL; tdb = tdb->next)
     {
     if (tdbIsComposite(tdb))
         dyStringPrintf(dyComposites,"%s,",tdb->track);
     else if (tdbIsCompositeChild(tdb))
         {
         struct trackDb *composite = tdbGetComposite(tdb);
         dyStringPrintf(dyComposites,"%s,",composite->track);
         }
     }
 if (dyStringLen(dyComposites) > 0)
     {
     char *composites = dyStringCannibalize(&dyComposites);
     composites[strlen(composites) - 1] = '\0';  // drop the last ','
-    slPairAdd(pMdbSelects,"composite",composites); // Composite should not already be in the list, because it is only indirectly sortable
+    //warn("Found composites: %s",composites);
+    slPairAdd(pMdbSelects,MDB_VAR_COMPOSITE,composites); // Composite should not already be in the list, because it is only indirectly sortable
     return TRUE;
     }
 
+//warn("No composites found");
 dyStringFree(&dyComposites);
 return FALSE;
 }
 #endif///def SUPPORT_COMPOSITE_SEARCH
 
 #ifdef USE_TABS
 static struct slRef *simpleSearchForTdbs(struct trix *trix,char **descWords,int descWordCount)
 // Performs the simple search and returns the found tracks.
 {
 struct slRef *foundTdbs = NULL;
 
 struct trixSearchResult *tsList;
 for(tsList = trixSearch(trix, descWordCount, descWords, TRUE); tsList != NULL; tsList = tsList->next)
     {
     struct trackDb *tdb = (struct track *) hashFindVal(trackHash, tsList->itemId);
@@ -158,31 +182,31 @@
 struct slName *tdbGroups = tdbListGetGroups(tdbList);
 if (tdbList == NULL)
     return *grps;
 
 while (grpList != NULL)
     {
     struct grp *grp = slPopHead(&grpList);
     if (slNameInList(tdbGroups,grp->name))
         slAddHead(grps,grp);
     }
 slNameFreeList(&tdbGroups);
 slReverse(grps);
 return *grps;
 }
 
-void doSearch(char *db,char *organism,struct cart *cart,struct trackDb *tdbList)
+static void doFileSearch(char *db,struct cart *cart,struct trackDb *tdbList)
 {
 if (!advancedJavascriptFeaturesEnabled(cart))
     {
     warn("Requires advanced javascript features.");
     return;
     }
 #ifdef SUPPORT_COMPOSITE_SEARCH
 char *nameSearch = cartOptionalString(cart, TRACK_SEARCH_ON_NAME);
 char *descSearch=NULL;
 #endif///def SUPPORT_COMPOSITE_SEARCH
 char *fileTypeSearch = cartOptionalString(cart, FILE_SEARCH_ON_FILETYPE);
 boolean doSearch = sameWord(cartUsualString(cart, FILE_SEARCH,"no"), "search");
 struct sqlConnection *conn = hAllocConn(db);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
 #ifdef ONE_FUNC
@@ -199,32 +223,34 @@
     {
     selectedTab = simpleTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_SIMPLE);
     freez(&nameSearch);
     }
 else if(sameString(currentTab, "filesTab"))
     {
     selectedTab = filesTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
     }
 #else///ifndef USE_TABS
 enum searchTab selectedTab = filesTab;
 descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
 #endif///ndef USE_TABS
 
+#ifndef DESCRIPTION_MATCH_ON_EACH_WORD
 if(descSearch)
     stripChar(descSearch, '"');
+#endif///ndef DESCRIPTION_MATCH_ON_EACH_WORD
 
 #ifdef USE_TABS
 struct trix *trix;
 char trixFile[HDB_MAX_PATH_STRING];
 getSearchTrixFile(db, trixFile, sizeof(trixFile));
 trix = trixOpen(trixFile);
 #endif///def USE_TABS
 #endif///def SUPPORT_COMPOSITE_SEARCH
 
 printf("<div style='max-width:1080px;'>");
 // FIXME: Do we need a form at all?
 //printf("<form action='%s' name='%s' id='%s' method='get'>\n\n", hgTracksName(),FILE_SEARCH_FORM,FILE_SEARCH_FORM);
 printf("<form action='../cgi-bin/hgFileSearch' name='%s' id='%s' method='get'>\n\n", FILE_SEARCH_FORM,FILE_SEARCH_FORM);
 cartSaveSession(cart);  // Creates hidden var of hgsid to avoid bad voodoo
 //safef(buf, sizeof(buf), "%lu", clock1());
@@ -404,39 +430,46 @@
                 findTracksSort(&tracks,sortBy);
 
             displayFoundTracks(cart,tracks,tracksFound,sortBy);
 
             if (measureTiming)
                 uglyTime("Displayed found files");
             }
         }
     else if(selectedTab==filesTab && mdbPairs != NULL)
 #endif///def USE_TABS
         {
         #ifdef SUPPORT_COMPOSITE_SEARCH
         if (nameSearch || descSearch || groupSearch)
             {  // Use nameSearch, descSearch and groupSearch to narrow down the list of composites.
 
+            if (isNotEmpty(nameSearch) || isNotEmpty(descSearch) || isNotEmpty(groupSearch))
+                {
             struct trackDb *tdbList = hTrackDb(db);
-            struct trackDb *tdbsMatch = tdbFilterOn(&tdbList, nameSearch, descSearch, groupSearch);
+                struct trackDb *tdbsMatch = tdbFilterBy(&tdbList, nameSearch, descSearch, groupSearch);
 
             // Now we have a list of tracks, so we need a unique list of composites to add to mdbSelects
-            mdbSelectsAddFoundComposites(&mdbSelects,tdbsMatch);
+                doSearch = mdbSelectsAddFoundComposites(&mdbSelects,tdbsMatch);
+                }
             }
         #endif///def SUPPORT_COMPOSITE_SEARCH
 
+        if (doSearch && mdbSelects != NULL && isNotEmpty(fileTypeSearch))
         fileSearchResults(db, conn, mdbSelects, fileTypeSearch);
+        else
+            printf("<DIV id='filesFound'><BR>No files found.<BR></DIV><BR>\n");
+
         if (measureTiming)
             uglyTime("Searched for files");
         }
 
     slPairFreeList(&mdbSelects);
     }
 hFreeConn(&conn);
 
 webNewSection("About Downloadable Files Search");
 if(metaDbExists)
     printf("<p>Search for terms in track names, descriptions, groups, and ENCODE "
             "metadata.  If multiple terms are entered, only tracks with all terms "
             "will be part of the results.");
 else
     printf("<p>Search for terms in track descriptions, groups, and names. "
@@ -462,31 +495,31 @@
 cartWebStart(cart, db, "Search for Downloadable Files in the %s %s Assembly", organism, hFreezeFromDb(db));
 
 webIncludeResourceFile("HGStyle.css");
 webIncludeResourceFile("jquery-ui.css");
 webIncludeResourceFile("ui.dropdownchecklist.css");
 jsIncludeFile("jquery.js", NULL);
 jsIncludeFile("jquery-ui.js", NULL);
 //jsIncludeFile("ui.core.js",NULL);   // NOTE: This appears to be not needed as long as jquery-ui.js comes before ui.dropdownchecklist.js
 jsIncludeFile("ui.dropdownchecklist.js",NULL);
 jsIncludeFile("utils.js",NULL);
 
 // This line is needed to get the multi-selects initialized
 //printf("<script type='text/javascript'>$(document).ready(function() { setTimeout('updateMetaDataHelpLinks(0);',50);  $('.filterBy').each( function(i) { $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true });});});</script>\n");
 printf("<script type='text/javascript'>$(document).ready(function() { updateMetaDataHelpLinks(0);  $('.filterBy').each( function(i) { $(this).dropdownchecklist({ firstItemChecksAll: true, noneIsAll: true });});});</script>\n");
 
-doSearch(db,organism,cart,tdbList);
+doFileSearch(db,cart,tdbList);
 
 
 printf("<BR>\n");
 webEnd();
 }
 
 char *excludeVars[] = { "submit", "Submit", "g", "ajax", FILE_SEARCH,TRACK_SEARCH_ADD_ROW,TRACK_SEARCH_DEL_ROW};  // HOW IS 'ajax" going to be supported?
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 cgiSpoof(&argc, argv);
 htmlSetBackground(hBackgroundImage());
 cartEmptyShell(doMiddle, hUserCookie(), excludeVars, NULL);
 return 0;