748b06ac95ff2a3957be3845bd3594984e3cc3cf chmalee Wed Aug 17 19:21:48 2022 -0700 Rename test cgi to official name. Always search everything, only show categories that have matches in the result list. Add /search endpoint to hubApi, add code to search help docs in hgSuggest but don't call it yet in autoComplete.js. Minor fixups so search result links work correctly. Fixing up old programs that call hgPositionsFind diff --git src/hg/lib/hgFind.c src/hg/lib/hgFind.c index f49fa44..cb57d62 100644 --- src/hg/lib/hgFind.c +++ src/hg/lib/hgFind.c @@ -38,30 +38,31 @@ #include "trix.h" #include "trackHub.h" #include "udc.h" #include "hubConnect.h" #include "bigBedFind.h" #include "genbank.h" #include "chromAlias.h" #include "cart.h" #include "cartTrackDb.h" #include "jsonParse.h" // Exhaustive searches can lead to timeouts on CGIs (#11626). // However, hgGetAnn requires exhaustive searches (#11665). #define NONEXHAUSTIVE_SEARCH_LIMIT 500 #define EXHAUSTIVE_SEARCH_REQUIRED -1 +#define SNIPPET_LIMIT 100 char *hgAppName = ""; /* alignment tables to check when looking for mrna alignments */ static char *estTables[] = { "intronEst", "all_est", "xenoEst", NULL }; static char *estLabels[] = { "Spliced ESTs", "ESTs", "Other ESTs", NULL }; static char *mrnaTables[] = { "all_mrna", "xenoMrna", NULL }; static char *mrnaLabels[] = { "mRNAs", "Other mRNAs", NULL }; static struct dyString *hgpMatchNames = NULL; void hgPosFree(struct hgPos **pEl) /* Free up hgPos. */ { struct hgPos *el; if ((el = *pEl) != NULL) @@ -410,30 +411,31 @@ if (verboseLevel() >= 3) { int count = slCount(idList); verbose(3, "*** Got %d results from %s\n\n", count, indexFile); } } freeMem(escapedKey); return idList; } static struct hgPosTable *addKnownGeneTable(char *db, struct hgPositions *hgp, char *name) /* Create new table for known genes matches, add it to hgp, and return it. */ { struct hgPosTable *table; AllocVar(table); +table->searchTime = -1; if (differentString(name, "knownGene")) { char *masterGeneTrack = hdbGetMasterGeneTrack(name); table->description = cloneString(masterGeneTrack); table->name = cloneString(masterGeneTrack); } else { if (hTableExists(db, "knownAttrs")) table->description = cloneString("Gencode Genes"); else if (hTableExists(db, "kgProtMap2")) table->description = cloneString("UCSC Genes"); else table->description = cloneString("Known Genes"); @@ -584,77 +586,76 @@ /* Hang all pos onto table. */ for (tp = tpList; tp != NULL; tp = tp->next) { struct hgPos *next; for (pos = tp->posList; pos != NULL; pos = next) { next = pos->next; slAddHead(&posList, pos); } } slSort(&posList, hgPosCmpCanonical); table->posList = posList; hashFree(&hash); -dyStringFree(&dy); } +dyStringFree(&dy); +} static boolean findKnownGeneFullText(char *db, char *term,struct hgPositions *hgp, char *name, char *path, struct hgFindSpec *hfs, boolean measureTiming) -/* Look for position in full text. TODO: Add snippet support*/ +/* Look for position in full text. */ { long startTime = clock1000(); boolean gotIt = FALSE; struct trix *trix; struct trixSearchResult *tsrList; char *lowered = cloneString(term); char *keyWords[HGFIND_MAX_KEYWORDS]; int keyCount; struct hgPosTable *table = NULL; trix = trixOpen(path); tolowers(lowered); keyCount = chopLine(lowered, keyWords); tsrList = trixSearch(trix, keyCount, keyWords, tsmExpand); if (tsrList != NULL) { table = addKnownGeneTable(db, hgp, name); struct sqlConnection *conn = hAllocConn(db); - struct sqlConnection *conn2 = hAllocConn(db); addKnownGeneItems(table, tsrList, conn, name, trix, hfs); hFreeConn(&conn); - hFreeConn(&conn2); gotIt = TRUE; } freez(&lowered); trixSearchResultFreeList(&tsrList); trixClose(&trix); // This is hacky but rely on knownGene table being at head of list // for timing. TODO: make this more robust if (measureTiming && table != NULL) table->searchTime = clock1000() - startTime; return gotIt; } static char *getUiUrl(struct cart *cart) /* Get rest of UI from browser. */ { static struct dyString *dy = NULL; static char *s = NULL; if (dy == NULL) { dy = dyStringNew(64); - if (cart != NULL) + if (cart != NULL && cart->sessionId != NULL) dyStringPrintf(dy, "%s=%s", cartSessionVarName(), cartSessionId(cart)); s = dy->string; } return s; } static void singlePos(struct hgPositions *hgp, char *tableDescription, char *posDescription, char *tableName, char *posName, char *browserName, char *chrom, int start, int end) /* Fill in pos for simple case single position. */ { struct hgPosTable *table; struct hgPos *pos; @@ -981,42 +982,44 @@ sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'", table, acc); struct sqlResult *sr = sqlGetResult(conn, query); char **row; while ((row = sqlNextRow(sr)) != NULL) { struct psl *psl = pslLoad(row+rowOffset); slAddHead(&pslList, psl); } slReverse(&pslList); sqlFreeResult(&sr); } return pslList; } static void addPslResultToHgp(struct cart *cart, struct hgPositions *hgp, char *db, char *tableName, - char *shortLabel, char *acc, struct psl *pslList) + char *shortLabel, char *acc, struct psl *pslList, boolean measureTiming) /* Create an hgPosTable for the given psl search results, and add it to hgp->tableList. */ { if (pslList == NULL) return; +long startTime = clock1000(); struct hgPosTable *table; struct dyString *dy = dyStringNew(1024); struct psl *psl; char hgAppCombiner = (strchr(hgAppName, '?')) ? '&' : '?'; char *ui = getUiUrl(cart); AllocVar(table); +table->searchTime = -1; table->htmlStart = mrnaHtmlStart; table->htmlEnd = mrnaHtmlEnd; table->htmlOnePos = mrnaHtmlOnePos; slAddHead(&hgp->tableList, table); dyStringPrintf(dy, "%s Alignments in %s", acc, shortLabel); table->description = cloneString(dy->string); table->name = cloneString(tableName); char *trackName = hGetTrackForTable(db, table->name); slSort(&pslList, pslCmpScore); for (psl = pslList; psl != NULL; psl = psl->next) { struct hgPos *pos; dyStringClear(dy); AllocVar(pos); pos->chrom = hgOfficialChromName(db, psl->tName); @@ -1028,34 +1031,36 @@ hgAppName, hgAppCombiner, hgPosBrowserRange(pos, NULL), trackName, hCarefulTrackOpenVisCart(cart, db, trackName)); if (ui != NULL) dyStringPrintf(dy, "&%s", ui); dyStringPrintf(dy, "%s\">", hgp->extraCgi); dyStringPrintf(dy, "%5d %5.1f%% %9s %s %9d %9d %8s %5d %5d %5d</A>", psl->match + psl->misMatch + psl->repMatch + psl->nCount, 100.0 - pslCalcMilliBad(psl, TRUE) * 0.1, skipChr(psl->tName), psl->strand, psl->tStart + 1, psl->tEnd, psl->qName, psl->qStart+1, psl->qEnd, psl->qSize); dyStringPrintf(dy, "\n"); pos->description = cloneString(dy->string); slAddHead(&table->posList, pos); } slReverse(&table->posList); +if (measureTiming) + table->searchTime = clock1000() - startTime; dyStringFree(&dy); } -static boolean findMrnaPos(struct cart *cart, char *db, char *acc, struct hgPositions *hgp) +static boolean findMrnaPos(struct cart *cart, char *db, char *acc, struct hgPositions *hgp, boolean measureTiming) /* Find MRNA or EST position(s) from accession number. * Look to see if it's an mRNA or EST. Fill in hgp and return * TRUE if it is, otherwise return FALSE. */ /* NOTE: this excludes RefSeq mrna's, as they are currently * handled in findRefGenes(), which is called later in the main function */ { struct sqlConnection *conn = hAllocConn(db); if (!sqlTableExists(conn, gbCdnaInfoTable)) { hFreeConn(&conn); return FALSE; } char *type = mrnaType(db, acc); if (isEmpty(type)) { @@ -1097,31 +1102,31 @@ for (c = chromList; c != NULL; c = c->next) { safef(splitTable, sizeof(splitTable), "%s_%s", c->name, tableName); struct psl *chrPslList = getPslFromTable(conn, db, splitTable, acc); if (pslList == NULL) pslList = chrPslList; else slCat(pslList, chrPslList); } } else pslList = getPslFromTable(conn, db, tableName, acc); if (pslList == NULL) continue; gotResults = TRUE; - addPslResultToHgp(cart, hgp, db, tableName, label, acc, pslList); + addPslResultToHgp(cart, hgp, db, tableName, label, acc, pslList, measureTiming); if (!sameString(tableName, "intronEst")) /* for speed -- found proper table, so don't need to look farther */ break; } hFreeConn(&conn); return gotResults; } static char *getGenbankGrepIndex(char *db, struct hgFindSpec *hfs, char *table, char *suffix) /* If hg.conf has a grepIndex.genbank setting, hfs has a (placeholder) * grepIndex setting, and we can access the index file for table, then * return the filename; else return NULL. */ /* Special case for genbank: Mark completely specifies the root in * hg.conf, so hfs's grepIndex setting value is ignored -- it is used @@ -1319,46 +1324,48 @@ static boolean mrnaAligns(struct sqlConnection *conn, char *table, char *acc) /* Return TRUE if accession is in the designated alignment table (for speed, * this assumes that we've already checked that the table exists) */ { char query[256]; sqlSafef(query, sizeof(query), "select count(*) from %s where qName = '%s'", table, acc); return (sqlQuickNum(conn, query) > 0); } static int addMrnaPositionTable(char *db, struct hgPositions *hgp, struct slName **pAccList, struct hash *accOrgHash, struct cart *cart, struct sqlConnection *conn, char *hgAppName, - boolean aligns, boolean isXeno) + boolean aligns, boolean isXeno, boolean measureTiming) /* Generate table of positions that match criteria. * Add to hgp if any found. Return number found */ { struct hgPosTable *table = NULL; struct slName *el = NULL; struct slName *elToFree = NULL; +long startTime = clock1000(); struct dyString *dy = dyStringNew(256); char *ui = getUiUrl(cart); int organismID = hOrganismID(hgp->database); /* id from mrna organism table */ int alignCount = 0; char hgAppCombiner = (strchr(hgAppName, '?')) ? '&' : '?'; char *mrnaTable = isXeno ? "xenoMrna" : "all_mrna"; boolean mrnaTableExists = hTableExists(hgp->database, mrnaTable); AllocVar(table); +table->searchTime = -1; /* Examine all accessions to see if they fit criteria for * this table. Add all matching to the position list, and * remove from the accession list */ for (el = *pAccList; el != NULL; el = el->next) { freez(&elToFree); char *acc = el->name; /* check if item matches xeno criterion */ int itemOrganismID = hashIntVal(accOrgHash, acc); if (isXeno == (itemOrganismID == organismID)) continue; /* check if item matches alignment criterion */ @@ -1439,56 +1446,58 @@ { char *organism = hOrganism(hgp->database); /* dbDb organism column */ if (alignCount == 1) { // So far we have not bothered to look up the coordinates because there are almost always // multiple matches among which the user will have to choose. However, it is possible // for there to be a unique match (hgwdev 19-02-15, hg38, "elmer" --> U01022). In that // case we should look up the coordinates so the user doesn't have to click through a page // with one match leading to another search. char shortLabel[256]; safef(shortLabel, sizeof shortLabel, "%s%s %sligned mRNAs", isXeno ? "Non-" : "", organism, aligns ? "A" : "Una"); char *acc = table->posList->name; struct psl *pslList = getPslFromTable(conn, hgp->database, mrnaTable, acc); - addPslResultToHgp(cart, hgp, hgp->database, mrnaTable, shortLabel, acc, pslList); + addPslResultToHgp(cart, hgp, hgp->database, mrnaTable, shortLabel, acc, pslList, measureTiming); if (hgp->tableList) alignCount = slCount(hgp->tableList->posList); else alignCount = 0; } else { char title[256]; slReverse(&table->posList); safef(title, sizeof(title), "%s%s %sligned mRNA Search Results", isXeno ? "Non-" : "", organism, aligns ? "A" : "Una"); table->description = cloneString(title); table->name = cloneString(mrnaTable); table->htmlOnePos = mrnaKeysHtmlOnePos; slAddHead(&hgp->tableList, table); } freeMem(organism); } +if (measureTiming) + table->searchTime = clock1000() - startTime; dyStringFree(&dy); return alignCount; } static boolean findMrnaKeys(struct cart *cart, char *db, struct hgFindSpec *hfs, - char *keys, int limitResults, struct hgPositions *hgp) + char *keys, int limitResults, struct hgPositions *hgp, boolean measureTiming) /* Find mRNA that has keyword in one of its fields. */ { int alignCount; char *tables[] = { productNameTable, geneNameTable, authorTable, tissueTable, cellTable, descriptionTable, developmentTable, }; struct hash *allKeysHash = NULL; struct slName *allKeysList = NULL; struct sqlConnection *conn = hAllocConn(db); boolean found = FALSE; /* If we can use grep to search all tables, then use piped grep to * implement implicit "AND" of multiple keys. */ if (gotAllGenbankGrepIndexFiles(db, hfs, tables, ArraySize(tables))) @@ -1531,42 +1540,42 @@ freeHash(&allKeysHash); slFreeList(&allKeysList); allKeysHash = andedHash; andedHash = NULL; allKeysList = andedList; andedList = NULL; } } } if (allKeysList == NULL) return FALSE; /* generate position lists and add to hgp */ /* organism aligning */ alignCount = addMrnaPositionTable(db, hgp, &allKeysList, allKeysHash, cart, conn, - hgAppName, TRUE, FALSE); + hgAppName, TRUE, FALSE, measureTiming); /* organism non-aligning */ addMrnaPositionTable(db, hgp, &allKeysList, allKeysHash, cart, conn, - hgAppName, FALSE, FALSE); + hgAppName, FALSE, FALSE, measureTiming); /* xeno aligning */ /* NOTE: to suppress display of xeno mrna's in non-model organisms * (RT 801 and 687), uncommented the following... * add to display list only if there is a scarcity of items * already listed as aligning (low number of own mRna's for this organism) */ if (alignCount < 20) addMrnaPositionTable(db, hgp, &allKeysList, allKeysHash, cart, conn, - hgAppName, TRUE, TRUE); + hgAppName, TRUE, TRUE, measureTiming); hashFree(&allKeysHash); hFreeConn(&conn); return(found); } static boolean isUnsignedInt(char *s) /* Return TRUE if s is in format to be an unsigned int. */ { int size=0; char c; while ((c = *s++) != 0) { if (++size > 10 || !isdigit(c)) return FALSE; } @@ -1684,30 +1693,31 @@ hashAdd(hash, rl->mrnaAcc, rl); continue; } hashAdd(hash, rl->mrnaAcc, rl); sqlSafef(where, sizeof where, "name = '%s'", rl->mrnaAcc); gpr = genePredReaderQuery(conn, hfs->searchTable, where); while ((gp = genePredReaderNext(gpr)) != NULL) { struct hgPos *pos = NULL; AllocVar(pos); if (table == NULL) { char desc[256]; AllocVar(table); + table->searchTime = -1; table->name = cloneString(hfs->searchTable); if (startsWith("xeno", hfs->searchTable)) safef(desc, sizeof(desc), "Non-%s RefSeq Genes", hOrganism(db)); else safef(desc, sizeof(desc), "RefSeq Genes"); table->description = cloneString(desc); slAddHead(&hgp->tableList, table); } slAddHead(&table->posList, pos); pos->name = cloneString(rl->name); pos->browserName = cloneString(rl->mrnaAcc); dyStringClear(ds); dyStringPrintf(ds, "(%s) %s", rl->mrnaAcc, rl->product); pos->description = cloneString(ds->string); pos->chrom = hgOfficialChromName(db, gp->chrom); @@ -2285,31 +2295,30 @@ return findBigBedPosInTdbList(cart, db, tdb, spec, hgp, hfs, measureTiming); } boolean searchSpecial(struct cart *cart, char *db, struct hgFindSpec *hfs, char *term, int limitResults, struct hgPositions *hgp, boolean relativeFlag, int relStart, int relEnd, boolean *retFound, boolean measureTiming) /* Handle searchTypes for which we have special code. Return true if * we have special code. Set retFind according to whether we find term. */ { boolean isSpecial = TRUE; boolean found = FALSE; char *upcTerm = cloneString(term); touppers(upcTerm); - if (startsWith("knownGene", hfs->searchType)) { char *knownDatabase = hdbDefaultKnownDb(db); char *name = (sameString(knownDatabase, db)) ? "knownGene" : knownDatabase; char *indexPath = hReplaceGbdb(hgFindSpecSetting(hfs, "searchTrix")); if (indexPath == NULL) indexPath = makeIndexPath(db, name); if (gotFullText(db, indexPath)) found = findKnownGeneFullText(db, term, hgp, name, indexPath, hfs, measureTiming); } else if (sameString(hfs->searchType, "refGene")) { found = findRefGenes(db, hfs, term, hgp, measureTiming); } else if (isBigFileFind(hfs)) @@ -2331,35 +2340,35 @@ int start, end; found = findChromContigPos(db, term, &chrom, &start, &end); if (found) { if (relativeFlag) { end = start + relEnd; start = start + relStart; } singlePos(hgp, hfs->searchDescription, NULL, hfs->searchTable, term, term, chrom, start, end); } } else if (sameString(hfs->searchType, "mrnaAcc")) { - found = findMrnaPos(cart, db, term, hgp); + found = findMrnaPos(cart, db, term, hgp, measureTiming); } else if (sameString(hfs->searchType, "mrnaKeyword")) { - found = findMrnaKeys(cart, db, hfs, upcTerm, limitResults, hgp); + found = findMrnaKeys(cart, db, hfs, upcTerm, limitResults, hgp, measureTiming); } else if (sameString(hfs->searchType, "sgdGene")) { found = findYeastGenes(db, term, hgp); } else { isSpecial = FALSE; } *retFound = found; freeMem(upcTerm); return(isSpecial); } @@ -2460,30 +2469,31 @@ table = hgp->tableList; for (tPtr = tableList; tPtr != NULL; tPtr = tPtr->next) { // we do not have control over the original sql since it comes from trackDb.ra or elsewhere? struct dyString *query = sqlDyStringCreate(hfs->query, tPtr->name, term); if (limitResults != EXHAUSTIVE_SEARCH_REQUIRED) sqlDyStringPrintf(query, " limit %d", limitResults); sr = sqlGetResult(conn, dyStringContents(query)); dyStringFree(&query); while ((row = sqlNextRow(sr)) != NULL) { if(table == NULL) { AllocVar(table); + table->searchTime = -1; table->description = description; table->name = cloneString(hfs->searchTable); slAddHead(&hgp->tableList, table); } found = TRUE; AllocVar(pos); pos->chrom = cloneString(row[0]); pos->chromStart = atoi(row[1]); pos->chromEnd = atoi(row[2]); if (isNotEmpty(xrefTerm)) truncatef(buf, sizeof(buf), xrefTerm); else safef(buf, sizeof(buf), "%s%s", termPrefix ? termPrefix : "", row[3]); pos->name = cloneString(buf); @@ -2692,60 +2702,69 @@ } static struct hgFindSpec *hfsFind(struct hgFindSpec *list, char *name) /* Return first element of list that matches name. */ { struct hgFindSpec *el; for (el = list; el != NULL; el = el->next) if (sameString(name, el->searchName)) return el; return NULL; } static void myLoadFindSpecs(char *db, struct searchCategory *categories, struct hgFindSpec **quickList, struct hgFindSpec **fullList) /* Get all find specs where the search table or search name is what we want */ { +struct hgFindSpec *shortList = NULL, *longList = NULL; struct dyString *clause = dyStringNew(0); struct searchCategory *categ; -sqlDyStringPrintf(clause, "select * from hgFindSpec_chmalee where searchName in ("); +struct sqlConnection *conn = hAllocConn(db); +struct sqlResult *sr = NULL; +struct slName *tbl, *tblList = hTrackDbList(); +for (tbl = tblList; tbl != NULL; tbl = tbl->next) + { + char *tblName = replaceChars(tbl->name, "trackDb", "hgFindSpec"); + sqlDyStringPrintf(clause, "select * from %s where searchName in (", tblName); for (categ = categories; categ != NULL; categ = categ->next) { sqlDyStringPrintf(clause, "'%s'", categ->id); if (categ->next) sqlDyStringPrintf(clause, ","); } sqlDyStringPrintf(clause, ") or searchTable in ("); for (categ = categories; categ != NULL; categ = categ->next) { + if (sameString(categ->id, "mrna")) + sqlDyStringPrintf(clause, "'all_mrna'"); + else sqlDyStringPrintf(clause, "'%s'", categ->id); if (categ->next) sqlDyStringPrintf(clause, ","); } sqlDyStringPrintf(clause, ")"); -struct hgFindSpec *shortList = NULL, *longList = NULL; -struct sqlConnection *conn = hAllocConn(db); -struct sqlResult *sr = sqlGetResult(conn, dyStringCannibalize(&clause)); + sr = sqlGetResult(conn, dyStringCannibalize(&clause)); char **row = NULL; while ((row = sqlNextRow(sr)) != NULL) { struct hgFindSpec *hfs = hgFindSpecLoad(row); if (hfs->shortCircuit) slAddHead(&shortList, hfs); else slAddHead(&longList, hfs); } sqlFreeResult(&sr); + } hFreeConn(&conn); if (quickList != NULL) { slSort(&shortList, hgFindSpecPriCmp); *quickList = shortList; } else hgFindSpecFreeList(&shortList); if (fullList != NULL) { slSort(&longList, hgFindSpecPriCmp); *fullList = longList; } else @@ -2809,158 +2828,187 @@ { boolean isVisible = FALSE; if (tdb->parent == NULL) { char *cartVis = cartOptionalString(cart, tdb->track); if (cartVis == NULL) isVisible = tdb->visibility != tvHide; else isVisible = differentString(cartVis, "hide"); } else if (isParentVisible(cart, tdb) && isSubtrackVisible(cart, tdb)) isVisible = TRUE; return isVisible; } -static struct searchableTrack *getSearchableTracks(struct cart *cart, char *database, struct hash *trackHash) +struct hash *hgFindTrackHash = NULL; +struct hash *hgFindGroupHash = NULL; + +int cmpCategories(const void *a, const void *b) +/* Compare two categories for uniquifying */ +{ +struct searchCategory *categA = *(struct searchCategory **)a; +struct searchCategory *categB = *(struct searchCategory **)b; +return strcmp(categA->id, categB->id); +} + +static struct searchableTrack *getSearchableTracks(struct cart *cart, char *database) /* Return the list of all tracks with an hgFindSpec available */ { if (trackHubDatabase(database)) return NULL; +struct searchableTrack *ret = NULL; struct sqlConnection *conn = hAllocConn(database); +struct slName *tbl, *tblList = hTrackDbList(); +for (tbl = tblList; tbl != NULL; tbl = tbl->next) + { + char *tdbName, *findSpecName; + tdbName = tbl->name; + findSpecName = replaceChars(tbl->name, "trackDb", "hgFindSpec"); char query[1024]; -sqlSafef(query, sizeof(query), "select distinct tableName,shortLabel,longLabel,searchDescription,priority " - "from hgFindSpec_chmalee join trackDb_chmalee on " - "hgFindSpec_chmalee.searchTable=trackDb_chmalee.tableName or " - "hgFindSpec_chmalee.searchName=trackDb_chmalee.tableName where searchTable !='knownGene' and searchName != 'knownGene'" - "order by priority,shortLabel"); + sqlSafef(query, sizeof(query), "select distinct " + "tableName,shortLabel,longLabel,searchDescription,priority " + "from %s join %s on " + "%s.searchTable=%s.tableName or " + "%s.searchName=%s.tableName or " + "%s.searchTable = concat('all_', %s.tableName) " + "where searchTable !='knownGene' and searchName != 'knownGene' " + "order by priority,shortLabel", + findSpecName, tdbName, findSpecName, tdbName, findSpecName, tdbName, findSpecName, tdbName); struct sqlResult *sr = sqlGetResult(conn, query); char **row = NULL; -struct searchableTrack *ret = NULL; struct trackDb *tdb = NULL; while ( (row = sqlNextRow(sr)) != NULL) { - if ( (tdb = hashFindVal(trackHash, row[0])) != NULL) + if ( (tdb = hashFindVal(hgFindTrackHash, row[0])) != NULL) { struct searchableTrack *track = NULL; AllocVar(track); track->track = cloneString(row[0]); track->shortLabel = cloneString(row[1]); track->longLabel = cloneString(row[2]); track->description = cloneString(row[3]); track->visibility = isTrackVisible(cart, tdb); track->priority = sqlDouble(row[4]); track->grp = tdb->grp; slAddHead(&ret, track); } } sqlFreeResult(&sr); + } hFreeConn(&conn); slReverse(&ret); return ret; } -//TODO: fix all these -#define hiveSearch "/hive/users/chmalee/search/manticore/" -#define publicHubsTrix "hubSearchTextRows" -#define helpDocsTrix "searchableDocs" - static struct trackDb *hubCategoriesToTdbList(struct searchCategory *categories) /* Make a list of trackDbs for the selected tracks */ { struct trackDb *ret = NULL; struct searchCategory *categ; for (categ = categories; categ != NULL; categ = categ->next) { if (startsWith("hub_", categ->id)) slAddHead(&ret, categ->tdb); } return ret; } static struct searchCategory *searchCategoryFromTdb(struct trackDb *tdb, struct searchableTrack *searchTrack, int visibility) /* Make a searchCategory from a leaf tdb, use searchCategory settings if possible, as they * have more accurate visibilities and labels */ { struct searchCategory *category = NULL; AllocVar(category); category->tdb = tdb; +if (sameString(tdb->track, "mrna") || sameString(tdb->track, "est")) + { + char tableName[10]; + safef(tableName, sizeof(tableName), "all_%s", tdb->track); + category->id = cloneString(tableName); + } +else category->id = tdb->track; category->name = searchTrack != NULL ? searchTrack->shortLabel : tdb->shortLabel; category->visibility = searchTrack != NULL ? searchTrack->visibility: tdb->visibility; if (visibility > 0) // for when tdb is from a hub track category->visibility = visibility; category->priority = searchTrack != NULL ? searchTrack->priority : tdb->priority; if (slCount(category->errors) == 0) { category->label = searchTrack != NULL ? searchTrack->shortLabel: tdb->shortLabel; category->description = searchTrack != NULL ? searchTrack->description: tdb->longLabel; category->groupName = searchTrack != NULL ? searchTrack->grp: tdb->grp; category->parents = NULL; while (tdb->parent) { slNameAddHead(&category->parents, tdb->parent->track); slNameAddHead(&category->parents, tdb->parent->shortLabel); tdb = tdb->parent; } if (category->parents) slReverse(&category->parents); } return category; } -struct searchCategory *makeTrixCategory(char *indexName, char *database) +struct trix *openStaticTrix(char *trixName) +/* Open up a trix file in hgFixed */ +{ +char trixPath[PATH_LEN]; +safef(trixPath, sizeof(trixPath), "%s%s.ix", hgFixedTrix, trixName); +struct trix *ret = trixOpen(trixPath); +return ret; +} + +static struct searchCategory *makeTrixCategory(char *indexName, char *database) /* Fill out the fields for a category filter for the UI. */ { struct searchCategory *category = NULL; AllocVar(category); struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { if (sameString(indexName, "publicHubs")) { category->id = "publicHubs"; category->name = "publicHubs"; category->label = "Public Hubs"; - category->description = "Search track names and track descriptions of public hubs"; - category->priority = 3.0; - char trixPath[PATH_LEN]; - safef(trixPath, sizeof(trixPath), "%s%s.ix", hiveSearch, publicHubsTrix); - category->trix = trixOpen(trixPath); + category->description = "Track names and descriptions of public hubs"; + category->priority = 4.0; + category->trix = openStaticTrix(publicHubsTrix); } else if (sameString(indexName, "helpDocs")) { category->id = "helpDocs"; category->name = "helpDocs"; category->label = "Help Pages"; - category->description = "Search for matches to help documentation"; + category->description = "Help documentation"; category->visibility = 1; - category->priority = 4.0; - char trixPath[PATH_LEN]; - safef(trixPath, sizeof(trixPath), "%s%s.ix", hiveSearch, helpDocsTrix); - category->trix = trixOpen(trixPath); + category->priority = 5.0; + category->trix = openStaticTrix(helpDocsTrix); } else if (startsWith("trackDb", indexName)) { category->id = "trackDb"; category->name = "trackDb"; category->visibility = 1; - category->priority = 2.0; + category->priority = 3.0; char trixPath[PATH_LEN]; safef(trixPath, sizeof(trixPath), "%s Track Labels/Descriptions", database); category->label = cloneString(trixPath); - category->description = "Search for matches to track names or track descriptions"; + category->description = "Track names or descriptions"; safef(trixPath, sizeof(trixPath), "/gbdb/%s/trackDb.ix", database); category->trix = trixOpen(trixPath); } } errCatchEnd(errCatch); if (errCatch->gotError) slAddHead(&category->errors, slNameNew(errCatch->message->string)); return category; } static struct searchCategory *makeCategoryForTrack(struct trackDb *tdb, struct searchableTrack *searchTrack) /* Make a searchCategory from a track. If the track is any type of container, * we will recurse down all the way to subtracks, as only leaf nodes have searchSpecs */ { struct trackDb *sub; @@ -2977,178 +3025,398 @@ } else { struct searchCategory *temp = searchCategoryFromTdb(sub, NULL, 0); if (temp) slAddHead(&ret, temp); } } } else ret = searchCategoryFromTdb(tdb, searchTrack, 0); return ret; } struct searchCategory *makeCategory(struct cart *cart, char *categName, struct searchableTrack *searchTrack, char *db, - struct hash *trackHash, struct hash *groupHash) + struct hash *groupHash) /* Make a single searchCategory, unless the requested categName is a container * track or track group (for example all phenotype tracks), in which case we make * categories for each subtrack */ { struct searchCategory *ret = NULL; if (sameString(categName, "helpDocs")) ret = makeTrixCategory("helpDocs", NULL); else if (sameString(categName, "publicHubs")) ret = makeTrixCategory("publicHubs", NULL); else if (startsWith("trackDb", categName)) ret = makeTrixCategory("trackDb", db); else if (hashLookup(groupHash, categName) != NULL) { // add all tracks for this track grouping - struct hashEl *hel, *helList = hashElListHash(trackHash); + struct hashEl *hel, *helList = hashElListHash(hgFindTrackHash); for (hel = helList; hel != NULL; hel = hel->next) { struct trackDb *tdb = hel->val; if (isTdbSearchable(tdb) && sameString(tdb->grp, categName)) { struct searchCategory *temp = makeCategoryForTrack(tdb, searchTrack); if (temp) slAddHead(&ret, temp); } } } else { // must be a track, ret will contain subtracks if necessary - struct trackDb *tdb = hashFindVal(trackHash, categName); + struct trackDb *tdb = hashFindVal(hgFindTrackHash, categName); if (tdb) ret = makeCategoryForTrack(tdb, searchTrack); } return ret; } -struct searchCategory *getCategsForNonDb(struct cart *cart, char *db, struct hash *trackHash, struct hash *groupHash) +struct searchCategory *getCategsForNonDb(struct cart *cart, char *db, struct hash *groupHash) /* Return the default categories for all databases */ { struct searchCategory *ret = NULL; -struct searchCategory *kgCategory = makeCategory(cart, "knownGene", NULL, db, trackHash, groupHash); +struct searchCategory *kgCategory = makeCategory(cart, "knownGene", NULL, db, groupHash); if (kgCategory) slAddHead(&ret, kgCategory); -struct searchCategory *helpDocCategory = makeCategory(cart, "helpDocs", NULL, db, trackHash, groupHash); +struct searchCategory *helpDocCategory = makeCategory(cart, "helpDocs", NULL, db, groupHash); if (helpDocCategory) slAddHead(&ret, helpDocCategory); -struct searchCategory *publicHubCategory = makeCategory(cart, "publicHubs", NULL, db, trackHash, groupHash); +struct searchCategory *publicHubCategory = makeCategory(cart, "publicHubs", NULL, db, groupHash); if (publicHubCategory) slAddHead(&ret, publicHubCategory); char trackDbIndexName[2048]; safef(trackDbIndexName, sizeof(trackDbIndexName), "trackDb%s", db); -struct searchCategory *tdbCategory = makeCategory(cart, trackDbIndexName, NULL, db, trackHash, groupHash); +struct searchCategory *tdbCategory = makeCategory(cart, trackDbIndexName, NULL, db, groupHash); if (tdbCategory) slAddHead(&ret, tdbCategory); return ret; } -struct searchCategory *getCategsForDatabase(struct cart *cart, char *db, struct hash *trackHash, struct hash *groupHash) -/* Get the default categories to search if user has not selected any before. - * By default we search for gene loci (knownGene), track names, and track items */ +static struct searchableTrack *makeGenbankSearchableTrack(struct trackDb *tdb, struct cart *cart) +{ +struct searchableTrack *track = NULL; +AllocVar(track); +track->track = cloneString(tdb->track); +track->shortLabel = cloneString(tdb->shortLabel); +track->longLabel = cloneString(tdb->longLabel); +track->description = cloneString(tdb->longLabel); +track->visibility = isTrackVisible(cart, tdb); +track->priority = tdb->priority; +track->grp = tdb->grp; +return track; +} + +struct searchCategory *getCategsForDatabase(struct cart *cart, char *db, struct hash *groupHash) +/* Get the track categories to search for a particular database */ { struct searchCategory *ret = NULL; +struct trackDb *tdb = NULL; -struct searchableTrack *track = NULL, *searchableTracks = getSearchableTracks(cart, db, trackHash); +struct searchableTrack *track = NULL, *searchableTracks = getSearchableTracks(cart, db); for (track = searchableTracks; track != NULL; track = track->next) { - struct searchCategory *trackCategory = makeCategory(cart, track->track, track, db, trackHash, groupHash); + struct searchCategory *trackCategory = makeCategory(cart, track->track, track, db, groupHash); if (trackCategory) { if (ret) slCat(&ret, trackCategory); else ret = trackCategory; } } + +// only the all_mrna table will have a valid struct searchableTrack added, we need +// to make some for the rest of the searchable genbank mrna/est tracks: +char *table = NULL; +char **tables = mrnaTables; +while ((table = *tables++) != NULL) + { + if (!sameString(table, "all_mrna") && (tdb = hashFindVal(hgFindTrackHash, table)) != NULL) + { + struct searchableTrack *tmp = makeGenbankSearchableTrack(tdb, cart); + struct searchCategory *category = makeCategory(cart, tmp->track, tmp, db, groupHash); + if (category) + slAddHead(&ret, category); + } + } +tables = estTables; +while ((table = *tables++) != NULL) + { + if ( (tdb = hashFindVal(hgFindTrackHash, table)) != NULL) + { + struct searchableTrack *tmp = makeGenbankSearchableTrack(tdb, cart); + struct searchCategory *category = makeCategory(cart, tmp->track, tmp, db, groupHash); + if (category) + slAddHead(&ret, category); + } + } + // add hub tracks to list -struct trackDb *tdb, *hubList = hubCollectTracks(db, NULL); +struct trackDb *hubList = hubCollectTracks(db, NULL); hubList = getSearchableBigBeds(hubList); for (tdb = hubList; tdb != NULL; tdb = tdb->next) { int visibility = isTrackVisible(cart, tdb); struct searchCategory *tmp = searchCategoryFromTdb(tdb, NULL, visibility); if (tmp) slAddHead(&ret, tmp); } return ret; } -struct searchCategory *getAllCategories(struct cart *cart, char *db, struct hash *trackHash, struct hash *groupHash) +struct searchCategory *getAllCategories(struct cart *cart, char *db, struct hash *groupHash) +/* Return all searchable stuff, both current db specific tracks, and things like hubs that searchable + * no matter the current database */ { struct searchCategory *ret = NULL; -struct searchCategory *tdbCategories = getCategsForDatabase(cart, db, trackHash, groupHash); +struct searchCategory *tdbCategories = getCategsForDatabase(cart, db, groupHash); if (tdbCategories) ret = tdbCategories; -struct searchCategory *staticCategs = getCategsForNonDb(cart, db, trackHash, groupHash); +struct searchCategory *staticCategs = getCategsForNonDb(cart, db, groupHash); if (staticCategs) { if (ret) slCat(&ret, staticCategs); else ret = staticCategs; } return ret; } +static struct hash *hubLabelHash = NULL; + +/* struct hubLabel: a helper struct for making links to hubs in the search result list */ +struct hubLabel + { + char *shortLabel; + char *longLabel; + char *hubId; + }; + + +static void getLabelsForHubs() +/* Hash up the shortLabels, longLabels and hub_id for a hubUrl */ +{ +if (hubLabelHash != NULL) + return; +hubLabelHash = hashNew(0); +struct sqlConnection *conn = hConnectCentral(); +char **row; +struct sqlResult *sr; +char query[2048]; +sqlSafef(query, sizeof(query), "select hp.hubUrl, hp.shortLabel, hp.longLabel, concat('hub_',id) from hubPublic hp join hubStatus hs on hp.hubUrl=hs.hubUrl"); +sr = sqlGetResult(conn, query); +while ( (row = sqlNextRow(sr)) != NULL) + { + struct hubLabel *label = NULL; + AllocVar(label); + label->shortLabel = cloneString(row[1]); + label->longLabel = cloneString(row[2]); + label->hubId = cloneString(row[3]); + char *hubUrl = cloneString(row[0]); + hashAdd(hubLabelHash, hubUrl, label); + } +hDisconnectCentral(&conn); +} + +static struct hubLabel *getLabelForHub(char *hubUrl) +/* Look up the shortLabel, longLabel, and hub_id for a hubUrl */ +{ +if (!hubLabelHash) + getLabelsForHubs(); +return (struct hubLabel *)hashFindVal(hubLabelHash, hubUrl); +} + +static boolean fillOutTrackDbHgPos(struct hgPos *this, struct trixSearchResult *tsr) +{ +boolean foundIt = FALSE; +struct trackDb *tdb = (struct trackDb *)hashFindVal(hgFindTrackHash, this->name); +if (tdb) + { + struct dyString *tdbLabels = dyStringNew(0); + dyStringPrintf(tdbLabels, "%s:%s:%s", tsr->itemId, tdb->shortLabel, tdb->longLabel); + this->name = dyStringCannibalize(&tdbLabels); + foundIt = TRUE; + } +return foundIt; +} + +static boolean fillOutPublicHubsHgPos(struct hgPos *this, struct trixSearchResult *tsr) +{ +boolean foundIt = FALSE; +char *itemId[5]; +int numItems = chopString(tsr->itemId, ":", itemId, ArraySize(itemId)); +struct dyString *hubLabel = dyStringNew(0); +char hubUrl[PATH_LEN]; +safef(hubUrl, sizeof(hubUrl), "%s:%s", itemId[0], itemId[1]); +struct hubLabel *label = getLabelForHub(hubUrl); +if (!label) + return foundIt; +else + foundIt = TRUE; +char *db = ""; +struct dyString *track = dyStringNew(0); +if (numItems > 2) + db = itemId[2] != NULL ? itemId[2] : ""; +if (numItems > 3) + dyStringPrintf(track, "%s_%s", label->hubId, itemId[3]); +dyStringPrintf(hubLabel, "%s:%s:%s:%s:%s", hubUrl, db, dyStringCannibalize(&track), label->shortLabel, label->longLabel); +this->name = dyStringCannibalize(&hubLabel); +return foundIt; +} + +static boolean doTrixQuery(struct searchCategory *category, char *searchTerm, struct hgPositions *hgp, char *database, boolean measureTiming) +/* Get a trix search result and potentially snippets for an hgFixed trix index. + * TODO: return an error message if there is a problem with the trix index or snippet index */ +{ +long startTime = clock1000(); +boolean ret = FALSE; +char *lowered = cloneString(searchTerm); +char *keyWords[16]; +int keyCount; +tolowers(lowered); +keyCount = chopLine(lowered, keyWords); +// TODO: let the user control this: +int maxReturn = SNIPPET_LIMIT; +struct trixSearchResult *tsrList = NULL; +if (category->trix) + { + tsrList = trixSearch(category->trix, keyCount, keyWords, tsmExpand); + struct errCatch *errCatch = errCatchNew(); + if (errCatchStart(errCatch)) + initSnippetIndex(category->trix); + errCatchEnd(errCatch); + // silently return if there was a problem opening the snippet index + if (errCatch->gotError || errCatch->gotWarning) + return FALSE; + errCatchFree(&errCatch); + } +struct trixSearchResult *tsr = NULL; +int len = 0; +struct hgPosTable *table = NULL; +AllocVar(table); +table->searchTime = -1; +table->name = category->name; +table->description = category->description; +for (tsr = tsrList; tsr != NULL; tsr = tsr->next) + { + if (startsWith(category->name,"publicHubs")) + { + // Check that this public hubs result is for our current database + char *itemId[5]; + int numItems = chopString(cloneString(tsr->itemId), ":", itemId, ArraySize(itemId)); + if (numItems <= 2 || isEmpty(itemId[2]) || !sameString(itemId[2], database)) + continue; + } + struct errCatch *errCatch = errCatchNew(); + if (errCatchStart(errCatch)) + { + addSnippetForResult(tsr, category->trix); + } + errCatchEnd(errCatch); + // silently return if there was a problem getting a single snippet, there is + // probably a data error with the rest of the index if so + if (errCatch->gotError || errCatch->gotWarning) + return FALSE; + errCatchFree(&errCatch); + struct hgPos *this = NULL; + AllocVar(this); + this->name = tsr->itemId; + this->description = tsr->snippet; + if (startsWith(category->name, "trackDb")) + { + boolean addedTdbFields = fillOutTrackDbHgPos(this, tsr); + if (!addedTdbFields) + continue; + } + if (sameString(category->name, "publicHubs")) + { + boolean addedHubFields = fillOutPublicHubsHgPos(this, tsr); + if (!addedHubFields) + continue; + } + slAddHead(&table->posList, this); + + len++; + if (len > maxReturn) + break; + } + +if (table->posList != NULL) + { + slReverse(&table->posList); + if (measureTiming) + table->searchTime = clock1000() - startTime; + slAddHead(&hgp->tableList, table); + ret = TRUE; + } +return ret; +} + static boolean userDefinedSearch(char *db, char *term, int limitResults, struct cart *cart, struct hgPositions *hgp, struct searchCategory *categories, boolean measureTiming) /* If a search type(s) is specified in the cart, perform that search. * If the search is successful, fill in hgp and return TRUE. */ { boolean foundIt = FALSE; -struct hgFindSpec *shortList = NULL, *longList = NULL; struct hash *foundSpecHash = hashNew(0); -struct hgFindSpec *hfs; +struct hgFindSpec *shortList = NULL, *longList = NULL; +struct trackDb *hubCategoryList = NULL; +// get all the lists of what to query: if (!trackHubDatabase(db)) { if (categories) myLoadFindSpecs(db, categories, &shortList, &longList); else hgFindSpecGetAllSpecs(db, &shortList, &longList); } +// lastly search any included track hubs, or in the case of an assembly hub, any of the tracks +hubCategoryList = hubCategoriesToTdbList(categories); +struct hgFindSpec *hfs; for (hfs = shortList; hfs != NULL; hfs = hfs->next) { boolean foundSpec = hgFindUsingSpec(cart, db, hfs, term, limitResults, hgp, FALSE, 0, 0, FALSE, measureTiming); if (foundSpec) hashAdd(foundSpecHash, hfs->searchTable, hfs->searchTable); foundIt |= foundSpec; } for (hfs = longList; hfs != NULL; hfs = hfs->next) { if (hashFindVal(foundSpecHash, hfs->searchTable) != NULL) continue; foundIt |= hgFindUsingSpec(cart, db, hfs, term, limitResults, hgp, FALSE, 0, 0, FALSE, measureTiming); } - // lastly search any included track hubs, or in the case of an assembly hub, any of the tracks -struct trackDb *hubCategoryList = hubCategoriesToTdbList(categories); if (hubCategoryList) foundIt |= findBigBedPosInTdbList(cart, db, hubCategoryList, term, hgp, NULL, measureTiming); -if (foundIt) +getLabelsForHubs(); +struct searchCategory *category; +for (category = categories; category != NULL; category = category->next) { - fixSinglePos(hgp); - if (cart && hgp->singlePos && isNotEmpty(hgp->singlePos->highlight)) - cartSetString(cart, "addHighlight", hgp->singlePos->highlight); - slReverse(&hgp->tableList); + if (startsWith("trackDb", category->id) + || sameString(category->id, "helpDocs") + || sameString(category->id, "publicHubs")) + { + foundIt |= doTrixQuery(category, term, hgp, db, measureTiming); } + } + return foundIt; } static boolean singleSearch(char *db, char *term, int limitResults, struct cart *cart, struct hgPositions *hgp, boolean measureTiming) /* If a search type is specified in the CGI line (not cart), perform that search. * If the search is successful, fill in hgp as a single-pos result and return TRUE. */ { char *search = cgiOptionalString("singleSearch"); if (search == NULL) return FALSE; cartRemove(cart, "singleSearch"); boolean foundIt = FALSE; if (sameString(search, "knownCanonical")) @@ -3176,48 +3444,51 @@ // a little data structure for combining multiple transcripts that resolve // to the same hgvs change. This struct can be used to fill out a struct hgPos struct hgvsHelper { struct hgvsHelper *next; char *chrom; // chromosome name of position int chromStart; // start of position int chromEnd; // end of position struct slName *validTranscripts; // valid transcripts/protein accessions for this position char *label; // corresponding hgvs term char *table; // type of match, LRG, NCBI, etc boolean mapError; // does this hgvs mapping result in a map error? }; -static boolean matchesHgvs(struct cart *cart, char *db, char *term, struct hgPositions *hgp) +static boolean matchesHgvs(struct cart *cart, char *db, char *term, struct hgPositions *hgp, + boolean measureTiming) /* Return TRUE if the search term looks like a variant encoded using the HGVS nomenclature * See http://varnomen.hgvs.org/ * If search term is a pseudo hgvs term like GeneName AminoAcidPosition (RUNX2 Arg155) and * matches more than one transcript, fill out the hgp with the potential matches so the user * can choose where to go, otherwise return a singlePos */ { boolean foundIt = FALSE; +long startTime = clock1000(); struct hgvsVariant *hgvsList = hgvsParseTerm(term); if (hgvsList == NULL) hgvsList = hgvsParsePseudoHgvs(db, term); if (hgvsList) { struct hgvsVariant *hgvs = NULL; int hgvsListLen = slCount(hgvsList); struct hgPosTable *table; AllocVar(table); table->description = "HGVS"; + table->searchTime = -1; int padding = 5; int mapErrCnt = 0; struct dyString *dyWarn = dyStringNew(0); struct hgvsHelper *helper = NULL; struct hash *uniqHgvsPos = hashNew(0); struct dyString *chromPosIndex = dyStringNew(0); struct dyString *allWarnings = dyStringNew(0); for (hgvs = hgvsList; hgvs != NULL; hgvs = hgvs->next) { dyStringClear(dyWarn); dyStringClear(chromPosIndex); char *pslTable = NULL; struct bed *mapping = hgvsValidateAndMap(hgvs, db, term, dyWarn, &pslTable); if (dyStringLen(dyWarn) > 0) mapErrCnt++; @@ -3328,30 +3599,32 @@ pos->chromEnd = spanEnd + padding; pos->name = "Approximate area"; pos->description = term; pos->browserName = term; slAddHead(&table->posList, pos); // highlight the 'mapped' bases to distinguish from padding hgp->tableList->posList->highlight = addHighlight(db, helper->chrom, spanStart, spanEnd); warn("%s", dyStringContents(allWarnings)); warn("Sorry, couldn't locate %s, moving to general location", term); } else warn("%s", dyStringContents(dyWarn)); } dyStringFree(&dyWarn); dyStringFree(&allWarnings); + if (measureTiming && hgp && hgp->tableList) + table->searchTime = clock1000() - startTime; } return foundIt; } struct hgPositions *hgPositionsFind(char *db, char *term, char *extraCgi, char *hgAppNameIn, struct cart *cart, boolean multiTerm, boolean measureTiming, struct searchCategory *categories) /* Return container of tracks and positions (if any) that match term. */ { struct hgPositions *hgp = NULL, *hgpItem = NULL; regmatch_t substrs[4]; boolean canonicalSpec = FALSE; boolean gbrowserSpec = FALSE; boolean lengthSpec = FALSE; boolean singleBaseSpec = FALSE; boolean relativeFlag = FALSE; @@ -3372,31 +3645,38 @@ term = trimSpaces(term); if(isEmpty(term)) return hgp; hgp->query = cloneString(term); hgp->database = db; if (extraCgi == NULL) extraCgi = ""; hgp->extraCgi = cloneString(extraCgi); if (singleSearch(db, term, limitResults, cart, hgp, measureTiming)) return hgp; if (categories != NULL) { + if (!matchesHgvs(cart, db, term, hgp, measureTiming)) userDefinedSearch(db, term, limitResults, cart, hgp, categories, measureTiming); + slReverse(&hgp->tableList); + if (multiTerm) + collapseSamePos(hgp); + fixSinglePos(hgp); + if (cart && hgp->singlePos && isNotEmpty(hgp->singlePos->highlight)) + cartSetString(cart, "addHighlight", hgp->singlePos->highlight); if (hgp->posCount > 0) return hgp; } /* Allow any search term to end with a :Start-End range -- also support stuff * pasted in from BED (chrom start end) or SQL query (chrom | start | end). * If found, strip it off and remember the start and end. */ char *originalTerm = term; if ((canonicalSpec = regexMatchSubstrNoCase(term, canonicalRangeExp, substrs, ArraySize(substrs))) || (gbrowserSpec = regexMatchSubstrNoCase(term, gbrowserRangeExp, substrs, ArraySize(substrs))) || (lengthSpec = regexMatchSubstrNoCase(term, lengthRangeExp, substrs, ArraySize(substrs))) || regexMatchSubstrNoCase(term, bedRangeExp, substrs, ArraySize(substrs)) || @@ -3440,31 +3720,31 @@ hgParseChromRange(db, term, &chrom, &start, &end); if (relativeFlag) { int chromSize = end; end = start + relEnd; start = start + relStart; if (end > chromSize) end = chromSize; if (start < 0) start = 0; } singlePos(hgp, "Chromosome Range", NULL, "chromInfo", originalTerm, "", chrom, start, end); } -else if (!matchesHgvs(cart, db, term, hgp)) +else if (!matchesHgvs(cart, db, term, hgp, measureTiming)) { struct hgFindSpec *shortList = NULL, *longList = NULL; struct hgFindSpec *hfs; boolean done = FALSE; // Disable singleBaseSpec for any term that is not hgOfficialChromName // because that mangles legitimate IDs that are [A-Z]:[0-9]+. if (singleBaseSpec) { singleBaseSpec = relativeFlag = FALSE; term = cloneString(originalTerm); // restore original term relStart = relEnd = 0; } if (!trackHubDatabase(db))