98faa189349f1bb9b033f314ffe327541bacf290 chmalee Tue Mar 31 15:17:30 2026 -0700 Add a new hgFindSpec setting, searchItemLabel, that allows using hgFindSpec defined labels (with variable substitution) as the label for each line of a search result. This only works for bigBed tracks. refs #37299 diff --git src/hg/lib/bigBedFind.c src/hg/lib/bigBedFind.c index 318c93a76e7..2329a842b6e 100644 --- src/hg/lib/bigBedFind.c +++ src/hg/lib/bigBedFind.c @@ -11,102 +11,121 @@ #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 *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; +// Generic searchItemLabel support: allows ${fieldName} patterns in hgFindSpec +char *searchItemLabel = NULL; +char **fieldNames = NULL; +if (hfs) + searchItemLabel = hgFindSpecSetting(hfs, "searchItemLabel"); +if (searchItemLabel) + { + AllocArray(fieldNames, bbi->fieldCount); + struct slName *field = NULL, *fields = bbFieldNames(bbi); + int i = 0; + for (field = fields; field != NULL && i < bbi->fieldCount; field = field->next) + fieldNames[i++] = field->name; + } + +// MANE/HGNC description support struct asObject *as = NULL; int ncbiIdIx = -1, geneNameIx = -1; struct sqlConnection *conn = NULL; if (hfs && (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 ); +char startBuf[256], endBuf[256], *row[bbi->fieldCount]; 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 (searchItemLabel) + { + bigBedIntervalToRow(interval, chromName, startBuf, endBuf, row, bbi->fieldCount); + hgPos->name = replaceFieldInPattern(searchItemLabel, bbi->fieldCount, fieldNames, row); + } + else + { + hgPos->name = bigBedMakeLabel(tdb, labelColumns, interval, chromName); + if (hfs && (sameString(hfs->searchName, "mane") || sameString(hfs->searchName, "hgnc"))) + bigBedIntervalToRow(interval, chromName, startBuf, endBuf, row, bbi->fieldCount); + } + 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); + // MANE/HGNC description logic 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 + else if (sameString(hfs->searchName, "hgnc")) { // 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 *db) /* Given a bigBed file with a search index, check for term. */ { struct errCatch *errCatch = errCatchNew(); struct hgPos *posList = NULL; if (errCatchStart(errCatch)) {