feat(search): extend hasText to match sender/receiver/tag names, add hasTagPartial

- hasText now JOINs sender (LEFT JOIN) and uses EXISTS subqueries for
  receivers and tags to avoid duplicate rows
- hasTagPartial added for live debounced tag text filter (ILIKE partial match)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-06 13:07:39 +02:00
parent e6f12e6d90
commit beca2d463a
2 changed files with 118 additions and 9 deletions

View File

@@ -250,6 +250,62 @@ class DocumentSpecificationsTest {
assertThat(result).isEmpty();
}
@Test
void hasText_findsByPartialSenderLastName() {
List<Document> result = documentRepository.findAll(Specification.where(hasText("üller")));
assertThat(result).extracting(Document::getTitle)
.containsExactlyInAnyOrder("Alter Brief", "Neuerer Brief");
}
@Test
void hasText_findsByPartialReceiverLastName() {
List<Document> result = documentRepository.findAll(Specification.where(hasText("schmid")));
assertThat(result).extracting(Document::getTitle).containsExactly("Alter Brief");
}
@Test
void hasText_findsByPartialTagName() {
List<Document> result = documentRepository.findAll(Specification.where(hasText("amili")));
assertThat(result).extracting(Document::getTitle)
.containsExactlyInAnyOrder("Alter Brief", "Familienfoto");
}
@Test
void hasText_doesNotProduceDuplicatesForDocumentWithMultipleReceivers() {
Person receiver2 = personRepository.save(Person.builder().firstName("Karl").lastName("Schmidt").build());
briefEarly.setReceivers(new java.util.HashSet<>(Set.of(receiver, receiver2)));
documentRepository.save(briefEarly);
List<Document> result = documentRepository.findAll(Specification.where(hasText("schmid")));
assertThat(result).hasSize(1);
}
// ─── hasTagPartial ────────────────────────────────────────────────────────
@Test
void hasTagPartial_returnsAllDocuments_whenTextIsNull() {
List<Document> result = documentRepository.findAll(Specification.where(hasTagPartial(null)));
assertThat(result).hasSize(3);
}
@Test
void hasTagPartial_findsByPartialTagName() {
List<Document> result = documentRepository.findAll(Specification.where(hasTagPartial("amili")));
assertThat(result).extracting(Document::getTitle).containsExactly("Alter Brief");
}
@Test
void hasTagPartial_isCaseInsensitive() {
List<Document> result = documentRepository.findAll(Specification.where(hasTagPartial("URLAUB")));
assertThat(result).extracting(Document::getTitle).containsExactly("Neuerer Brief");
}
@Test
void hasTagPartial_returnsEmpty_whenNoTagMatches() {
List<Document> result = documentRepository.findAll(Specification.where(hasTagPartial("xyz")));
assertThat(result).isEmpty();
}
// ─── hasStatus ────────────────────────────────────────────────────────────
@Test