feat(search): add SearchMatchData record for per-document match signals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import java.util.List;
|
||||
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.
|
||||
*/
|
||||
public record SearchMatchData(
|
||||
/**
|
||||
* Best-ranked matching transcription line, or null if no block matched.
|
||||
*/
|
||||
String transcriptionSnippet,
|
||||
|
||||
/**
|
||||
* Character offsets of highlighted terms within the document title.
|
||||
* Empty when the title did not contribute to the match.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<MatchOffset> titleOffsets,
|
||||
|
||||
/**
|
||||
* True when the sender's name matched the query.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
boolean senderMatched,
|
||||
|
||||
/**
|
||||
* IDs of receiver persons whose names matched the query.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<UUID> matchedReceiverIds,
|
||||
|
||||
/**
|
||||
* IDs of tags whose names matched the query.
|
||||
*/
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<UUID> matchedTagIds
|
||||
) {
|
||||
/** Canonical "no match data" value for a single document. */
|
||||
public static SearchMatchData empty() {
|
||||
return new SearchMatchData(null, List.of(), false, List.of(), List.of());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SearchMatchDataTest {
|
||||
|
||||
@Test
|
||||
void transcription_snippet_is_nullable() {
|
||||
SearchMatchData data = new SearchMatchData(null, List.of(), false, List.of(), List.of());
|
||||
|
||||
assertThat(data.transcriptionSnippet()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void non_null_list_fields_are_empty_by_default_in_empty_factory() {
|
||||
SearchMatchData data = SearchMatchData.empty();
|
||||
|
||||
assertThat(data.transcriptionSnippet()).isNull();
|
||||
assertThat(data.titleOffsets()).isEmpty();
|
||||
assertThat(data.matchedReceiverIds()).isEmpty();
|
||||
assertThat(data.matchedTagIds()).isEmpty();
|
||||
assertThat(data.senderMatched()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void holds_all_field_values() {
|
||||
MatchOffset offset = new MatchOffset(0, 4);
|
||||
SearchMatchData data = new SearchMatchData(
|
||||
"schreibt dir aus dem Feld",
|
||||
List.of(offset),
|
||||
true,
|
||||
List.of(),
|
||||
List.of()
|
||||
);
|
||||
|
||||
assertThat(data.transcriptionSnippet()).isEqualTo("schreibt dir aus dem Feld");
|
||||
assertThat(data.titleOffsets()).containsExactly(offset);
|
||||
assertThat(data.senderMatched()).isTrue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user