From 5f5a4868953e94f885654a6d2b8bfbd8afae3da0 Mon Sep 17 00:00:00 2001 From: ManyTheFish Date: Thu, 11 Jan 2024 11:36:41 +0100 Subject: [PATCH] Reduce formatting time --- meilisearch/src/search.rs | 23 +++++++++++++++++++---- milli/src/search/new/matches/mod.rs | 6 +++++- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/meilisearch/src/search.rs b/meilisearch/src/search.rs index b5dba8a58..7e783aa42 100644 --- a/meilisearch/src/search.rs +++ b/meilisearch/src/search.rs @@ -897,6 +897,14 @@ fn format_fields<'a>( let mut matches_position = compute_matches.then(BTreeMap::new); let mut document = document.clone(); + // reduce the formated option list to the attributes that should be formatted, + // instead of all the attribute to display. + let formating_fields_options: Vec<_> = formatted_options + .iter() + .filter(|(_, option)| option.should_format()) + .map(|(fid, option)| (field_ids_map.name(*fid).unwrap(), option)) + .collect(); + // select the attributes to retrieve let displayable_names = displayable_ids.iter().map(|&fid| field_ids_map.name(fid).expect("Missing field name")); @@ -905,13 +913,15 @@ fn format_fields<'a>( // to the value and merge them together. eg. If a user said he wanted to highlight `doggo` // and crop `doggo.name`. `doggo.name` needs to be highlighted + cropped while `doggo.age` is only // highlighted. - let format = formatted_options + // Warn: The time to compute the `format` list scales with the number of field to format; + // cummulated with `map_leaf_values` that iterates over all the nested fields, it gives a quadratic complexity: + // `d*f` where `d` is the total number of field to display and `f` the total number of field to format. + let format = formating_fields_options .iter() - .filter(|(field, _option)| { - let name = field_ids_map.name(**field).unwrap(); + .filter(|(name, _option)| { milli::is_faceted_by(name, key) || milli::is_faceted_by(key, name) }) - .map(|(_, option)| *option) + .map(|(_, option)| **option) .reduce(|acc, option| acc.merge(option)); let mut infos = Vec::new(); @@ -941,6 +951,11 @@ fn format_value<'a>( infos: &mut Vec, compute_matches: bool, ) -> Value { + // early skip recursive function if nothing needs to be changed. + if !format_options.as_ref().map_or(false, FormatOptions::should_format) && !compute_matches { + return value; + } + match value { Value::String(old_string) => { let mut matcher = builder.build(&old_string); diff --git a/milli/src/search/new/matches/mod.rs b/milli/src/search/new/matches/mod.rs index 067fa1efd..8de1d9262 100644 --- a/milli/src/search/new/matches/mod.rs +++ b/milli/src/search/new/matches/mod.rs @@ -72,7 +72,7 @@ impl<'m> MatcherBuilder<'m> { } } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone, Default, Debug)] pub struct FormatOptions { pub highlight: bool, pub crop: Option, @@ -82,6 +82,10 @@ impl FormatOptions { pub fn merge(self, other: Self) -> Self { Self { highlight: self.highlight || other.highlight, crop: self.crop.or(other.crop) } } + + pub fn should_format(&self) -> bool { + self.highlight || self.crop.is_some() + } } #[derive(Clone, Debug)]