Boosts candidates whose file contains multiple query terms. For each candidate, counts how many of the query terms appear (case- insensitive) in the candidate's `name`, `qualified_name`, or `file_path`. Candidates matching 2+ terms get a multiplicative boost.
(candidates: &mut [SearchResult], query_terms: &[String])
| 858 | /// insensitive) in the candidate's `name`, `qualified_name`, or `file_path`. |
| 859 | /// Candidates matching 2+ terms get a multiplicative boost. |
| 860 | fn apply_cooccurrence_boost(candidates: &mut [SearchResult], query_terms: &[String]) { |
| 861 | for candidate in candidates.iter_mut() { |
| 862 | let haystack = format!( |
| 863 | "{} {} {}", |
| 864 | candidate.node.name.to_lowercase(), |
| 865 | candidate.node.qualified_name.to_lowercase(), |
| 866 | candidate.node.file_path.to_lowercase(), |
| 867 | ); |
| 868 | let hits: usize = query_terms |
| 869 | .iter() |
| 870 | .filter(|term| haystack.contains(term.as_str())) |
| 871 | .count(); |
| 872 | if hits >= 2 { |
| 873 | // Boost proportional to coverage: 2 terms → 1.3×, 3 → 1.6×, etc. |
| 874 | candidate.score *= 1.0 + (hits as f64 - 1.0) * 0.3; |
| 875 | } |
| 876 | } |
| 877 | candidates.sort_by(|a, b| { |
| 878 | b.score |
| 879 | .partial_cmp(&a.score) |
| 880 | .unwrap_or(std::cmp::Ordering::Equal) |
| 881 | }); |
| 882 | } |
| 883 | |
| 884 | /// Applies a per-file cap to search results, keeping the top `max_total` |
| 885 | /// results but allowing at most `max_per_file` from any single file. |