src/hg/hgTracks/searchTracks.c 1.6

1.6 2010/05/30 01:38:17 larrym
include metadata in description search; make last metadata select variable (with a javascript callback)
Index: src/hg/hgTracks/searchTracks.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/hgTracks/searchTracks.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -b -B -U 4 -r1.5 -r1.6
--- src/hg/hgTracks/searchTracks.c	29 May 2010 20:58:15 -0000	1.5
+++ src/hg/hgTracks/searchTracks.c	30 May 2010 01:38:17 -0000	1.6
@@ -14,8 +14,10 @@
 #include "web.h"
 #include "jksql.h"
 #include "hdb.h"
 
+static char const rcsid[] = "$Id$";
+
 #define ANYLABEL "Any"
 
 static int gCmpGroup(const void *va, const void *vb)
 /* Compare groups based on label. */
@@ -47,35 +49,49 @@
     (sameString(op, "contains") && containsStringNoCase(track->shortLabel, str) != NULL) ||
     (sameString(op, "contains") && containsStringNoCase(track->longLabel, str) != NULL));
 }
 
-static boolean isDescriptionMatch(struct track *track, char *str)
+static boolean isDescriptionMatch(struct track *track, char *str, struct hash *trackMetadata)
 {
 // We parse str and look for every word ANYWHERE in track description (i.e. google style).
 // XXXX currently quite primitive; do stemming, strip html markup ??
 // Also, we should fold all metadata into the description of every track.
-char *html = track->tdb->html;
 boolean found = FALSE;
-if((!html || !strlen(html)) && track->tdb->parent)
+
+if(str && strlen(str))
+    {
+    char *html = track->tdb->html;
+    if(html == NULL || !strlen(html))
     {
     // XXXX is there a cleaner way to find parent?
-    html = track->tdb->parent->html;
+        struct trackDb *parent = track->tdb->parent;
+        while(parent != NULL && (parent->html == NULL || !strlen(parent->html)))
+            parent = parent->parent;
+        if(parent != NULL)
+            html = parent->html;
     }
-if(str && strlen(str))
+
+    if(html && strlen(html))
     {
     char *tmp = cloneString(str);
     char *val = nextWord(&tmp);
     while (val != NULL)
         {
         if(strstrNoCase(html, val) == NULL)
             {
+                struct hashEl *el;
             found = FALSE;
+                for(el = hashLookup(trackMetadata, track->track); el != NULL; el = hashLookupNext(el))
+                    if(sameWord((char *) el->val, val))
             break;
+                found = el != NULL;
             }
         else
-            {
             found = TRUE;
+            if(found)
             val = nextWord(&tmp);
+            else
+                break;
             }
         }
     }
 return found;
@@ -138,15 +154,36 @@
 sqlFreeResult(&sr);
 return retval;
 }
 
+static int metaDbVars(struct sqlConnection *conn, char *** metaValues)
+{
+// Search the assemblies metaDb table; If name == NULL, we search every metadata field.
+char query[256];
+struct sqlResult *sr = NULL;
+char **row = NULL;
+int i;
+struct slName *el, *varList = NULL;
+char **retval;
+
+safef(query, sizeof(query), "select distinct var from metaDb");
+sr = sqlGetResult(conn, query);
+while ((row = sqlNextRow(sr)) != NULL)
+    slNameAddHead(&varList, row[0]);
+sqlFreeResult(&sr);
+retval = needMem(sizeof(char *) * slCount(varList));
+slNameSort(&varList);
+for (el = varList, i = 0; el != NULL; el = el->next, i++)
+    retval[i] = el->name;
+*metaValues = retval;
+return i;
+}
+
 void doSearchTracks(struct group *groupList)
 {
 struct group *group;
 char *ops[] = {"is", "contains"};
 char *op_labels[] = {"is", "contains"};
-char *metaNames[] = {"antibody", "cell line"};
-char *metaValues[] = {"antibody", "cell"};
 char *groups[128];
 char *labels[128];
 int numGroups = 1;
 groups[0] = ANYLABEL;
@@ -154,13 +191,12 @@
 char *nameSearch = cartOptionalString(cart, "hgt.nameSearch");
 char *nameOp = cartOptionalString(cart, "hgt.nameOp");
 char *descSearch = cartOptionalString(cart, "hgt.descSearch");
 char *groupSearch = cartOptionalString(cart, "hgt.groupSearch");
-char *metaName = cartOptionalString(cart, "hgt.metaName");
+char *metaName = cartUsualString(cart, "hgt.metaName", "cell");
 char *metaOp = cartOptionalString(cart, "hgt.metaOp");
 char *metaSearch = cartOptionalString(cart, "hgt.metaSearch");
 char *antibodySearch = cartOptionalString(cart, "hgt.antibodySearch");
-char *cellSearch = cartOptionalString(cart, "hgt.cellSearch");
 char **terms;
 struct sqlConnection *conn = hAllocConn(database);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
 struct slRef *tracks = NULL;
@@ -180,15 +216,18 @@
             internalErr();
         }
     }
 
