fix(search): recover exact-name and mid-token matches in global search #36791 - #36793
fix(search): recover exact-name and mid-token matches in global search #36791#36793ihoffmann-dot wants to merge 5 commits into
Conversation
|
Claude finished @ihoffmann-dot's task in 3m 5s —— View job Code Review — recover exact-name and mid-token matches in global search
The production change is sound: the mandatory gate is widened from New Issues
Everything else checks out — the |
…rcise score ordering #36791
|
Queued — the merge queue status continues in this comment ↓. |
Merge Queue Status
This pull request spent 1 hour 48 minutes 13 seconds in the queue, including 1 hour 47 minutes 18 seconds running CI. Required conditions to merge
ReasonPull request #36793 has been dequeued GitHub refused to merge the pull request. Pull Request is in the merge queue. This is usually enforced by a branch protection or ruleset rule. HintYou should look at the reason for the failure and decide if the pull request needs to be fixed or if you want to requeue it. Tick the box to put this pull request back in the merge queue (same as
|
Problem
GlobalSearchAttributeStrategy— shared by the Content Search portlet's main search box, Content Drive's keyword search, the Relationships field's dynamic search dialog, and any other Lucene Query Builder consumer — gates matching on a single mandatory clause:+catchall:<term>*(a prefix match). A term that lands mid-token, or spans a tokenizer boundary, never satisfies that gate — even a content's own exact full name can fail to match.Example: a FileAsset named
IMG_1004.jpegtokenizes toimg_1004+jpeg(thestandardtokenizer splits on.but not_). SearchingIMG/img/IMG_1004/jpegworks (genuine prefixes), but1004(mid-token),1004.jpeg(boundary-spanning), and the exact full nameIMG_1004.jpegdo not match — confirmed identically in both the Content Search portlet (via the realLuceneQueryBuilder+ContentletAPI.searchpath) and Content Drive.Found while working on #36688 (PR #36767), which intentionally aligned Content Drive with this same shared strategy for consistency/performance — surfacing this pre-existing, platform-wide gap.
Fix
Widen the mandatory gate from
+catchall:<term>*alone to+(catchall:<term>*^10 OR fieldName_dotraw:*<term>*^2).fieldName_dotraw(e.g.title_dotraw) already exists in the index mapping (keywordtype, no reindex) and is scoped to that one field only — unlikecatchall, which aggregates every field of the document.catchall:*term*that [BUG] Content Drive: keyword/title search returns inconsistent or incoherent results #36688 deliberately removed (that one matched a term anywhere in any field, including unrelated body text — slow and irrelevant).^10vs^2) are deliberate: a genuine token-prefix hit must outrank a raw-substring hit that can land anywhere, including mid-word. Without this, a document found only via the substring fallback could outrank one whose title actually starts with the search term.Matching matrix (validated)
Seeding a FileAsset named
IMG_1004.jpeg, every substring shape a user might type now matches — token prefixes, mid-token fragments, boundary-spanning fragments and the exact full name, in any case:IMG/img/Img/IMG_/IMG_1004/jpeg1004(mid-token)_dotrawsubstring1004.jpeg(spans the.)_dotrawsubstringMG_100(pure mid-substring)_dotrawsubstringG_1004.jpe/g_1004.jpe_dotrawsubstring.jpeg_dotrawsubstringIMG_1004.jpeg/img_1004.jpeg(exact name)_dotrawsubstring1004anywhereValidation
GlobalSearchAttributeStrategyMatchingTest(integration, 7/7 green) covers the matrix above plus relevance ordering.Ranking — important caveat. A genuine token-prefix match ranks above a substring-only match, which is the reason for the asymmetric boosts. This was verified with both items in the same Content Type and the prefix item seeded older than the substring one — so a recency-driven order would rank it last; it ranking first can only come from the score. Verified stable across 3 consecutive runs.
However, relevance ordering only applies when a content type is selected:
LuceneQueryBuilder.getOrderByClause()downgrades the default"score,modDate desc"to plain"modDate desc"whenevercontentTypeIdsis empty. So in the unfiltered global search box, ordering is by modification date and these boosts do not change it — they matter for the content-type-scoped searches (and for which documents match at all, everywhere). That pre-existing behavior is untouched by this PR and is worth a separate conversation if relevance ordering is wanted in the unfiltered case.Tests scope their query to their own test site; without that, ranking assertions pick up whatever other content is in the shared test index and flip depending on which test classes ran in the same JVM.
LuceneQueryBuilderTest's existing global-search test case updated to the new query string.Note (pre-existing, unrelated): running the full
LuceneQueryBuilderTestsuite surfaces one already-failing test (testSearchableFieldsByContentType[With Binary Field]) — its hardcoded expected string doesn't matchBinaryFieldStrategy's current output (which already emits a_dotrawclause, added in unrelated commitb335f43d83). This branch's diff againstBinaryFieldStrategy.javais empty and the failure reproduces onmain. Flagged for visibility, not fixed here.Scope
Closes #36791. Separate from #36688/#36767 (Content Drive keyword search fix) — this is shared, platform-wide search code, opened as its own issue/PR per team discussion. As a side effect, once merged, Content Drive (via the same shared strategy) also gains exact-name/mid-token matching.
Out of scope / follow-ups
_dotrawwildcard at scale (expected cheaper than the oldcatchallwildcard — much smaller term dictionary — but not empirically benchmarked here).🤖 Generated with Claude Code