Feature/unified nl search #106
+21
-7
@@ -364,13 +364,27 @@ pub async fn unified_search<TagD: TagDao>(
|
|||||||
// ── 4. Rank ──
|
// ── 4. Rank ──
|
||||||
match semantic {
|
match semantic {
|
||||||
Some(ref sem) => {
|
Some(ref sem) => {
|
||||||
// Semantic term present: CLIP-rank, then keep only hits that pass
|
// When structured filters are present they ARE the constraint —
|
||||||
// the structured filters (by content_hash).
|
// CLIP only ranks within the candidate set. So drop the global
|
||||||
let scored =
|
// similarity threshold (it's tuned for whole-library search and
|
||||||
match score_photos(&state, &exif_dao, sem, &library_ids, threshold, None).await {
|
// would pre-discard filter-matching photos that scored just under
|
||||||
Ok(s) => s,
|
// it — e.g. a 2022 beach photo at 0.18 — before the intersection
|
||||||
Err(e) => return score_error_response(e),
|
// ever runs). With no filters, keep the user's threshold for the
|
||||||
};
|
// plain semantic case.
|
||||||
|
let clip_threshold = if has_struct { -1.0 } else { threshold };
|
||||||
|
let scored = match score_photos(
|
||||||
|
&state,
|
||||||
|
&exif_dao,
|
||||||
|
sem,
|
||||||
|
&library_ids,
|
||||||
|
clip_threshold,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return score_error_response(e),
|
||||||
|
};
|
||||||
let considered = scored.considered;
|
let considered = scored.considered;
|
||||||
let clip_hits = scored.hits.len();
|
let clip_hits = scored.hits.len();
|
||||||
let hits: Vec<(f32, String)> = if has_struct {
|
let hits: Vec<(f32, String)> = if has_struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user