multimetric/
multimetric.rs

1use matchete::{Matcher, Composite, Strategy, Custom, Similarity};
2
3// Reuse metric implementations
4struct LevenshteinMetric;
5struct JaccardMetric;
6
7impl Similarity<String, String> for LevenshteinMetric {
8    fn score(&self, query: &String, candidate: &String) -> f64 {
9        let distance = levenshtein_distance(query, candidate);
10        let max_len = query.len().max(candidate.len());
11        if max_len == 0 { 1.0 } else { 1.0 - (distance as f64 / max_len as f64) }
12    }
13
14    fn exact(&self, query: &String, candidate: &String) -> bool {
15        query == candidate
16    }
17}
18
19impl Similarity<String, String> for JaccardMetric {
20    fn score(&self, query: &String, candidate: &String) -> f64 {
21        let query_chars: std::collections::HashSet<char> = query.chars().collect();
22        let candidate_chars: std::collections::HashSet<char> = candidate.chars().collect();
23
24        let intersection = query_chars.intersection(&candidate_chars).count();
25        let union = query_chars.union(&candidate_chars).count();
26
27        if union == 0 { 1.0 } else { intersection as f64 / union as f64 }
28    }
29}
30
31fn levenshtein_distance(a: &str, b: &str) -> usize {
32    // Same implementation as before
33    let len_a = a.len();
34    let len_b = b.len();
35
36    if len_a == 0 { return len_b; }
37    if len_b == 0 { return len_a; }
38
39    let mut matrix = vec![vec![0; len_b + 1]; len_a + 1];
40
41    for i in 0..=len_a { matrix[i][0] = i; }
42    for j in 0..=len_b { matrix[0][j] = j; }
43
44    for i in 1..=len_a {
45        for j in 1..=len_b {
46            let cost = if a.chars().nth(i - 1) == b.chars().nth(j - 1) { 0 } else { 1 };
47            matrix[i][j] = (matrix[i - 1][j] + 1)
48                .min(matrix[i][j - 1] + 1)
49                .min(matrix[i - 1][j - 1] + cost);
50        }
51    }
52
53    matrix[len_a][len_b]
54}
55
56fn main() {
57    // Create a composite metric with weighted strategy
58    let composite_metric = Composite::<String, String>::new(Strategy::Weighted(vec![0.6, 0.4]))
59        .add(LevenshteinMetric)
60        .add(JaccardMetric);
61
62    // Create a matcher with the composite metric
63    let matcher = Matcher::<String, String>::new()
64        .add(composite_metric, 1.0)
65        .threshold(0.5);
66
67    // Define the query and candidate strings
68    let query = String::from("hello world");
69    let candidates = vec![
70        String::from("hello"),
71        String::from("hello there"),
72        String::from("world"),
73    ];
74
75    // Find matches with a limit of 2
76    println!("Multiple Metrics Example");
77    println!("=======================");
78    let matches = matcher.find_limit(&query, &candidates, 2);
79    println!("Matches found: {}", matches.len());
80    for (i, m) in matches.iter().enumerate() {
81        println!(
82            "Match {}:\n  Score: {:.2}\n  Candidate: {}\n  Exact: {}",
83            i + 1, m.score, m.candidate, m.exact
84        );
85    }
86}