From 7183d15fe597e37f25c8e47801ac3c13ac994964 Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 27 May 2026 21:04:48 +0200 Subject: [PATCH] fix(document): restore pure-text-relevance FTS fast path past undated count The global undated-count rework moved the pure-text-RELEVANCE shortcut into runSearch, where it ran after the unconditional findAllMatchingIdsByFts call. That routed pure-text relevance through the in-memory id path and returned empty match data, breaking FTS rank order and snippet/offset enrichment. Hoist the shortcut back to the top of searchDocuments so it short-circuits to findFtsPageRaw before findAllMatchingIdsByFts, while still computing the global undatedCount for all non-fast-path searches. Refs #668 Co-Authored-By: Claude Opus 4.7 --- .../document/DocumentService.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java index d25cf03c..73c60e43 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java @@ -669,6 +669,18 @@ public class DocumentService { public DocumentSearchResult searchDocuments(String text, LocalDate from, LocalDate to, UUID sender, UUID receiver, List tags, String tagQ, DocumentStatus status, DocumentSort sort, String dir, TagOperator tagOperator, boolean undated, Pageable pageable) { boolean hasText = StringUtils.hasText(text); + // Pure-text RELEVANCE: push pagination + ts_rank ordering into SQL — skip + // findAllMatchingIdsByFts entirely (ADR-008). This must run BEFORE any + // findAllMatchingIdsByFts call so the fast path is preserved. An active undated + // filter must NOT take this path: it bypasses buildSearchSpec, so the + // undatedOnly predicate would be silently dropped. By definition this path has + // no date/sender/receiver/tag/status filters, and undated documents are valid + // FTS hits already folded into the ranked page, so there is no separate undated + // count to report here. + if (!undated && isPureTextRelevance(hasText, sort, from, to, sender, receiver, tags, tagQ, status)) { + return relevanceSortedPageFromSql(text, pageable); + } + List rankedIds = null; if (hasText) { rankedIds = documentRepository.findAllMatchingIdsByFts(text); @@ -706,13 +718,8 @@ public class DocumentService { List tags, String tagQ, DocumentStatus status, DocumentSort sort, String dir, TagOperator tagOperator, boolean undated, Pageable pageable) { - // Pure-text RELEVANCE: push pagination into SQL — skip findAllMatchingIdsByFts entirely (ADR-008). - // An active undated filter must NOT take this path: it bypasses buildSearchSpec, so the - // undatedOnly predicate would be silently dropped. - if (!undated && isPureTextRelevance(hasText, sort, from, to, sender, receiver, tags, tagQ, status)) { - return relevanceSortedPageFromSql(text, pageable); - } - + // The pure-text RELEVANCE fast path is handled by the caller (searchDocuments) + // before findAllMatchingIdsByFts runs, so it never reaches here (ADR-008). Specification spec = buildSearchSpec( hasText, rankedIds, from, to, sender, receiver, tags, tagQ, status, tagOperator, undated);