-cartWebStart(cart, database, "Track Search (prototype!)");
+// cartWebStart(cart, database, );
+webStartWrapperDetailedNoArgs(cart, database, "", "Track Search (prototype!)", FALSE, FALSE, FALSE, FALSE);
+
+hPrintf("<input type='hidden' name='db' value='%s'>\n", database);
 
 hPrintf("<form action='%s' name='SearchTracks' method='post'>\n\n", hgTracksName());
 hPrintf("<table>\n");
 
 hPrintf("<tr><td></td><td><b>Description:</b></td><td>contains</td>\n");
-hPrintf("<td><input type='text' name='hgt.descSearch' value='%s'></td></tr>\n", descSearch == NULL ? "" : descSearch);
+hPrintf("<td><input type='text' name='hgt.descSearch' value='%s' size='80'></td></tr>\n", descSearch == NULL ? "" : descSearch);
 
 hPrintf("<tr><td>and</td><td><b>Track Name:</b></td><td>\n");
 cgiMakeDropListFull("hgt.nameOp", op_labels, ops, ArraySize(ops), nameOp == NULL ? "contains" : nameOp, NULL);
 hPrintf("</td>\n<td><input type='text' name='hgt.nameSearch' value='%s'></td></tr>\n", nameSearch == NULL ? "" : nameSearch);
@@ -200,26 +239,24 @@
 
 if(metaDbExists)
     {
     int len;
+    char **metaValues = NULL;
+    int count = metaDbVars(conn, &metaValues);
+
     hPrintf("<tr><td>and</td>\n");
     hPrintf("<td><b>Antibody</b></td><td>is</td>\n<td>\n");
     len = getTermList(conn, &terms, "antibody");
     cgiMakeDropListFull("hgt.antibodySearch", terms, terms, len, antibodySearch, NULL);
     hPrintf("</td></tr>\n");
 
     hPrintf("<tr><td>and</td>\n");
-    hPrintf("<td><b>Cell Line</b></td><td>is</td>\n<td>\n");
-    len = getTermList(conn, &terms, "cell");
-    cgiMakeDropListFull("hgt.cellSearch", terms, terms, len, cellSearch, NULL);
-    hPrintf("</td></tr>\n");
-
-    hPrintf("<tr><td>and</td><td>\n");
-    cgiMakeDropListFull("hgt.metaName", metaNames, metaValues, ArraySize(metaNames), metaName, NULL);
     hPrintf("</td><td>\n");
-    cgiMakeDropListFull("hgt.metaOp", op_labels, ops, ArraySize(ops), metaOp == NULL ? "contains" : metaOp, NULL);
-    hPrintf("</td><td>\n");
-    hPrintf("<input type='text' name='hgt.metaSearch' value='%s'></td></tr>\n", metaSearch == NULL ? "" : metaSearch);
+    cgiMakeDropListClassWithStyleAndJavascript("hgt.metaName", metaValues, count, metaName, 
+                                               NULL, NULL, "onchange=metaPulldownChanged(this)");
+    hPrintf("</td><td>is</td>\n<td>\n");
+    len = getTermList(conn, &terms, metaName);
+    cgiMakeDropListFull("hgt.metaSearch", terms, terms, len, metaSearch, NULL);
     hPrintf("</td></tr>\n");
     }
 
 hPrintf("</table>\n");
@@ -227,36 +264,28 @@
 hPrintf("<input type='submit' name='%s' value='Search'>\n", searchTracks);
 hPrintf("<input type='submit' name='submit' value='Cancel'>\n");
 hPrintf("</form>\n");
 
