Repository: meilisearch/meilisearch
File: milli/src/search/matches/matching_words.rs (Rust)

Code before:
#[derive(Default)]
pub struct MatchingWords {
    inner: Vec<(Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)>,
}

impl fmt::Debug for MatchingWords {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "[")?;
        for (matching_words, primitive_word_id) in self.inner.iter() {
            writeln!(f, "({matching_words:?}, {primitive_word_id:?})")?;
        }
        writeln!(f, "]")?;
        Ok(())
    }
}

impl MatchingWords {
    pub fn new(matching_words: Vec<(Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)>) -> Self {
        // keep only the matching words that contains at least 1 word to match.
        let mut matching_words: Vec<_> = matching_words
            .into_iter()
            .filter(|(mw, ids)| !mw.is_empty() && !ids.is_empty())
            .collect();

        // Sort word by len in DESC order prioritizing the longuest matches,
        // in order to highlight the longuest part of the matched word.
        matching_words.sort_unstable_by_key(|(mw, _)| Reverse((mw.len(), mw[0].word.len())));

        Self { inner: matching_words }
    }

    /// Returns an iterator over terms that match or partially match the given token.
    pub fn match_token<'a, 'b>(&'a self, token: &'b Token<'b>) -> MatchesIter<'a, 'b> {
        MatchesIter { inner: Box::new(self.inner.iter()), token }
    }
}

/// Iterator over terms that match the given token,
/// This allow to lazily evaluate matches.
pub struct MatchesIter<'a, 'b> {
    #[allow(clippy::type_complexity)]
    inner: Box<dyn Iterator<Item = &'a (Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)> + 'a>,
    token: &'b Token<'b>,
}

impl<'a> Iterator for MatchesIter<'a, '_> {
    type Item = MatchType<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.inner.next() {
            Some((matching_words, ids)) => match matching_words[0].match_token(self.token) {

Review comment (suggestion):
```suggestion
    pub fn new(mut matching_words: Vec<(Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)>) -> Self {
        // keep only the matching words that contain at least 1 word to match.
        matching_words.retain(|(mw, ids)| !mw.is_empty() && !ids.is_empty());

```

Code after:
/// referencing words that match the given query tree.
#[derive(Default)]
pub struct MatchingWords {
    inner: Vec<(Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)>,
}

impl fmt::Debug for MatchingWords {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "[")?;
        for (matching_words, primitive_word_id) in self.inner.iter() {
            writeln!(f, "({matching_words:?}, {primitive_word_id:?})")?;
        }
        writeln!(f, "]")?;
        Ok(())
    }
}

impl MatchingWords {
    pub fn new(
        mut matching_words: Vec<(Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)>,
    ) -> crate::Result<Self> {
        // if one of the matching_words vec doesn't contain a word.
        if matching_words.iter().any(|(mw, _)| mw.is_empty()) {
            return Err(InternalError::InvalidMatchingWords.into());
        }

        // Sort word by len in DESC order prioritizing the longuest matches,
        // in order to highlight the longuest part of the matched word.
        matching_words.sort_unstable_by_key(|(mw, _)| Reverse((mw.len(), mw[0].word.len())));

        Ok(Self { inner: matching_words })
    }

    /// Returns an iterator over terms that match or partially match the given token.
    pub fn match_token<'a, 'b>(&'a self, token: &'b Token<'b>) -> MatchesIter<'a, 'b> {
        MatchesIter { inner: Box::new(self.inner.iter()), token }
    }
}

/// Iterator over terms that match the given token,
/// This allow to lazily evaluate matches.
pub struct MatchesIter<'a, 'b> {
    #[allow(clippy::type_complexity)]
    inner: Box<dyn Iterator<Item = &'a (Vec<Rc<MatchingWord>>, Vec<PrimitiveWordId>)> + 'a>,
    token: &'b Token<'b>,
}

impl<'a> Iterator for MatchesIter<'a, '_> {
    type Item = MatchType<'a>;

    fn next(&mut self) -> Option<Self::Item> {