feat: Optimize image hash checks with bulk queries and add database indexing for performance

This commit is contained in:
Ross
2026-05-11 12:07:55 +01:00
parent e55a3bf36a
commit 2b9f3e3b68
6 changed files with 85 additions and 28 deletions
+41 -25
View File
@@ -583,41 +583,57 @@ def check_image_hash(request, hash: str):
@router.post("/check_image_hashes/", auth=TokenAuth())
def check_images_hashes(request, hashes: List[str]):
"""Checks a list of image hashes and returns the series id / url if found
"""Checks a list of image hashes and returns the series id / url if found.
Uses two bulk queries (one for SeriesImage, one for UncategorisedDicom) to
handle large batches efficiently regardless of batch size.
Return format
{ "hash_id": {"id": "series_id|false", "url": "series_url|false"}, ...}
"""
try:
hash_status = {}
hash_status: dict = {h: {"id": False, "url": False} for h in hashes}
for hash in hashes:
# Prefer filter().first() to avoid MultipleObjectsReturned
series_image = SeriesImage.objects.filter(image_blake3_hash=hash).select_related("series").first()
if series_image:
try:
url = series_image.series.get_absolute_url()
except Exception:
url = None
data = {"id": series_image.pk, "url": url, "type": "series"}
hash_status[hash] = data
continue
# Single bulk query for all SeriesImage matches
series_images = (
SeriesImage.objects.filter(image_blake3_hash__in=hashes)
.select_related("series")
.only("pk", "image_blake3_hash", "series__slug", "series__pk")
)
matched_hashes: set = set()
for si in series_images:
if si.image_blake3_hash in matched_hashes:
continue # keep first match per hash
try:
url = si.series.get_absolute_url()
except Exception:
url = None
hash_status[si.image_blake3_hash] = {"id": si.pk, "url": url, "type": "series"}
matched_hashes.add(si.image_blake3_hash)
uncategorised = UncategorisedDicom.objects.filter(image_blake3_hash=hash).first()
if uncategorised:
try:
uploads_url = reverse("atlas:user_uploads")
except Exception:
uploads_url = None
data = {"id": uncategorised.pk, "url": uploads_url, "type": "uncategorised"}
hash_status[hash] = data
continue
hash_status[hash] = {"id": False, "url": False}
# Single bulk query for remaining hashes in UncategorisedDicom
remaining = [h for h in hashes if h not in matched_hashes]
if remaining:
try:
uploads_url = reverse("atlas:user_uploads")
except Exception:
uploads_url = None
uncategorised_qs = (
UncategorisedDicom.objects.filter(image_blake3_hash__in=remaining)
.only("pk", "image_blake3_hash")
)
for uc in uncategorised_qs:
if uc.image_blake3_hash not in matched_hashes:
hash_status[uc.image_blake3_hash] = {
"id": uc.pk,
"url": uploads_url,
"type": "uncategorised",
}
matched_hashes.add(uc.image_blake3_hash)
return hash_status
except Exception as e:
except Exception:
logger.exception("check_images_hashes failed")
return JsonResponse({"detail": "Internal server error"}, status=500)