401691176ea0ade97d1c643bc2ed4c382fee1ef2 chmalee Fri Nov 3 16:59:50 2023 -0700 Fix multi-term searches when using genomePosCJ: 1. make sure to collapse hgps together and combine highlight strings and item highlight names, 2. make sure hgSearch checks the json data returned during its init function if there was an error from the search term, such as different chromosomes for a multi-term search, refs #22978 diff --git src/hg/cgilib/cartJson.c src/hg/cgilib/cartJson.c index 08a721b..4e4229b 100644 --- src/hg/cgilib/cartJson.c +++ src/hg/cgilib/cartJson.c @@ -138,49 +138,92 @@ { stripString(pos->description, "\n"); jsonWriteString(jw, "description", stripAnchor(pos->description)); } jsonWriteObjectEnd(jw); // end one match } jsonWriteListEnd(jw); // end matches if (table->searchTime >= 0) jsonWriteNumber(jw, "searchTime", table->searchTime); jsonWriteObjectEnd(jw); // end one table } } jsonWriteListEnd(jw); // end positionMatches } +static struct hgPositions *collapseHgpList(struct hgPositions *hgpList, struct jsonWrite *jw, char *searchTerm, char *db) +/* Combine a bunch of singlePos hgps together */ +{ +struct hgPositions *hgp = hgpList, *final = NULL; +AllocVar(final); +final->query = searchTerm; +final->database = cloneString(hgp->database); +final->tableList = hgp->tableList; +final->posCount = hgp->posCount; +final->singlePos = hgp->singlePos; +final->extraCgi = cloneString(final->extraCgi); +final->useAlias = hgp->useAlias; +final->shortCircuited = hgp->shortCircuited; + +struct dyString *newHighlight = dyStringNew(0); +dyStringPrintf(newHighlight, "%s", hgp->singlePos->highlight); +struct dyString *newPosNames = dyStringNew(0); +dyStringPrintf(newPosNames, "%s", hgp->singlePos->name); +for (hgp = hgpList->next; hgp != NULL; hgp = hgp->next) + { + if (!hgp->singlePos || + (final && !(sameString(final->singlePos->chrom, hgp->singlePos->chrom)))) + { + jsonWriteStringf(jw, "error", "collapsing hgps for hits on different chromsomes, database: '%s', searchTerm: '%s'", db, searchTerm); + return NULL; + } + else + { + dyStringPrintf(newHighlight, "|%s", hgp->singlePos->highlight); + dyStringPrintf(newPosNames, ",%s", hgp->singlePos->name); + final->posCount += hgp->posCount; + if (final->singlePos->chromStart > hgp->singlePos->chromStart) + final->singlePos->chromStart = hgp->singlePos->chromStart; + if (final->singlePos->chromEnd < hgp->singlePos->chromEnd) + final->singlePos->chromEnd = hgp->singlePos->chromEnd; + } + } +final->singlePos->highlight = dyStringCannibalize(&newHighlight); +final->singlePos->name = dyStringCannibalize(&newPosNames); +return final; +} + struct hgPositions *genomePosCJ(struct jsonWrite *jw, char *db, char *spec, char **retChromName, int *retWinStart, int *retWinEnd, struct cart *cart, struct searchCategory *categories, boolean categorySearch) /* Search for positions in genome that match user query. * Return an hgp unless there is a problem. hgp->singlePos will be set if a single * position matched. * Otherwise display list of positions, put # of positions in retWinStart, * and return NULL. */ { char *hgAppName = "cartJson"; struct hgPositions *hgp = NULL; char *chrom = NULL; int start = BIGNUM; int end = 0; boolean measureTiming = cartUsualBoolean(cart, "measureTiming", FALSE); char *terms[16]; int termCount = chopByChar(cloneString(spec), ';', terms, ArraySize(terms)); boolean multiTerm = (termCount > 1); +struct hgPositions *hgpList = NULL; // if a multiTerm search we need to collapse the hgps together int i = 0; for (i = 0; i < termCount; i++) { trimSpaces(terms[i]); if (isEmpty(terms[i])) continue; hgp = hgPositionsFind(db, terms[i], "", hgAppName, cart, multiTerm, measureTiming, categories); if (hgp == NULL || hgp->posCount == 0) { jsonWriteStringf(jw, "error", "Sorry, couldn't locate %s in %s %s", htmlEncode(terms[i]), trackHubSkipHubName(hOrganism(db)), hFreezeDate(db)); if (multiTerm) jsonWriteStringf(jw, "error", @@ -191,51 +234,59 @@ } if (hgp->singlePos != NULL) { if (!categorySearch && chrom != NULL && !sameString(chrom, hgp->singlePos->chrom)) { jsonWriteStringf(jw, "error", "Sites occur on different chromosomes: %s, %s.", chrom, hgp->singlePos->chrom); return NULL; } chrom = hgp->singlePos->chrom; if (hgp->singlePos->chromStart < start) start = hgp->singlePos->chromStart; if (hgp->singlePos->chromEnd > end) end = hgp->singlePos->chromEnd; + if (multiTerm) + { + slAddHead(&hgpList, hgp); + } } else { hgPositionsJson(jw, db, hgp, cart); if (multiTerm && !categorySearch && hgp->posCount != 1) { jsonWriteStringf(jw, "error", "%s not uniquely determined (%d locations) -- " "can't do multi-position search.", terms[i], hgp->posCount); return NULL; } break; } } if (hgp != NULL) { *retChromName = chrom; *retWinStart = start; *retWinEnd = end; } +if (multiTerm && hgpList != NULL) + { + return collapseHgpList(hgpList, jw, spec, db); + } return hgp; } static void changePosition(struct cartJson *cj, char *newPosition) /* Update position in cart, after performing lookup if necessary. * Usually we don't report what we just changed, but since we might modify it, * print out the final value. */ { char *db = cartString(cj->cart, "db"); char *chrom = NULL; int start=0, end=0; struct hgPositions *hgp = genomePosCJ(cj->jw, db, newPosition, &chrom, &start, &end, cj->cart, NULL, TRUE); // If it resolved to a single position, update the cart; otherwise the app can // present the error (or list of matches) to the user. if (hgp && hgp->singlePos)