feat(documents): add DocumentService.getDensity (#385)
Maps the repository's Object[] rows into a DocumentDensityResult and pairs them with the archive-wide min/max meta_date range. Read-only, no @Transactional needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -125,6 +125,19 @@ public class DocumentService {
|
||||
return titles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-month document counts for the timeline density widget (issue #385).
|
||||
* Returns only months that have at least one document — the frontend fills
|
||||
* gaps using the {@code minDate}/{@code maxDate} bounds.
|
||||
*/
|
||||
public DocumentDensityResult getDensity(LocalDate from, LocalDate to) {
|
||||
List<MonthBucket> buckets = documentRepository.findDensityByMonth(from, to).stream()
|
||||
.map(row -> new MonthBucket((String) row[0], ((Number) row[1]).intValue()))
|
||||
.toList();
|
||||
DocumentDateRangeProjection range = documentRepository.findMinMaxDocumentDate();
|
||||
return new DocumentDensityResult(buckets, range.getMinDate(), range.getMaxDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt eine Datei hoch.
|
||||
* - Prüft, ob ein Eintrag (aus Excel) schon existiert.
|
||||
|
||||
@@ -2321,4 +2321,56 @@ class DocumentServiceTest {
|
||||
assertThat(documentService.save(doc)).isEqualTo(doc);
|
||||
verify(documentRepository).save(doc);
|
||||
}
|
||||
|
||||
// ─── getDensity ────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void getDensity_returnsEmptyResult_whenArchiveIsEmpty() {
|
||||
when(documentRepository.findDensityByMonth(null, null)).thenReturn(List.of());
|
||||
DocumentDateRangeProjection emptyRange = mock(DocumentDateRangeProjection.class);
|
||||
when(emptyRange.getMinDate()).thenReturn(null);
|
||||
when(emptyRange.getMaxDate()).thenReturn(null);
|
||||
when(documentRepository.findMinMaxDocumentDate()).thenReturn(emptyRange);
|
||||
|
||||
DocumentDensityResult result = documentService.getDensity(null, null);
|
||||
|
||||
assertThat(result.buckets()).isEmpty();
|
||||
assertThat(result.minDate()).isNull();
|
||||
assertThat(result.maxDate()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDensity_mapsRepositoryRowsToMonthBuckets() {
|
||||
List<Object[]> rows = List.of(
|
||||
new Object[]{"1915-08", 2L},
|
||||
new Object[]{"1915-09", 1L}
|
||||
);
|
||||
when(documentRepository.findDensityByMonth(null, null)).thenReturn(rows);
|
||||
DocumentDateRangeProjection range = mock(DocumentDateRangeProjection.class);
|
||||
when(range.getMinDate()).thenReturn(LocalDate.of(1915, 8, 3));
|
||||
when(range.getMaxDate()).thenReturn(LocalDate.of(1915, 9, 1));
|
||||
when(documentRepository.findMinMaxDocumentDate()).thenReturn(range);
|
||||
|
||||
DocumentDensityResult result = documentService.getDensity(null, null);
|
||||
|
||||
assertThat(result.buckets()).extracting(MonthBucket::month)
|
||||
.containsExactly("1915-08", "1915-09");
|
||||
assertThat(result.buckets()).extracting(MonthBucket::count)
|
||||
.containsExactly(2, 1);
|
||||
assertThat(result.minDate()).isEqualTo(LocalDate.of(1915, 8, 3));
|
||||
assertThat(result.maxDate()).isEqualTo(LocalDate.of(1915, 9, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getDensity_passesFromAndToBoundsToRepository() {
|
||||
when(documentRepository.findDensityByMonth(any(), any())).thenReturn(List.of());
|
||||
DocumentDateRangeProjection range = mock(DocumentDateRangeProjection.class);
|
||||
when(documentRepository.findMinMaxDocumentDate()).thenReturn(range);
|
||||
|
||||
LocalDate from = LocalDate.of(1914, 1, 1);
|
||||
LocalDate to = LocalDate.of(1918, 12, 31);
|
||||
documentService.getDensity(from, to);
|
||||
|
||||
verify(documentRepository).findDensityByMonth(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user