/
githubmirror
/
meilisearch
Обзор
Документация
Войти
/
githubmirror
/
meilisearch
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
crates/milli/src/attribute_patterns.rs
215 строк
8 KB
Kerollmops
Chnage the way we match patterns by doing an intersection
16 июл 2026, 12:32
16 июл 2026, 12:32
8197bab
Код
Авторство
О чём код?
use std::fmt; use deserr::Deserr; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use crate::is_faceted_by; /// A collection of patterns used to match attribute names. Patterns can /// include wildcards (`*`) for flexible matching. For example, `title` /// matches exactly, `overview_*` matches any attribute starting with /// `overview_`, and `*_date` matches any attribute ending with `_date`. #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)] #[repr(transparent)] #[serde(transparent)] pub struct AttributePatterns { /// An array of attribute name patterns. Each pattern can be an exact /// attribute name, or include wildcards (`*`) at the start, end, or /// both. Examples: `["title", "description_*", "*_date", "*content*"]`. #[schema(example = json!(["title", "overview_*", "release_date"]))] pub patterns: Vec<String>, } // manual impl: transparent + manual deserr impl impl routes::RequestBody for AttributePatterns {} impl<E: deserr::DeserializeError> Deserr<E> for AttributePatterns { fn deserialize_from_value<V: deserr::IntoValue>( value: deserr::Value<V>, location: deserr::ValuePointerRef, ) -> Result<Self, E> { Vec::<String>::deserialize_from_value(value, location).map(|patterns| Self { patterns }) } } impl From<Vec<String>> for AttributePatterns { fn from(patterns: Vec<String>) -> Self { Self { patterns } } } impl AttributePatterns { /// Match a string against the attribute patterns using the /// match_pattern function. pub fn match_str(&self, str: &str) -> PatternMatch { let mut pattern_match = PatternMatch::NoMatch; for pattern in &self.patterns { match match_pattern(pattern, str) { PatternMatch::Match => return PatternMatch::Match, PatternMatch::Parent => pattern_match = PatternMatch::Parent, PatternMatch::NoMatch => (), } } pattern_match } pub fn intersect_patterns(&self, other: &str) -> bool { for pattern in &self.patterns { match match_pattern(other, pattern) { PatternMatch::Match | PatternMatch::Parent => return true, PatternMatch::NoMatch => match match_pattern(pattern, other) { PatternMatch::Match | PatternMatch::Parent => return true, PatternMatch::NoMatch => (), }, } } false } pub fn is_empty(&self) -> bool { self.patterns.is_empty() } } impl fmt::Debug for AttributePatterns { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.patterns.iter()).finish() } } /// Match a string against a pattern. /// /// The pattern can be a wildcard, a prefix, a suffix or an exact match. /// /// # Arguments /// /// * `pattern` - The pattern to match against. /// * `str` - The string to match against the pattern. pub fn match_pattern(pattern: &str, str: &str) -> PatternMatch { // If the pattern is a wildcard, return Match if pattern == "*" { return PatternMatch::Match; } else if pattern.starts_with('*') && pattern.ends_with('*') { // If the pattern starts and ends with a wildcard, return Match if the string contains the pattern without the wildcards if str.contains(&pattern[1..pattern.len() - 1]) { return PatternMatch::Match; } } else if let Some(pattern) = pattern.strip_prefix('*') { // If the pattern starts with a wildcard, return Match if the string ends with the pattern without the wildcard if str.ends_with(pattern) { return PatternMatch::Match; } } else if let Some(pattern) = pattern.strip_suffix('*') { // If the pattern ends with a wildcard, return Match if the string starts with the pattern without the wildcard if str.starts_with(pattern) { return PatternMatch::Match; } } else if pattern == str { // If the pattern is exactly the string, return Match return PatternMatch::Match; } // If the field is a parent field of the pattern, return Parent if is_faceted_by(pattern, str) { PatternMatch::Parent } else { PatternMatch::NoMatch } } /// Match a field against a pattern using the legacy behavior. /// /// A field matches a pattern if it is a parent of the pattern or if it is /// the pattern itself. This behavior is used to match the sortable /// attributes, the searchable attributes and the filterable attributes /// rules `Field`. /// /// # Arguments /// /// * `pattern` - The pattern to match against. /// * `field` - The field to match against the pattern. pub fn match_field_legacy(pattern: &str, field: &str) -> PatternMatch { if is_faceted_by(field, pattern) { // If the field matches the pattern or is a nested field of the pattern, return Match (legacy behavior) PatternMatch::Match } else if is_faceted_by(pattern, field) { // If the field is a parent field of the pattern, return Parent PatternMatch::Parent } else { // If the field does not match the pattern and is not a parent of a nested field that matches the pattern, return NoMatch PatternMatch::NoMatch } } /// Match a field against a set of patterns using the legacy behavior. /// /// A field matches a set of patterns if it is a parent of one of the patterns or if it matches one of the patterns. /// /// # Arguments /// /// * `patterns` - The set of patterns to match against. /// * `field` - The field to match against the patterns. pub fn field_match_any_patterns_legacy<I, P>(patterns: I, field: &str) -> PatternMatch where I: IntoIterator<Item = P>, P: AsRef<str>, { let mut selection = PatternMatch::NoMatch; for pattern in patterns { match match_field_legacy(pattern.as_ref(), field) { PatternMatch::Match => return PatternMatch::Match, PatternMatch::Parent => selection = PatternMatch::Parent, PatternMatch::NoMatch => (), } } selection } /// Match a field against a distinct field. pub fn match_distinct_field(distinct_field: Option<&str>, field: &str) -> PatternMatch { if let Some(distinct_field) = distinct_field { if field == distinct_field { // If the field matches exactly the distinct field, return Match return PatternMatch::Match; } else if is_faceted_by(distinct_field, field) { // If the field is a parent field of the distinct field, return Parent return PatternMatch::Parent; } } // If the field does not match the distinct field and is not a parent of a nested field that matches the distinct field, return NoMatch PatternMatch::NoMatch } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PatternMatch { /// The field is a parent of a nested field that matches the pattern /// For example, the field is `toto`, and the pattern is `toto.titi` Parent, /// The field matches the pattern Match, /// The field does not match the pattern NoMatch, } #[cfg(test)] mod tests { use super::*; #[test] fn test_match_pattern() { assert_eq!(match_pattern("*", "test"), PatternMatch::Match); assert_eq!(match_pattern("test*", "test"), PatternMatch::Match); assert_eq!(match_pattern("test*", "testa"), PatternMatch::Match); assert_eq!(match_pattern("*test", "test"), PatternMatch::Match); assert_eq!(match_pattern("*test", "atest"), PatternMatch::Match); assert_eq!(match_pattern("*test*", "test"), PatternMatch::Match); assert_eq!(match_pattern("*test*", "atesta"), PatternMatch::Match); assert_eq!(match_pattern("*test*", "atest"), PatternMatch::Match); assert_eq!(match_pattern("*test*", "testa"), PatternMatch::Match); assert_eq!(match_pattern("test*test", "test"), PatternMatch::NoMatch); assert_eq!(match_pattern("*test", "testa"), PatternMatch::NoMatch); assert_eq!(match_pattern("test*", "atest"), PatternMatch::NoMatch); } }