feat(dto): add DocumentSearchItem record and refactor DocumentSearchResult to items-based shape
Replaces {documents, matchData, total} with {items: List<DocumentSearchItem>, total}
where each item collocates document + matchData + completionPercentage + contributors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.raddatz.familienarchiv.audit.ActivityActorDTO;
|
||||
import org.raddatz.familienarchiv.model.Document;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DocumentSearchItem(
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
Document document,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
SearchMatchData matchData,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
int completionPercentage,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<ActivityActorDTO> contributors
|
||||
) {}
|
||||
@@ -1,35 +1,16 @@
|
||||
package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.raddatz.familienarchiv.model.Document;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public record DocumentSearchResult(
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
List<Document> documents,
|
||||
List<DocumentSearchItem> items,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
long total,
|
||||
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
Map<UUID, SearchMatchData> matchData
|
||||
long total
|
||||
) {
|
||||
/**
|
||||
* Creates a fully-enriched result from documents and their match overlay data.
|
||||
* Absent map entries (e.g. document deleted between FTS and enrichment) are safe —
|
||||
* the frontend treats a missing entry as "no match data".
|
||||
*/
|
||||
public static DocumentSearchResult withMatchData(List<Document> documents, Map<UUID, SearchMatchData> matchData) {
|
||||
return new DocumentSearchResult(documents, documents.size(), matchData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a result without match data — used for filter-only searches (no text query).
|
||||
* No pagination yet — the full matched set is always returned.
|
||||
* When pagination is added, total must come from a DB COUNT query, not list.size().
|
||||
*/
|
||||
public static DocumentSearchResult of(List<Document> documents) {
|
||||
return withMatchData(documents, Map.of());
|
||||
public static DocumentSearchResult of(List<DocumentSearchItem> items) {
|
||||
return new DocumentSearchResult(items, items.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,59 +2,52 @@ package org.raddatz.familienarchiv.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.raddatz.familienarchiv.audit.ActivityActorDTO;
|
||||
import org.raddatz.familienarchiv.model.Document;
|
||||
import org.raddatz.familienarchiv.model.DocumentStatus;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DocumentSearchResultTest {
|
||||
|
||||
private Document doc(UUID id) {
|
||||
return Document.builder()
|
||||
.id(id)
|
||||
private DocumentSearchItem item(UUID docId) {
|
||||
Document doc = Document.builder()
|
||||
.id(docId)
|
||||
.title("Test")
|
||||
.originalFilename("test.pdf")
|
||||
.status(DocumentStatus.UPLOADED)
|
||||
.build();
|
||||
return new DocumentSearchItem(doc, SearchMatchData.empty(), 0, List.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withMatchData_total_equals_list_size() {
|
||||
void of_total_equals_list_size() {
|
||||
DocumentSearchResult result = DocumentSearchResult.of(List.of(item(UUID.randomUUID()), item(UUID.randomUUID())));
|
||||
|
||||
assertThat(result.total()).isEqualTo(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void of_exposes_items_with_completion_and_contributors() {
|
||||
UUID id = UUID.randomUUID();
|
||||
List<Document> docs = List.of(doc(id));
|
||||
Map<UUID, SearchMatchData> matchData = Map.of(id, SearchMatchData.empty());
|
||||
ActivityActorDTO actor = new ActivityActorDTO("AB", "#f00", "Anna Braun");
|
||||
Document doc = Document.builder().id(id).title("T").originalFilename("t.pdf")
|
||||
.status(DocumentStatus.UPLOADED).build();
|
||||
DocumentSearchItem item = new DocumentSearchItem(doc, SearchMatchData.empty(), 75, List.of(actor));
|
||||
|
||||
DocumentSearchResult result = DocumentSearchResult.withMatchData(docs, matchData);
|
||||
DocumentSearchResult result = DocumentSearchResult.of(List.of(item));
|
||||
|
||||
assertThat(result.total()).isEqualTo(1L);
|
||||
assertThat(result.items()).hasSize(1);
|
||||
assertThat(result.items().get(0).completionPercentage()).isEqualTo(75);
|
||||
assertThat(result.items().get(0).contributors()).containsExactly(actor);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withMatchData_exposes_match_data_map() {
|
||||
UUID id = UUID.randomUUID();
|
||||
SearchMatchData data = new SearchMatchData("snippet", List.of(), false, List.of(), List.of(), List.of(), null, List.of());
|
||||
DocumentSearchResult result = DocumentSearchResult.withMatchData(List.of(doc(id)), Map.of(id, data));
|
||||
|
||||
assertThat(result.matchData()).containsKey(id);
|
||||
assertThat(result.matchData().get(id).transcriptionSnippet()).isEqualTo("snippet");
|
||||
}
|
||||
|
||||
@Test
|
||||
void of_factory_returns_empty_match_data() {
|
||||
UUID id = UUID.randomUUID();
|
||||
DocumentSearchResult result = DocumentSearchResult.of(List.of(doc(id)));
|
||||
|
||||
assertThat(result.matchData()).isEmpty();
|
||||
assertThat(result.total()).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void documents_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
|
||||
Schema schema = DocumentSearchResult.class.getDeclaredField("documents").getAnnotation(Schema.class);
|
||||
void items_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException {
|
||||
Schema schema = DocumentSearchResult.class.getDeclaredField("items").getAnnotation(Schema.class);
|
||||
assertThat(schema).isNotNull();
|
||||
assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user