ab2163a1bcc671f938eee484695ad77df98b4d79 chmalee Fri Feb 13 11:15:47 2026 -0800 Add special item search descriptions to MANE and HGNC tracks, refs #34483 diff --git src/hg/lib/bigBedFind.c src/hg/lib/bigBedFind.c index d0a354d8167..5edf68b624e 100644 --- src/hg/lib/bigBedFind.c +++ src/hg/lib/bigBedFind.c @@ -1,105 +1,149 @@ #include "common.h" #include "bPlusTree.h" #include "bbiFile.h" #include "bigBed.h" #include "hgFind.h" #include "trix.h" #include "trackHub.h" #include "hubConnect.h" #include "hdb.h" #include "errCatch.h" #include "bigBedLabel.h" #include "bigBedFind.h" +#include "genbank.h" static struct hgPos *bigBedIntervalListToHgPositions(struct cart *cart, struct trackDb *tdb, struct bbiFile *bbi, char *term, struct bigBedInterval *intervalList, - char *description, struct hgFindSpec *hfs) + char *description, struct hgFindSpec *hfs, char *db) /* Given an open bigBed file, and an interval list, return a pointer to a list of hgPos structures. */ { struct hgPos *posList = NULL; char chromName[bbi->chromBpt->keySize+1]; int lastChromId = -1; struct bigBedInterval *interval; struct slInt *labelColumns = NULL; +struct asObject *as = NULL; +int ncbiIdIx = -1, geneNameIx = -1; +struct sqlConnection *conn = NULL; +if (sameString(hfs->searchName, "mane") || sameString(hfs->searchName, "hgnc")) + { + // TODO: right now we are only doing this for MANE and HGNC, but if we are gonna add + // special descriptions to more tracks in the future then we should have some generic + // way of attaching a description to an hfs, whether that hfs is a mysql search or a + // bigBed search + as = bigBedAsOrDefault(bbi); + if (sameString(hfs->searchName, "mane")) + { + conn = hAllocConn(db); + ncbiIdIx = asColumnFindIx(as->columnList, "ncbiId"); + } + else if (sameString(hfs->searchName, "hgnc")) + geneNameIx = asColumnFindIx(as->columnList, "geneName"); + } + bigBedLabelCalculateFields(cart, tdb, bbi, &labelColumns ); for (interval = intervalList; interval != NULL; interval = interval->next) { struct hgPos *hgPos; AllocVar(hgPos); slAddHead(&posList, hgPos); bbiCachedChromLookup(bbi, interval->chromId, lastChromId, chromName, sizeof(chromName)); lastChromId = interval->chromId; hgPos->chrom = cloneString(chromName); hgPos->chromStart = interval->start; hgPos->chromEnd = interval->end; hgPos->name = bigBedMakeLabel(tdb, labelColumns, interval, chromName); hgPos->browserName = cloneString(term); hgPos->description = cloneString(description); if (hfs) { char *paddingStr = hgFindSpecSetting(hfs, "padding"); int padding = isEmpty(paddingStr) ? 0 : atoi(paddingStr); if (padding > 0) { // highlight the item bases only, to distinguish from padding hgPos->highlight = addHighlight(cartString(cart, "db"), hgPos->chrom, hgPos->chromStart, hgPos->chromEnd); hgPos->chromStart -= padding; hgPos->chromEnd += padding; if (hgPos->chromStart < 0) hgPos->chromStart = 0; } + // special code here for per hfs bigBed searches + if (sameString(hfs->searchName, "mane") || sameString(hfs->searchName, "hgnc")) + { + char startBuf[256], endBuf[256], *row[bbi->fieldCount]; + bigBedIntervalToRow(interval, chromName, startBuf, endBuf, row, bbi->fieldCount); + if (sameString(hfs->searchName, "mane")) + { + // here the description comes from hgFixed.refLink.product, linked via mane.bb.ncbiId + if (ncbiIdIx > 0) + { + struct dyString *query = sqlDyStringCreate("select product from %s where mrnaAcc=substring_index('%s', '.', 1)", refLinkTable, row[ncbiIdIx]); + hgPos->description = sqlQuickString(conn, dyStringCannibalize(&query)); + } + } + else + { + // the description is the geneName field of the bigBed + if (geneNameIx > 0) + hgPos->description = cloneString(row[geneNameIx]); + } + } } } +if (conn) + hFreeConn(&conn); + return posList; } static struct hgPos *getPosFromBigBed(struct cart *cart, struct trackDb *tdb, struct bbiFile *bbi, - char *indexField, char *term, char *description, struct hgFindSpec *hfs) + char *indexField, char *term, char *description, struct hgFindSpec *hfs, char *db) /* Given a bigBed file with a search index, check for term. */ { struct errCatch *errCatch = errCatchNew(); struct hgPos *posList = NULL; if (errCatchStart(errCatch)) { int fieldIx; struct bptFile *bpt = bigBedOpenExtraIndex(bbi, indexField, &fieldIx); struct lm *lm = lmInit(0); struct bigBedInterval *intervalList; intervalList = bigBedNameQuery(bbi, bpt, fieldIx, term, lm); - posList = bigBedIntervalListToHgPositions(cart, tdb, bbi, term, intervalList, description, hfs); + posList = bigBedIntervalListToHgPositions(cart, tdb, bbi, term, intervalList, description, hfs, db); bptFileDetach(&bpt); } errCatchEnd(errCatch); if (errCatch->gotError) { // we fail silently if there is a problem e.g. bad index name return NULL; } return posList; } static struct hgPos *doTrixSearch(struct cart *cart, struct trackDb *tdb, char *trixFile, struct slName *indices, struct bbiFile *bbi, char *term, char *description, - struct hgFindSpec *hfs) + struct hgFindSpec *hfs, char *db) /* search a trix file in the "searchTrix" field of a bigBed trackDb */ { struct trix *trix = trixOpen(trixFile); int trixWordCount = 0; char *tmp = cloneString(term); char *val = nextWord(&tmp); char *trixWords[128]; while (val != NULL) { trixWords[trixWordCount] = strLower(val); trixWordCount++; if (trixWordCount == sizeof(trixWords)/sizeof(char*)) errAbort("exhausted space for trixWords"); @@ -117,31 +161,31 @@ boolean doSnippets = FALSE; if (context && sameString(context, "on")) { doSnippets = TRUE; initSnippetIndex(trix); } struct hgPos *posList = NULL; for ( ; tsList != NULL; tsList = tsList->next) { struct slName *oneIndex = indices; if (doSnippets) addSnippetForResult(tsList, trix); for (; oneIndex; oneIndex = oneIndex->next) { struct hgPos *posList2 = getPosFromBigBed(cart, tdb, bbi, oneIndex->name, - tsList->itemId, tsList->snippet, hfs); + tsList->itemId, tsList->snippet, hfs, db); posList = slCat(posList, posList2); } } return posList; } int posListCompare(const void *va, const void *vb) /* Compare to sort based on name and then position. */ { const struct hgPos *a = *((struct hgPos **)va); const struct hgPos *b = *((struct hgPos **)vb); int diff = strcmp(a->name, b->name); if (diff == 0) @@ -222,43 +266,43 @@ { bigBedFileClose(&bbi); return FALSE; } struct slName *indexList = slNameListFromString(indexField, ','); struct hgPos *posList1 = NULL, *posList2 = NULL; char *trixFile = trackDbSetting(tdb, "searchTrix"); // if there is a trix file, use it to search for the term if (trixFile != NULL) { struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { posList1 = doTrixSearch(cart, tdb, hReplaceGbdb(trixFile), indexList, bbi, term, - NULL, hfs); + NULL, hfs, db); } errCatchEnd(errCatch); if (errCatch->gotError) warn("trix search failure for %s: %s", tdb->table, dyStringContents(errCatch->message)); errCatchFree(&errCatch); } // now search for the raw id's struct slName *oneIndex=indexList; for (; oneIndex; oneIndex = oneIndex->next) { - posList2 = getPosFromBigBed(cart, tdb, bbi, oneIndex->name, term, NULL, hfs); + posList2 = getPosFromBigBed(cart, tdb, bbi, oneIndex->name, term, NULL, hfs, db); posList1 = slCat(posList1, posList2); } // the trix search and the id search may have found the same item so uniqify: slUniqify(&posList1, posListCompare, hgPosFree); if (posList1 != NULL) { struct hgPosTable *table; found = TRUE; AllocVar(table); slAddHead(&hgp->tableList, table); table->description = cloneString(description ? description : tdb->longLabel); table->name = cloneString(tdb->table); table->searchTime = -1;