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("\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