Merge pull request 'feat(manga): update title search to return multiple results' (#65) from feat/concurrency into main

Reviewed-on: #65
This commit is contained in:
rov 2026-06-24 09:22:46 -03:00
commit cc4c8105ef
2 changed files with 33 additions and 12 deletions

View File

@ -9,7 +9,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface MangaRepository public interface MangaRepository
extends JpaRepository<Manga, Long>, JpaSpecificationExecutor<Manga> { extends JpaRepository<Manga, Long>, JpaSpecificationExecutor<Manga> {
Optional<Manga> findByTitleIgnoreCase(String title); List<Manga> findAllByTitleIgnoreCase(String title);
List<Manga> findByFollowTrue(); List<Manga> findByFollowTrue();

View File

@ -26,9 +26,14 @@ public class MangaResolutionService {
private final MangaRepository mangaRepository; private final MangaRepository mangaRepository;
public Manga findOrCreateManga(String searchTitle) { public Manga findOrCreateManga(String searchTitle) {
var existingManga = mangaRepository.findByTitleIgnoreCase(searchTitle); var existingMangas = mangaRepository.findAllByTitleIgnoreCase(searchTitle);
if (existingManga.isPresent()) { if (existingMangas.size() == 1) {
return existingManga.get(); return existingMangas.get(0);
}
if (existingMangas.size() > 1) {
log.warn(
"Multiple mangas with title '{}', falling through to external ID resolution",
searchTitle);
} }
var aniListResult = searchMangaOnAniList(searchTitle); var aniListResult = searchMangaOnAniList(searchTitle);
@ -130,9 +135,16 @@ public class MangaResolutionService {
} }
if (nonNull(canonicalTitle)) { if (nonNull(canonicalTitle)) {
var existingByTitle = mangaRepository.findByTitleIgnoreCase(canonicalTitle); var existingByTitle = mangaRepository.findAllByTitleIgnoreCase(canonicalTitle);
if (existingByTitle.isPresent()) { if (existingByTitle.size() == 1) {
return mergeExternalIds(existingByTitle.get(), canonicalTitle, aniListId, malId); return mergeExternalIds(existingByTitle.get(0), canonicalTitle, aniListId, malId);
}
if (existingByTitle.size() > 1) {
log.warn(
"Multiple mangas with canonical title '{}', merging into first (ID: {})",
canonicalTitle,
existingByTitle.get(0).getId());
return mergeExternalIds(existingByTitle.get(0), canonicalTitle, aniListId, malId);
} }
} }
@ -148,7 +160,8 @@ public class MangaResolutionService {
try { try {
mangaUpdateProducer.sendMangaUpdateCommand(new MangaUpdateCommand(manga.getId())); mangaUpdateProducer.sendMangaUpdateCommand(new MangaUpdateCommand(manga.getId()));
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to send manga update command for manga '{}' (ID: {})", title, manga.getId(), e); log.error(
"Failed to send manga update command for manga '{}' (ID: {})", title, manga.getId(), e);
} }
return manga; return manga;
@ -172,9 +185,16 @@ public class MangaResolutionService {
} }
} }
if (nonNull(title)) { if (nonNull(title)) {
var existing = mangaRepository.findByTitleIgnoreCase(title); var existingByTitle = mangaRepository.findAllByTitleIgnoreCase(title);
if (existing.isPresent()) { if (existingByTitle.size() == 1) {
return mergeExternalIds(existing.get(), title, aniListId, malId); return mergeExternalIds(existingByTitle.get(0), title, aniListId, malId);
}
if (existingByTitle.size() > 1) {
log.warn(
"Multiple mangas with title '{}', merging into first (ID: {})",
title,
existingByTitle.get(0).getId());
return mergeExternalIds(existingByTitle.get(0), title, aniListId, malId);
} }
} }
@ -182,7 +202,8 @@ public class MangaResolutionService {
} }
} }
private Manga mergeExternalIds(Manga existing, String canonicalTitle, Long aniListId, Long malId) { private Manga mergeExternalIds(
Manga existing, String canonicalTitle, Long aniListId, Long malId) {
boolean updated = false; boolean updated = false;
if (isNull(existing.getAniListId()) && nonNull(aniListId)) { if (isNull(existing.getAniListId()) && nonNull(aniListId)) {