+if(descSearch != NULL && !strlen(descSearch))
+    descSearch = NULL;
 if(groupSearch != NULL && sameString(groupSearch, ANYLABEL))
     groupSearch = NULL;
-if(metaSearch != NULL && !strlen(metaSearch))
+if(metaSearch != NULL && (!strlen(metaSearch) || sameString(metaSearch, ANYLABEL)))
     metaSearch = NULL;
 if(antibodySearch != NULL && sameString(antibodySearch, ANYLABEL))
     antibodySearch = NULL;
-if(cellSearch != NULL && sameString(cellSearch, ANYLABEL))
-    cellSearch = NULL;
-if((nameSearch != NULL && strlen(nameSearch)) || descSearch != NULL || groupSearch != NULL || metaSearch != NULL || antibodySearch != NULL || cellSearch != NULL)
+if((nameSearch != NULL && strlen(nameSearch)) || descSearch != NULL || groupSearch != NULL || metaSearch != NULL || antibodySearch != NULL)
     {
-    // First do the metaDb searches, which can be quickly done for all tracks with db queryies.
+    // First do the metaDb searches, which can be done quickly for all tracks with db queries.
     struct hash *matchingTracks = newHash(0);
+    struct hash *trackMetadata = newHash(0);
     struct slName *el, *metaTracks = NULL;
     boolean checkMeta = FALSE;
     if(antibodySearch != NULL)
         {
         metaTracks = metaDbSearch(conn, "antibody", antibodySearch, "is");
         checkMeta++;
         }
-    if(cellSearch != NULL)
-        {
-        struct slName *tmp = metaDbSearch(conn, "cell", cellSearch, "is");
-        if(metaTracks == NULL)
-            metaTracks = tmp;
-        else
-            metaTracks = slNameIntersection(metaTracks, tmp);
-        checkMeta++;
-        }
     if(metaSearch != NULL && strlen(metaSearch) && metaName != NULL && strlen(metaName))
         {
         struct slName *tmp = metaDbSearch(conn, metaName, metaSearch, metaOp);
         if(metaTracks == NULL)
@@ -266,8 +295,25 @@
         checkMeta++;
         }
     for (el = metaTracks; el != NULL; el = el->next)
         hashAddInt(matchingTracks, el->name, 1);
+
+    if(metaDbExists && !isEmpty(descSearch))
+        {
+        // Load all metadata words for each track to facilitate metadata search.
+        char query[256];
+        struct sqlResult *sr = NULL;
+        char **row;
+        safef(query, sizeof(query), "select obj, val from metaDb");
+        sr = sqlGetResult(conn, query);
+        while ((row = sqlNextRow(sr)) != NULL)
+            {
+            char *str = cloneString(row[1]);
+            hashAdd(trackMetadata, row[0], str);
+            }
+        sqlFreeResult(&sr);
+        }
+
     for (group = groupList; group != NULL; group = group->next)
         {
         if(groupSearch == NULL || !strcmp(group->name, groupSearch))
             {
@@ -277,9 +323,9 @@
                 for (tr = group->trackList; tr != NULL; tr = tr->next)
                     {
                     struct track *track = tr->track;
                     if((isEmpty(nameSearch) || isNameMatch(track, nameSearch, nameOp)) && 
-                       (isEmpty(descSearch) || isDescriptionMatch(track, descSearch)) &&
+                       (isEmpty(descSearch) || isDescriptionMatch(track, descSearch, trackMetadata)) &&
                        (!checkMeta || hashLookup(matchingTracks, track->track) != NULL))
                         {
                         tracksFound++;
                         refAdd(&tracks, track);
@@ -289,9 +335,9 @@
                         struct track *subTrack;
                         for (subTrack = track->subtracks; subTrack != NULL; subTrack = subTrack->next)
                             {
                             if((isEmpty(nameSearch) || isNameMatch(subTrack, nameSearch, nameOp)) &&
-                               (isEmpty(descSearch) || isDescriptionMatch(subTrack, descSearch)) &&
+                               (isEmpty(descSearch) || isDescriptionMatch(subTrack, descSearch, trackMetadata)) &&
                                (!checkMeta || hashLookup(matchingTracks, subTrack->track) != NULL))
                                 {
                                 // XXXX to parent hash. - use tdb->parent instead.
                                 hashAdd(parents, subTrack->track, track);