feat(documents): add density and date-range repository queries (#385)
Native SQL aggregations backing GET /api/documents/density: - findDensityByMonth groups documents by truncated meta_date with optional from/to bounds (frontend fills zero-count gaps). - findMinMaxDocumentDate returns the earliest/latest meta_date via projection, null on empty archive. Covered by DocumentDensityIntegrationTest (Testcontainers PostgreSQL): empty archive, single+multi-month grouping, from/to bounds, null meta_date exclusion, min/max edge cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package org.raddatz.familienarchiv.document;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Spring Data projection for the document_date min/max query backing the timeline
|
||||
* density widget (issue #385). Column aliases in the native SQL must match these
|
||||
* getter names exactly. Both values are {@code null} when the documents table is empty.
|
||||
*/
|
||||
public interface DocumentDateRangeProjection {
|
||||
LocalDate getMinDate();
|
||||
LocalDate getMaxDate();
|
||||
}
|
||||
@@ -240,4 +240,28 @@ public interface DocumentRepository extends JpaRepository<Document, UUID>, JpaSp
|
||||
""")
|
||||
TranscriptionWeeklyStatsProjection findWeeklyStats();
|
||||
|
||||
/**
|
||||
* Document count grouped by calendar month for the timeline density widget (issue #385).
|
||||
* Each row is {@code Object[]{ String yearMonth, long count }}.
|
||||
* Months without documents are not present — the frontend fills gaps from minDate/maxDate.
|
||||
*/
|
||||
@Query(nativeQuery = true, value = """
|
||||
SELECT TO_CHAR(DATE_TRUNC('month', meta_date), 'YYYY-MM') AS month,
|
||||
COUNT(*) AS doc_count
|
||||
FROM documents
|
||||
WHERE meta_date IS NOT NULL
|
||||
AND (CAST(:from AS date) IS NULL OR meta_date >= :from)
|
||||
AND (CAST(:to AS date) IS NULL OR meta_date <= :to)
|
||||
GROUP BY 1
|
||||
ORDER BY 1
|
||||
""")
|
||||
List<Object[]> findDensityByMonth(@Param("from") LocalDate from, @Param("to") LocalDate to);
|
||||
|
||||
/**
|
||||
* Earliest and latest {@code meta_date} across all documents. Both getters return
|
||||
* {@code null} when the table holds no documents.
|
||||
*/
|
||||
@Query(nativeQuery = true, value = "SELECT MIN(meta_date) AS minDate, MAX(meta_date) AS maxDate FROM documents")
|
||||
DocumentDateRangeProjection findMinMaxDocumentDate();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user