feat(search): surface summary snippet when summary matched the query
Add a summary_snippet column to findEnrichmentData using ts_headline on documents.summary, only when the summary's tsvector matches the query. Expose it via SearchMatchData.summarySnippet / summaryOffsets and render a "Zusammenfassung" / "Summary" / "Resumen" labelled row in the document list — identical treatment to the transcription snippet row. Fixes the case where a document appeared in search results with no visible match explanation (e.g. searching "frucht" found a document whose summary mentioned "Früchte"). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #242.
This commit is contained in:
@@ -7,8 +7,8 @@ import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Match signals for a single document in a full-text search result.
|
||||
* All fields are non-null except {@code transcriptionSnippet}, which is null
|
||||
* when no transcription block matched the query.
|
||||
* All fields are non-null except {@code transcriptionSnippet} and {@code summarySnippet},
|
||||
* which are null when the respective field did not match the query.
|
||||
*/
|
||||
public record SearchMatchData(
|
||||
/**
|
||||
@@ -46,10 +46,22 @@ public record SearchMatchData(
|
||||
* Empty when no transcription block matched or the snippet has no highlights.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<MatchOffset> snippetOffsets
|
||||
List<MatchOffset> snippetOffsets,
|
||||
|
||||
/**
|
||||
* Highlighted summary excerpt, or null if the summary did not match the query.
|
||||
*/
|
||||
String summarySnippet,
|
||||
|
||||
/**
|
||||
* Character offsets of highlighted terms within the summary snippet.
|
||||
* Empty when the summary did not match or has no highlights.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<MatchOffset> summaryOffsets
|
||||
) {
|
||||
/** Canonical "no match data" value for a single document. */
|
||||
public static SearchMatchData empty() {
|
||||
return new SearchMatchData(null, List.of(), false, List.of(), List.of(), List.of());
|
||||
return new SearchMatchData(null, List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,14 +607,18 @@ public class DocumentService {
|
||||
Boolean senderMatched = (Boolean) row[3];
|
||||
String receiverIdsStr = (String) row[4];
|
||||
String tagIdsStr = (String) row[5];
|
||||
String summaryHeadline = (String) row[6];
|
||||
ParsedHighlight snippet = parseHighlight(snippetHeadline);
|
||||
ParsedHighlight summary = parseHighlight(summaryHeadline);
|
||||
result.put(docId, new SearchMatchData(
|
||||
snippet != null ? snippet.cleanText() : null,
|
||||
parseTitleOffsets(titleHeadline),
|
||||
senderMatched != null && senderMatched,
|
||||
parseUUIDs(receiverIdsStr),
|
||||
parseUUIDs(tagIdsStr),
|
||||
snippet != null ? snippet.offsets() : List.of()
|
||||
snippet != null ? snippet.offsets() : List.of(),
|
||||
summary != null ? summary.cleanText() : null,
|
||||
summary != null ? summary.offsets() : List.of()
|
||||
));
|
||||
}
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user