// One ranking policy for everything that scores: sort by score descending, ties in the order they
// arrived, then cut to the limit. Every store and retriever funnels through here so that two backends
// cannot disagree about what "the top three" means.
///|
/// `hits` sorted by score descending — equal scores keep their arrival order — cut to `limit`.
///
/// The sort is stable on purpose: with a small corpus, several documents tie at cosine `1.0`, and a
/// caller re-running a search expects the same three each time.
fn rank(hits : Array[Scored], limit : Int32) -> Array[Scored] {
if limit <= 0 {
return []
}
let order : Array[(Double, Int32, Scored)] = []
for i, hit in hits {
order.push((-hit.score, i, hit))
}
order.sort_by(fn(left, right) {
let by_score = left.0.compare(right.0)
if by_score != 0 {
by_score
} else {
left.1.compare(right.1)
}
})
let out : Array[Scored] = []
for entry in order {
if out.length() >= limit {
break
}
out.push(entry.2)
}
out
}