///|
fn index_match_order(left : IndexMatch, right : IndexMatch) -> Int {
if left.evidence.score > right.evidence.score {
-1
} else if left.evidence.score < right.evidence.score {
1
} else if left.normalized_name < right.normalized_name {
-1
} else if left.normalized_name > right.normalized_name {
1
} else if left.record.id < right.record.id {
-1
} else if left.record.id > right.record.id {
1
} else {
0
}
}
///|
fn collect_index_candidates(
index : NameIndex,
bucket_keys : Array[String],
) -> Array[String] {
let candidates : Array[String] = []
for key in bucket_keys {
match index.buckets.get(key) {
None => ()
Some(ids) =>
for id in ids {
if !candidates.contains(id) {
candidates.push(id)
}
}
}
}
candidates
}
///|
/// Retrieves blocking candidates, scores each once, and returns stable top-K results.
pub fn NameIndex::query(
self : NameIndex,
name : String,
limit : Int,
minimum_score : Double,
) -> Result[IndexQueryResult, IndexError] {
if limit < 1 {
return Err(InvalidQueryLimit(limit))
}
if minimum_score.is_nan() ||
minimum_score.is_inf() ||
minimum_score < 0.0 ||
minimum_score > 1.0 {
return Err(InvalidMinimumScore(minimum_score))
}
let (normalized_query, bucket_keys) = match
prepare_index_name(name, self.config, self.blocking_algorithms) {
Err(error) => return Err(error)
Ok(value) => value
}
let candidate_ids = collect_index_candidates(self, bucket_keys)
let matches : Array[IndexMatch] = []
for id in candidate_ids {
let record = match self.records.get(id) {
None => continue
Some(value) => value
}
let normalized_name = match self.normalized.get(id) {
None => continue
Some(value) => value
}
let evidence = match match_names(name, record.name, self.config) {
Err(error) => return Err(IndexMatchFailed(error))
Ok(value) => value
}
if evidence.score >= minimum_score {
matches.push({ record, normalized_name, evidence })
}
}
matches.sort_by(index_match_order)
if matches.length() > limit {
matches.truncate(limit)
}
Ok({
query: name,
normalized_query,
matches,
summary: {
candidate_count: candidate_ids.length(),
scored_count: candidate_ids.length(),
returned_count: matches.length(),
},
})
}