Merge branch 'main' into release-v1.14.0-tmp

This commit is contained in:
Tamo 2025-04-14 12:35:47 +02:00 committed by GitHub
commit b025f1bcf1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
113 changed files with 1268 additions and 852 deletions

View file

@ -271,7 +271,7 @@ fn fetch_matching_values_in_object(
}
fn starts_with(selector: &str, key: &str) -> bool {
selector.strip_prefix(key).map_or(false, |tail| {
selector.strip_prefix(key).is_some_and(|tail| {
tail.chars().next().map(|c| c == PRIMARY_KEY_SPLIT_SYMBOL).unwrap_or(true)
})
}

View file

@ -27,7 +27,7 @@ impl<'a, W> DocumentVisitor<'a, W> {
}
}
impl<'a, 'de, W: Write> Visitor<'de> for &mut DocumentVisitor<'a, W> {
impl<'de, W: Write> Visitor<'de> for &mut DocumentVisitor<'_, W> {
/// This Visitor value is nothing, since it write the value to a file.
type Value = Result<(), Error>;
@ -61,7 +61,7 @@ impl<'a, 'de, W: Write> Visitor<'de> for &mut DocumentVisitor<'a, W> {
}
}
impl<'a, 'de, W> DeserializeSeed<'de> for &mut DocumentVisitor<'a, W>
impl<'de, W> DeserializeSeed<'de> for &mut DocumentVisitor<'_, W>
where
W: Write,
{

View file

@ -1,4 +1,5 @@
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::convert::Infallible;
use std::fmt::Write;
use std::{io, str};
@ -120,13 +121,37 @@ only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and undersco
and can not be more than 511 bytes.", .document_id.to_string()
)]
InvalidDocumentId { document_id: Value },
#[error("Invalid facet distribution, {}", format_invalid_filter_distribution(.invalid_facets_name, .valid_patterns))]
#[error("Invalid facet distribution: {}",
if .invalid_facets_name.len() == 1 {
let field = .invalid_facets_name.iter().next().unwrap();
match .matching_rule_indices.get(field) {
Some(rule_index) => format!("Attribute `{}` matched rule #{} in filterableAttributes, but this rule does not enable filtering.\nHint: enable filtering in rule #{} by modifying the features.filter object\nHint: prepend another rule matching `{}` with appropriate filter features before rule #{}",
field, rule_index, rule_index, field, rule_index),
None => match .valid_patterns.is_empty() {
true => format!("Attribute `{}` is not filterable. This index does not have configured filterable attributes.", field),
false => format!("Attribute `{}` is not filterable. Available filterable attributes patterns are: `{}`.",
field,
.valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")),
}
}
} else {
format!("Attributes `{}` are not filterable. {}",
.invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", "),
match .valid_patterns.is_empty() {
true => "This index does not have configured filterable attributes.".to_string(),
false => format!("Available filterable attributes patterns are: `{}`.",
.valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")),
}
)
}
)]
InvalidFacetsDistribution {
invalid_facets_name: BTreeSet<String>,
valid_patterns: BTreeSet<String>,
matching_rule_indices: HashMap<String, usize>,
},
#[error(transparent)]
InvalidGeoField(#[from] GeoError),
InvalidGeoField(#[from] Box<GeoError>),
#[error("Invalid vector dimensions: expected: `{}`, found: `{}`.", .expected, .found)]
InvalidVectorDimensions { expected: usize, found: usize },
#[error("Invalid vector dimensions in document with id `{document_id}` in `._vectors.{embedder_name}`.\n - note: embedding #{embedding_index} has dimensions {found}\n - note: embedder `{embedder_name}` requires {expected}")]
@ -145,7 +170,12 @@ and can not be more than 511 bytes.", .document_id.to_string()
InvalidFilter(String),
#[error("Invalid type for filter subexpression: expected: {}, found: {}.", .0.join(", "), .1)]
InvalidFilterExpression(&'static [&'static str], Value),
#[error("Filter operator `{operator}` is not allowed for the attribute `{field}`.\n - Note: allowed operators: {}.\n - Note: field `{field}` {} in `filterableAttributes`", allowed_operators.join(", "), format!("matched rule #{rule_index}"))]
#[error("Filter operator `{operator}` is not allowed for the attribute `{field}`.\n - Note: allowed operators: {}.\n - Note: field `{field}` matched rule #{rule_index} in `filterableAttributes`\n - Hint: enable {} in rule #{rule_index} by modifying the features.filter object\n - Hint: prepend another rule matching `{field}` with appropriate filter features before rule #{rule_index}",
allowed_operators.join(", "),
if operator == "=" || operator == "!=" || operator == "IN" {"equality"}
else if operator == "<" || operator == ">" || operator == "<=" || operator == ">=" || operator == "TO" {"comparison"}
else {"the appropriate filter operators"}
)]
FilterOperatorNotAllowed {
field: String,
allowed_operators: Vec<String>,
@ -165,33 +195,51 @@ and can not be more than 511 bytes.", .document_id.to_string()
InvalidSortableAttribute { field: String, valid_fields: BTreeSet<String>, hidden_fields: bool },
#[error("Attribute `{}` is not filterable and thus, cannot be used as distinct attribute. {}",
.field,
match .valid_patterns.is_empty() {
true => "This index does not have configured filterable attributes.".to_string(),
false => format!("Available filterable attributes patterns are: `{}{}`.",
match (.valid_patterns.is_empty(), .matching_rule_index) {
// No rules match and no filterable attributes
(true, None) => "This index does not have configured filterable attributes.".to_string(),
// No rules match but there are some filterable attributes
(false, None) => format!("Available filterable attributes patterns are: `{}{}`.",
valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", "),
.hidden_fields.then_some(", <..hidden-attributes>").unwrap_or(""),
),
// A rule matched but filtering isn't enabled
(_, Some(rule_index)) => format!("Note: this attribute matches rule #{} in filterableAttributes, but this rule does not enable filtering.\nHint: enable filtering in rule #{} by adding appropriate filter features.\nHint: prepend another rule matching {} with filter features before rule #{}",
rule_index, rule_index, .field, rule_index
),
}
)]
InvalidDistinctAttribute {
field: String,
valid_patterns: BTreeSet<String>,
hidden_fields: bool,
matching_rule_index: Option<usize>,
},
#[error("Attribute `{}` is not facet-searchable. {}",
.field,
match .valid_patterns.is_empty() {
true => "This index does not have configured facet-searchable attributes. To make it facet-searchable add it to the `filterableAttributes` index settings.".to_string(),
false => format!("Available facet-searchable attributes patterns are: `{}{}`. To make it facet-searchable add it to the `filterableAttributes` index settings.",
match (.valid_patterns.is_empty(), .matching_rule_index) {
// No rules match and no facet searchable attributes
(true, None) => "This index does not have configured facet-searchable attributes. To make it facet-searchable add it to the `filterableAttributes` index settings.".to_string(),
// No rules match but there are some facet searchable attributes
(false, None) => format!("Available facet-searchable attributes patterns are: `{}{}`. To make it facet-searchable add it to the `filterableAttributes` index settings.",
valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", "),
.hidden_fields.then_some(", <..hidden-attributes>").unwrap_or(""),
),
// A rule matched but facet search isn't enabled
(_, Some(rule_index)) => format!("Note: this attribute matches rule #{} in filterableAttributes, but this rule does not enable facetSearch.\nHint: enable facetSearch in rule #{} by adding `\"facetSearch\": true` to the rule.\nHint: prepend another rule matching {} with facetSearch: true before rule #{}",
rule_index, rule_index, .field, rule_index
),
}
)]
InvalidFacetSearchFacetName {
field: String,
valid_patterns: BTreeSet<String>,
hidden_fields: bool,
matching_rule_index: Option<usize>,
},
#[error("Attribute `{}` is not searchable. Available searchable attributes are: `{}{}`.",
.field,
@ -396,45 +444,53 @@ pub enum GeoError {
BadLongitude { document_id: Value, value: Value },
}
#[allow(dead_code)]
fn format_invalid_filter_distribution(
invalid_facets_name: &BTreeSet<String>,
valid_patterns: &BTreeSet<String>,
) -> String {
if valid_patterns.is_empty() {
return "this index does not have configured filterable attributes.".into();
}
let mut result = String::new();
match invalid_facets_name.len() {
0 => (),
1 => write!(
result,
"attribute `{}` is not filterable.",
invalid_facets_name.first().unwrap()
)
.unwrap(),
_ => write!(
result,
"attributes `{}` are not filterable.",
invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
)
.unwrap(),
};
if invalid_facets_name.is_empty() {
if valid_patterns.is_empty() {
return "this index does not have configured filterable attributes.".into();
}
} else {
match invalid_facets_name.len() {
1 => write!(
result,
"Attribute `{}` is not filterable.",
invalid_facets_name.first().unwrap()
)
.unwrap(),
_ => write!(
result,
"Attributes `{}` are not filterable.",
invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
)
.unwrap(),
};
}
match valid_patterns.len() {
1 => write!(
result,
" The available filterable attribute pattern is `{}`.",
valid_patterns.first().unwrap()
)
.unwrap(),
_ => write!(
result,
" The available filterable attribute patterns are `{}`.",
valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
)
.unwrap(),
if valid_patterns.is_empty() {
if !invalid_facets_name.is_empty() {
write!(result, " This index does not have configured filterable attributes.").unwrap();
}
} else {
match valid_patterns.len() {
1 => write!(
result,
" Available filterable attributes patterns are: `{}`.",
valid_patterns.first().unwrap()
)
.unwrap(),
_ => write!(
result,
" Available filterable attributes patterns are: `{}`.",
valid_patterns.iter().map(AsRef::as_ref).collect::<Vec<&str>>().join(", ")
)
.unwrap(),
}
}
result
@ -446,7 +502,7 @@ fn format_invalid_filter_distribution(
/// ```ignore
/// impl From<FieldIdMapMissingEntry> for Error {
/// fn from(error: FieldIdMapMissingEntry) -> Error {
/// Error::from(InternalError::from(error))
/// Error::from(<InternalError>::from(error))
/// }
/// }
/// ```
@ -471,7 +527,7 @@ error_from_sub_error! {
str::Utf8Error => InternalError,
ThreadPoolBuildError => InternalError,
SerializationError => InternalError,
GeoError => UserError,
Box<GeoError> => UserError,
CriterionError => UserError,
}

View file

@ -25,7 +25,7 @@ impl ExternalDocumentsIds {
/// Returns `true` if hard and soft external documents lists are empty.
pub fn is_empty(&self, rtxn: &RoTxn<'_>) -> heed::Result<bool> {
self.0.is_empty(rtxn).map_err(Into::into)
self.0.is_empty(rtxn)
}
pub fn get<A: AsRef<str>>(

View file

@ -119,7 +119,7 @@ impl<'indexing> GlobalFieldsIdsMap<'indexing> {
}
}
impl<'indexing> MutFieldIdMapper for GlobalFieldsIdsMap<'indexing> {
impl MutFieldIdMapper for GlobalFieldsIdsMap<'_> {
fn insert(&mut self, name: &str) -> Option<FieldId> {
self.id_or_insert(name)
}

View file

@ -3039,10 +3039,15 @@ pub(crate) mod tests {
documents!({ "id" : 6, RESERVED_GEO_FIELD_NAME: {"lat": "unparseable", "lng": "unparseable"}}),
)
.unwrap_err();
assert!(matches!(
err1,
Error::UserError(UserError::InvalidGeoField(GeoError::BadLatitudeAndLongitude { .. }))
));
match err1 {
Error::UserError(UserError::InvalidGeoField(err)) => match *err {
GeoError::BadLatitudeAndLongitude { .. } => (),
otherwise => {
panic!("err1 is not a BadLatitudeAndLongitude error but rather a {otherwise:?}")
}
},
_ => panic!("err1 is not a BadLatitudeAndLongitude error but rather a {err1:?}"),
}
db_snap!(index, geo_faceted_documents_ids); // ensure that no more document was inserted
}

View file

@ -204,7 +204,7 @@ pub fn relative_from_absolute_position(absolute: Position) -> (FieldId, Relative
// Compute the absolute word position with the field id of the attribute and relative position in the attribute.
pub fn absolute_from_relative_position(field_id: FieldId, relative: RelativePosition) -> Position {
(field_id as u32) << 16 | (relative as u32)
((field_id as u32) << 16) | (relative as u32)
}
// TODO: this is wrong, but will do for now
/// Compute the "bucketed" absolute position from the field id and relative position in the field.
@ -372,7 +372,7 @@ pub fn is_faceted(field: &str, faceted_fields: impl IntoIterator<Item = impl AsR
/// assert!(!is_faceted_by("animaux.chien", "animaux.chie"));
/// ```
pub fn is_faceted_by(field: &str, facet: &str) -> bool {
field.starts_with(facet) && field[facet.len()..].chars().next().map_or(true, |c| c == '.')
field.starts_with(facet) && field[facet.len()..].chars().next().is_none_or(|c| c == '.')
}
pub fn normalize_facet(original: &str) -> String {

View file

@ -15,7 +15,7 @@ impl<'a, D: ObjectView, F: ArrayView> Context<'a, D, F> {
}
}
impl<'a, D: ObjectView, F: ArrayView> ObjectView for Context<'a, D, F> {
impl<D: ObjectView, F: ArrayView> ObjectView for Context<'_, D, F> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -52,7 +52,7 @@ impl<'a, D: ObjectView, F: ArrayView> ObjectView for Context<'a, D, F> {
}
}
impl<'a, D: ObjectView, F: ArrayView> ValueView for Context<'a, D, F> {
impl<D: ObjectView, F: ArrayView> ValueView for Context<'_, D, F> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}

View file

@ -67,7 +67,7 @@ impl<'a> Document<'a> {
}
}
impl<'a> ObjectView for Document<'a> {
impl ObjectView for Document<'_> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -98,7 +98,7 @@ impl<'a> ObjectView for Document<'a> {
}
}
impl<'a> ValueView for Document<'a> {
impl ValueView for Document<'_> {
fn as_debug(&self) -> &dyn Debug {
self
}
@ -283,7 +283,7 @@ impl<'doc> ParseableArray<'doc> {
}
}
impl<'doc> ArrayView for ParseableArray<'doc> {
impl ArrayView for ParseableArray<'_> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -311,7 +311,7 @@ impl<'doc> ArrayView for ParseableArray<'doc> {
}
}
impl<'doc> ValueView for ParseableArray<'doc> {
impl ValueView for ParseableArray<'_> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}
@ -353,7 +353,7 @@ impl<'doc> ValueView for ParseableArray<'doc> {
}
}
impl<'doc> ObjectView for ParseableMap<'doc> {
impl ObjectView for ParseableMap<'_> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -392,7 +392,7 @@ impl<'doc> ObjectView for ParseableMap<'doc> {
}
}
impl<'doc> ValueView for ParseableMap<'doc> {
impl ValueView for ParseableMap<'_> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}
@ -441,7 +441,7 @@ impl<'doc> ValueView for ParseableMap<'doc> {
}
}
impl<'doc> ValueView for ParseableValue<'doc> {
impl ValueView for ParseableValue<'_> {
fn as_debug(&self) -> &dyn Debug {
self
}
@ -622,7 +622,7 @@ struct ArraySource<'s, 'doc> {
s: &'s RawVec<'doc>,
}
impl<'s, 'doc> fmt::Display for ArraySource<'s, 'doc> {
impl fmt::Display for ArraySource<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
for item in self.s {
@ -638,7 +638,7 @@ struct ArrayRender<'s, 'doc> {
s: &'s RawVec<'doc>,
}
impl<'s, 'doc> fmt::Display for ArrayRender<'s, 'doc> {
impl fmt::Display for ArrayRender<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for item in self.s {
let v = ParseableValue::new(item, self.s.bump());

View file

@ -17,7 +17,7 @@ pub struct FieldValue<'a, D: ObjectView> {
metadata: Metadata,
}
impl<'a, D: ObjectView> ValueView for FieldValue<'a, D> {
impl<D: ObjectView> ValueView for FieldValue<'_, D> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}
@ -78,7 +78,7 @@ impl<'a, D: ObjectView> FieldValue<'a, D> {
}
}
impl<'a, D: ObjectView> ObjectView for FieldValue<'a, D> {
impl<D: ObjectView> ObjectView for FieldValue<'_, D> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -148,7 +148,7 @@ impl<'a, 'map, D: ObjectView> BorrowedFields<'a, 'map, D> {
}
}
impl<'a, D: ObjectView> ArrayView for OwnedFields<'a, D> {
impl<D: ObjectView> ArrayView for OwnedFields<'_, D> {
fn as_value(&self) -> &dyn ValueView {
self.0.as_value()
}
@ -170,7 +170,7 @@ impl<'a, D: ObjectView> ArrayView for OwnedFields<'a, D> {
}
}
impl<'a, 'map, D: ObjectView> ArrayView for BorrowedFields<'a, 'map, D> {
impl<D: ObjectView> ArrayView for BorrowedFields<'_, '_, D> {
fn as_value(&self) -> &dyn ValueView {
self
}
@ -212,7 +212,7 @@ impl<'a, 'map, D: ObjectView> ArrayView for BorrowedFields<'a, 'map, D> {
}
}
impl<'a, 'map, D: ObjectView> ValueView for BorrowedFields<'a, 'map, D> {
impl<D: ObjectView> ValueView for BorrowedFields<'_, '_, D> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}
@ -254,7 +254,7 @@ impl<'a, 'map, D: ObjectView> ValueView for BorrowedFields<'a, 'map, D> {
}
}
impl<'a, D: ObjectView> ValueView for OwnedFields<'a, D> {
impl<D: ObjectView> ValueView for OwnedFields<'_, D> {
fn as_debug(&self) -> &dyn std::fmt::Debug {
self
}
@ -292,7 +292,7 @@ struct ArraySource<'a, 'map, D: ObjectView> {
s: &'a BorrowedFields<'a, 'map, D>,
}
impl<'a, 'map, D: ObjectView> fmt::Display for ArraySource<'a, 'map, D> {
impl<D: ObjectView> fmt::Display for ArraySource<'_, '_, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
for item in self.s.values() {
@ -307,7 +307,7 @@ struct ArrayRender<'a, 'map, D: ObjectView> {
s: &'a BorrowedFields<'a, 'map, D>,
}
impl<'a, 'map, D: ObjectView> fmt::Display for ArrayRender<'a, 'map, D> {
impl<D: ObjectView> fmt::Display for ArrayRender<'_, '_, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for item in self.s.values() {
write!(f, "{}", item.render())?;

View file

@ -358,7 +358,7 @@ impl<'a> FacetDistribution<'a> {
) -> bool {
// If the field is not filterable, we don't want to compute the facet distribution.
if !matching_features(name, filterable_attributes_rules)
.map_or(false, |(_, features)| features.is_filterable())
.is_some_and(|(_, features)| features.is_filterable())
{
return false;
}
@ -378,13 +378,21 @@ impl<'a> FacetDistribution<'a> {
filterable_attributes_rules: &[FilterableAttributesRule],
) -> Result<()> {
let mut invalid_facets = BTreeSet::new();
let mut matching_rule_indices = HashMap::new();
if let Some(facets) = &self.facets {
for field in facets.keys() {
let is_valid_filterable_field =
matching_features(field, filterable_attributes_rules)
.map_or(false, |(_, features)| features.is_filterable());
if !is_valid_filterable_field {
let matched_rule = matching_features(field, filterable_attributes_rules);
let is_filterable = matched_rule.is_some_and(|(_, f)| f.is_filterable());
if !is_filterable {
invalid_facets.insert(field.to_string());
// If the field matched a rule but that rule doesn't enable filtering,
// store the rule index for better error messages
if let Some((rule_index, _)) = matched_rule {
matching_rule_indices.insert(field.to_string(), rule_index);
}
}
}
}
@ -400,6 +408,7 @@ impl<'a> FacetDistribution<'a> {
return Err(Error::UserError(UserError::InvalidFacetsDistribution {
invalid_facets_name: invalid_facets,
valid_patterns,
matching_rule_indices,
}));
}

View file

@ -79,7 +79,7 @@ struct FacetRangeSearch<'t, 'b, 'bitmap> {
docids: &'bitmap mut RoaringBitmap,
}
impl<'t, 'b, 'bitmap> FacetRangeSearch<'t, 'b, 'bitmap> {
impl<'t> FacetRangeSearch<'t, '_, '_> {
fn run_level_0(&mut self, starting_left_bound: &'t [u8], group_size: usize) -> Result<()> {
let left_key =
FacetGroupKey { field_id: self.field_id, level: 0, left_bound: starting_left_bound };

View file

@ -62,7 +62,7 @@ struct AscendingFacetSort<'t, 'e> {
)>,
}
impl<'t, 'e> Iterator for AscendingFacetSort<'t, 'e> {
impl<'t> Iterator for AscendingFacetSort<'t, '_> {
type Item = Result<(RoaringBitmap, &'t [u8])>;
fn next(&mut self) -> Option<Self::Item> {

View file

@ -66,15 +66,15 @@ enum FilterError<'a> {
ParseGeoError(BadGeoError),
TooDeep,
}
impl<'a> std::error::Error for FilterError<'a> {}
impl std::error::Error for FilterError<'_> {}
impl<'a> From<BadGeoError> for FilterError<'a> {
impl From<BadGeoError> for FilterError<'_> {
fn from(geo_error: BadGeoError) -> Self {
FilterError::ParseGeoError(geo_error)
}
}
impl<'a> Display for FilterError<'a> {
impl Display for FilterError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AttributeNotFilterable { attribute, filterable_patterns } => {
@ -237,7 +237,7 @@ impl<'a> Filter<'a> {
for fid in self.condition.fids(MAX_FILTER_DEPTH) {
let attribute = fid.value();
if matching_features(attribute, &filterable_attributes_rules)
.map_or(false, |(_, features)| features.is_filterable())
.is_some_and(|(_, features)| features.is_filterable())
{
continue;
}
@ -461,7 +461,7 @@ impl<'a> Filter<'a> {
filterable_attribute_rules: &[FilterableAttributesRule],
universe: Option<&RoaringBitmap>,
) -> Result<RoaringBitmap> {
if universe.map_or(false, |u| u.is_empty()) {
if universe.is_some_and(|u| u.is_empty()) {
return Ok(RoaringBitmap::new());
}

View file

@ -75,9 +75,11 @@ impl<'a> SearchForFacetValues<'a> {
let rtxn = self.search_query.rtxn;
let filterable_attributes_rules = index.filterable_attributes_rules(rtxn)?;
if !matching_features(&self.facet, &filterable_attributes_rules)
.map_or(false, |(_, features)| features.is_facet_searchable())
{
let matched_rule = matching_features(&self.facet, &filterable_attributes_rules);
let is_facet_searchable =
matched_rule.is_some_and(|(_, features)| features.is_facet_searchable());
if !is_facet_searchable {
let matching_field_names =
filtered_matching_patterns(&filterable_attributes_rules, &|features| {
features.is_facet_searchable()
@ -85,10 +87,14 @@ impl<'a> SearchForFacetValues<'a> {
let (valid_patterns, hidden_fields) =
index.remove_hidden_fields(rtxn, matching_field_names)?;
// Get the matching rule index if any rule matched the attribute
let matching_rule_index = matched_rule.map(|(rule_index, _)| rule_index);
return Err(UserError::InvalidFacetSearchFacetName {
field: self.facet.clone(),
valid_patterns,
hidden_fields,
matching_rule_index,
}
.into());
};
@ -129,7 +135,7 @@ impl<'a> SearchForFacetValues<'a> {
if authorize_typos && field_authorizes_typos {
let exact_words_fst = self.search_query.index.exact_words(rtxn)?;
if exact_words_fst.map_or(false, |fst| fst.contains(query)) {
if exact_words_fst.is_some_and(|fst| fst.contains(query)) {
if fst.contains(query) {
self.fetch_original_facets_using_normalized(
fid,

View file

@ -151,7 +151,7 @@ impl ScoreWithRatioResult {
}
}
impl<'a> Search<'a> {
impl Search<'_> {
#[tracing::instrument(level = "trace", skip_all, target = "search::hybrid")]
pub fn execute_hybrid(&self, semantic_ratio: f32) -> Result<(SearchResult, Option<u32>)> {
// TODO: find classier way to achieve that than to reset vector and query params

View file

@ -190,9 +190,10 @@ impl<'a> Search<'a> {
if let Some(distinct) = &self.distinct {
let filterable_fields = ctx.index.filterable_attributes_rules(ctx.txn)?;
// check if the distinct field is in the filterable fields
if !matching_features(distinct, &filterable_fields)
.map_or(false, |(_, features)| features.is_filterable())
{
let matched_rule = matching_features(distinct, &filterable_fields);
let is_filterable = matched_rule.is_some_and(|(_, features)| features.is_filterable());
if !is_filterable {
// if not, remove the hidden fields from the filterable fields to generate the error message
let matching_patterns =
filtered_matching_patterns(&filterable_fields, &|features| {
@ -200,11 +201,16 @@ impl<'a> Search<'a> {
});
let (valid_patterns, hidden_fields) =
ctx.index.remove_hidden_fields(ctx.txn, matching_patterns)?;
// Get the matching rule index if any rule matched the attribute
let matching_rule_index = matched_rule.map(|(rule_index, _)| rule_index);
// and return the error
return Err(Error::UserError(UserError::InvalidDistinctAttribute {
field: distinct.clone(),
valid_patterns,
hidden_fields,
matching_rule_index,
}));
}
}

View file

@ -537,7 +537,7 @@ impl<'ctx> SearchContext<'ctx> {
fid: u16,
) -> Result<Option<RoaringBitmap>> {
// if the requested fid isn't in the restricted list, return None.
if self.restricted_fids.as_ref().map_or(false, |fids| !fids.contains(&fid)) {
if self.restricted_fids.as_ref().is_some_and(|fids| !fids.contains(&fid)) {
return Ok(None);
}
@ -558,7 +558,7 @@ impl<'ctx> SearchContext<'ctx> {
fid: u16,
) -> Result<Option<RoaringBitmap>> {
// if the requested fid isn't in the restricted list, return None.
if self.restricted_fids.as_ref().map_or(false, |fids| !fids.contains(&fid)) {
if self.restricted_fids.as_ref().is_some_and(|fids| !fids.contains(&fid)) {
return Ok(None);
}

View file

@ -72,7 +72,7 @@ pub fn find_best_match_interval(matches: &[Match], crop_size: usize) -> [&Match;
let interval_score = get_interval_score(&matches[interval_first..=interval_last]);
let is_interval_score_better = &best_interval
.as_ref()
.map_or(true, |MatchIntervalWithScore { score, .. }| interval_score > *score);
.is_none_or(|MatchIntervalWithScore { score, .. }| interval_score > *score);
if *is_interval_score_better {
best_interval = Some(MatchIntervalWithScore {

View file

@ -8,6 +8,7 @@ use std::cmp::{max, min};
use charabia::{Language, SeparatorKind, Token, Tokenizer};
use either::Either;
use itertools::Itertools;
pub use matching_words::MatchingWords;
use matching_words::{MatchType, PartialMatch};
use r#match::{Match, MatchPosition};
@ -122,7 +123,7 @@ pub struct Matcher<'t, 'tokenizer, 'b, 'lang> {
matches: Option<(Vec<Token<'t>>, Vec<Match>)>,
}
impl<'t, 'tokenizer> Matcher<'t, 'tokenizer, '_, '_> {
impl<'t> Matcher<'t, '_, '_, '_> {
/// Iterates over tokens and save any of them that matches the query.
fn compute_matches(&mut self) -> &mut Self {
/// some words are counted as matches only if they are close together and in the good order,
@ -229,8 +230,7 @@ impl<'t, 'tokenizer> Matcher<'t, 'tokenizer, '_, '_> {
.iter()
.map(|m| MatchBounds {
start: tokens[m.get_first_token_pos()].byte_start,
// TODO: Why is this in chars, while start is in bytes?
length: m.char_count,
length: self.calc_byte_length(tokens, m),
indices: if array_indices.is_empty() {
None
} else {
@ -241,6 +241,18 @@ impl<'t, 'tokenizer> Matcher<'t, 'tokenizer, '_, '_> {
}
}
fn calc_byte_length(&self, tokens: &[Token<'t>], m: &Match) -> usize {
(m.get_first_token_pos()..=m.get_last_token_pos())
.flat_map(|i| match &tokens[i].char_map {
Some(char_map) => {
char_map.iter().map(|(original, _)| *original as usize).collect_vec()
}
None => tokens[i].lemma().chars().map(|c| c.len_utf8()).collect_vec(),
})
.take(m.char_count)
.sum()
}
/// Returns the bounds in byte index of the crop window.
fn crop_bounds(&self, tokens: &[Token<'_>], matches: &[Match], crop_size: usize) -> [usize; 2] {
let (

View file

@ -327,7 +327,7 @@ impl QueryGraph {
let mut peekable = term_with_frequency.into_iter().peekable();
while let Some((idx, frequency)) = peekable.next() {
term_weight.insert(idx, weight);
if peekable.peek().map_or(false, |(_, f)| frequency != *f) {
if peekable.peek().is_some_and(|(_, f)| frequency != *f) {
weight += 1;
}
}

View file

@ -398,7 +398,7 @@ fn split_best_frequency(
let right = ctx.word_interner.insert(right.to_owned());
if let Some(frequency) = ctx.get_db_word_pair_proximity_docids_len(None, left, right, 1)? {
if best.map_or(true, |(old, _, _)| frequency > old) {
if best.is_none_or(|(old, _, _)| frequency > old) {
best = Some((frequency, left, right));
}
}

View file

@ -203,7 +203,7 @@ pub fn number_of_typos_allowed<'ctx>(
Ok(Box::new(move |word: &str| {
if !authorize_typos
|| word.len() < min_len_one_typo as usize
|| exact_words.as_ref().map_or(false, |fst| fst.contains(word))
|| exact_words.as_ref().is_some_and(|fst| fst.contains(word))
{
0
} else if word.len() < min_len_two_typos as usize {

View file

@ -17,7 +17,7 @@ use crate::Result;
pub struct PhraseDocIdsCache {
pub cache: FxHashMap<Interned<Phrase>, RoaringBitmap>,
}
impl<'ctx> SearchContext<'ctx> {
impl SearchContext<'_> {
/// Get the document ids associated with the given phrase
pub fn get_phrase_docids(&mut self, phrase: Interned<Phrase>) -> Result<&RoaringBitmap> {
if self.phrase_docids.cache.contains_key(&phrase) {

View file

@ -263,7 +263,7 @@ impl SmallBitmapInternal {
pub fn contains(&self, x: u16) -> bool {
let (set, x) = self.get_set_index(x);
set & 0b1 << x != 0
set & (0b1 << x) != 0
}
pub fn insert(&mut self, x: u16) {
@ -381,7 +381,7 @@ pub enum SmallBitmapInternalIter<'b> {
Tiny(u64),
Small { cur: u64, next: &'b [u64], base: u16 },
}
impl<'b> Iterator for SmallBitmapInternalIter<'b> {
impl Iterator for SmallBitmapInternalIter<'_> {
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {

View file

@ -101,8 +101,7 @@ impl FacetsUpdateIncremental {
let key = FacetGroupKeyCodec::<BytesRefCodec>::bytes_decode(key)
.map_err(heed::Error::Encoding)?;
if facet_level_may_be_updated
&& current_field_id.map_or(false, |fid| fid != key.field_id)
if facet_level_may_be_updated && current_field_id.is_some_and(|fid| fid != key.field_id)
{
// Only add or remove a level after making all the field modifications.
self.inner.add_or_delete_level(wtxn, current_field_id.unwrap())?;
@ -530,8 +529,8 @@ impl FacetsUpdateIncrementalInner {
add_docids: Option<&RoaringBitmap>,
del_docids: Option<&RoaringBitmap>,
) -> Result<bool> {
if add_docids.map_or(true, RoaringBitmap::is_empty)
&& del_docids.map_or(true, RoaringBitmap::is_empty)
if add_docids.is_none_or(RoaringBitmap::is_empty)
&& del_docids.is_none_or(RoaringBitmap::is_empty)
{
return Ok(false);
}
@ -670,7 +669,7 @@ impl FacetsUpdateIncrementalInner {
}
}
impl<'a> FacetGroupKey<&'a [u8]> {
impl FacetGroupKey<&[u8]> {
pub fn into_owned(self) -> FacetGroupKey<Vec<u8>> {
FacetGroupKey {
field_id: self.field_id,

View file

@ -115,7 +115,7 @@ pub fn enrich_documents_batch<R: Read + Seek>(
if let Some(geo_value) = geo_field_id.and_then(|fid| document.get(fid)) {
if let Err(user_error) = validate_geo_from_json(&document_id, geo_value)? {
return Ok(Err(UserError::from(user_error)));
return Ok(Err(UserError::from(Box::new(user_error))));
}
}

View file

@ -160,11 +160,11 @@ pub fn extract_fid_docid_facet_values<R: io::Read + io::Seek>(
let del_geo_support = settings_diff
.old
.geo_fields_ids
.map_or(false, |(lat, lng)| field_id == lat || field_id == lng);
.is_some_and(|(lat, lng)| field_id == lat || field_id == lng);
let add_geo_support = settings_diff
.new
.geo_fields_ids
.map_or(false, |(lat, lng)| field_id == lat || field_id == lng);
.is_some_and(|(lat, lng)| field_id == lat || field_id == lng);
let del_filterable_values =
del_value.map(|value| extract_facet_values(&value, del_geo_support));
let add_filterable_values =

View file

@ -80,22 +80,28 @@ fn extract_lat_lng(
let (lat, lng) = match (lat, lng) {
(Some(lat), Some(lng)) => (lat, lng),
(Some(_), None) => {
return Err(GeoError::MissingLatitude { document_id: document_id() }.into())
return Err(
Box::new(GeoError::MissingLatitude { document_id: document_id() }).into()
)
}
(None, Some(_)) => {
return Err(GeoError::MissingLongitude { document_id: document_id() }.into())
return Err(
Box::new(GeoError::MissingLongitude { document_id: document_id() }).into()
)
}
(None, None) => return Ok(None),
};
let lat = extract_finite_float_from_value(
serde_json::from_slice(lat).map_err(InternalError::SerdeJson)?,
)
.map_err(|lat| GeoError::BadLatitude { document_id: document_id(), value: lat })?;
.map_err(|lat| GeoError::BadLatitude { document_id: document_id(), value: lat })
.map_err(Box::new)?;
let lng = extract_finite_float_from_value(
serde_json::from_slice(lng).map_err(InternalError::SerdeJson)?,
)
.map_err(|lng| GeoError::BadLongitude { document_id: document_id(), value: lng })?;
.map_err(|lng| GeoError::BadLongitude { document_id: document_id(), value: lng })
.map_err(Box::new)?;
Ok(Some([lat, lng]))
}
None => Ok(None),

View file

@ -69,7 +69,7 @@ pub fn extract_word_pair_proximity_docids<R: io::Read + io::Seek>(
let document_id = u32::from_be_bytes(document_id_bytes);
// if we change document, we fill the sorter
if current_document_id.map_or(false, |id| id != document_id) {
if current_document_id.is_some_and(|id| id != document_id) {
// FIXME: span inside of a hot loop might degrade performance and create big reports
let span = tracing::trace_span!(target: "indexing::details", "document_into_sorter");
let _entered = span.enter();
@ -96,7 +96,7 @@ pub fn extract_word_pair_proximity_docids<R: io::Read + io::Seek>(
if let Some(deletion) = KvReaderDelAdd::from_slice(value).get(DelAdd::Deletion) {
for (position, word) in KvReaderU16::from_slice(deletion).iter() {
// drain the proximity window until the head word is considered close to the word we are inserting.
while del_word_positions.front().map_or(false, |(_w, p)| {
while del_word_positions.front().is_some_and(|(_w, p)| {
index_proximity(*p as u32, position as u32) >= MAX_DISTANCE
}) {
word_positions_into_word_pair_proximity(
@ -129,7 +129,7 @@ pub fn extract_word_pair_proximity_docids<R: io::Read + io::Seek>(
if let Some(addition) = KvReaderDelAdd::from_slice(value).get(DelAdd::Addition) {
for (position, word) in KvReaderU16::from_slice(addition).iter() {
// drain the proximity window until the head word is considered close to the word we are inserting.
while add_word_positions.front().map_or(false, |(_w, p)| {
while add_word_positions.front().is_some_and(|(_w, p)| {
index_proximity(*p as u32, position as u32) >= MAX_DISTANCE
}) {
word_positions_into_word_pair_proximity(

View file

@ -46,7 +46,7 @@ pub fn extract_word_position_docids<R: io::Read + io::Seek>(
.ok_or(SerializationError::Decoding { db_name: Some(DOCID_WORD_POSITIONS) })?;
let document_id = DocumentId::from_be_bytes(document_id_bytes);
if current_document_id.map_or(false, |id| document_id != id) {
if current_document_id.is_some_and(|id| document_id != id) {
words_position_into_sorter(
current_document_id.unwrap(),
&mut key_buffer,

View file

@ -281,7 +281,7 @@ fn send_original_documents_data(
};
if !(remove_vectors.is_empty()
&& manual_vectors.is_empty()
&& embeddings.as_ref().map_or(true, |e| e.is_empty()))
&& embeddings.as_ref().is_none_or(|e| e.is_empty()))
{
let _ = lmdb_writer_sx.send(Ok(TypedChunk::VectorPoints {
remove_vectors,

View file

@ -514,12 +514,9 @@ where
InternalError::DatabaseMissingEntry { db_name: "embedder_category_id", key: None },
)?;
let embedder_config = settings_diff.embedding_config_updates.get(&embedder_name);
let was_quantized = settings_diff
.old
.embedding_configs
.get(&embedder_name)
.map_or(false, |conf| conf.2);
let is_quantizing = embedder_config.map_or(false, |action| action.is_being_quantized);
let was_quantized =
settings_diff.old.embedding_configs.get(&embedder_name).is_some_and(|conf| conf.2);
let is_quantizing = embedder_config.is_some_and(|action| action.is_being_quantized);
pool.install(|| {
let mut writer = ArroyWrapper::new(vector_arroy, embedder_index, was_quantized);

View file

@ -197,7 +197,7 @@ impl<'a, 'i> Transform<'a, 'i> {
// drop_and_reuse is called instead of .clear() to communicate to the compiler that field_buffer
// does not keep references from the cursor between loop iterations
let mut field_buffer_cache = drop_and_reuse(field_buffer);
if self.indexer_settings.log_every_n.map_or(false, |len| documents_count % len == 0) {
if self.indexer_settings.log_every_n.is_some_and(|len| documents_count % len == 0) {
progress_callback(UpdateIndexingStep::RemapDocumentAddition {
documents_seen: documents_count,
});

View file

@ -55,7 +55,7 @@ impl ChunkAccumulator {
match self
.inner
.iter()
.position(|right| right.first().map_or(false, |right| chunk.mergeable_with(right)))
.position(|right| right.first().is_some_and(|right| chunk.mergeable_with(right)))
{
Some(position) => {
let v = self.inner.get_mut(position).unwrap();
@ -664,11 +664,8 @@ pub(crate) fn write_typed_chunk_into_index(
let embedder_index = index.embedder_category_id.get(wtxn, &embedder_name)?.ok_or(
InternalError::DatabaseMissingEntry { db_name: "embedder_category_id", key: None },
)?;
let binary_quantized = settings_diff
.old
.embedding_configs
.get(&embedder_name)
.map_or(false, |conf| conf.2);
let binary_quantized =
settings_diff.old.embedding_configs.get(&embedder_name).is_some_and(|conf| conf.2);
// FIXME: allow customizing distance
let writer = ArroyWrapper::new(index.vector_arroy, embedder_index, binary_quantized);

View file

@ -56,13 +56,13 @@ where
content: &'t KvReaderFieldId,
}
impl<'t, Mapper: FieldIdMapper> Clone for DocumentFromDb<'t, Mapper> {
impl<Mapper: FieldIdMapper> Clone for DocumentFromDb<'_, Mapper> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<'t, Mapper: FieldIdMapper> Copy for DocumentFromDb<'t, Mapper> {}
impl<Mapper: FieldIdMapper> Copy for DocumentFromDb<'_, Mapper> {}
impl<'t, Mapper: FieldIdMapper> Document<'t> for DocumentFromDb<'t, Mapper> {
fn iter_top_level_fields(&self) -> impl Iterator<Item = Result<(&'t str, &'t RawValue)>> {
@ -154,7 +154,7 @@ impl<'a, 'doc> DocumentFromVersions<'a, 'doc> {
}
}
impl<'a, 'doc> Document<'doc> for DocumentFromVersions<'a, 'doc> {
impl<'doc> Document<'doc> for DocumentFromVersions<'_, 'doc> {
fn iter_top_level_fields(&self) -> impl Iterator<Item = Result<(&'doc str, &'doc RawValue)>> {
self.versions.iter_top_level_fields().map(Ok)
}

View file

@ -121,7 +121,7 @@ impl<'extractor> BalancedCaches<'extractor> {
}
pub fn insert_del_u32(&mut self, key: &[u8], n: u32) -> Result<()> {
if self.max_memory.map_or(false, |mm| self.alloc.allocated_bytes() >= mm) {
if self.max_memory.is_some_and(|mm| self.alloc.allocated_bytes() >= mm) {
self.start_spilling()?;
}
@ -138,7 +138,7 @@ impl<'extractor> BalancedCaches<'extractor> {
}
pub fn insert_add_u32(&mut self, key: &[u8], n: u32) -> Result<()> {
if self.max_memory.map_or(false, |mm| self.alloc.allocated_bytes() >= mm) {
if self.max_memory.is_some_and(|mm| self.alloc.allocated_bytes() >= mm) {
self.start_spilling()?;
}
@ -623,7 +623,7 @@ pub struct FrozenDelAddBbbul<'bump, B> {
pub add: Option<FrozenBbbul<'bump, B>>,
}
impl<'bump, B> FrozenDelAddBbbul<'bump, B> {
impl<B> FrozenDelAddBbbul<'_, B> {
fn is_empty(&self) -> bool {
self.del.is_none() && self.add.is_none()
}

View file

@ -31,7 +31,7 @@ pub struct DocumentExtractorData {
pub field_distribution_delta: HashMap<String, i64>,
}
impl<'a, 'b, 'extractor> Extractor<'extractor> for DocumentsExtractor<'a, 'b> {
impl<'extractor> Extractor<'extractor> for DocumentsExtractor<'_, '_> {
type Data = FullySend<RefCell<DocumentExtractorData>>;
fn init_data(&self, _extractor_alloc: &'extractor Bump) -> Result<Self::Data> {

View file

@ -37,7 +37,7 @@ pub struct FacetedExtractorData<'a, 'b> {
is_geo_enabled: bool,
}
impl<'a, 'b, 'extractor> Extractor<'extractor> for FacetedExtractorData<'a, 'b> {
impl<'extractor> Extractor<'extractor> for FacetedExtractorData<'_, '_> {
type Data = RefCell<BalancedCaches<'extractor>>;
fn init_data(&self, extractor_alloc: &'extractor Bump) -> Result<Self::Data> {

View file

@ -92,7 +92,7 @@ pub struct FrozenGeoExtractorData<'extractor> {
pub spilled_inserted: Option<BufReader<File>>,
}
impl<'extractor> FrozenGeoExtractorData<'extractor> {
impl FrozenGeoExtractorData<'_> {
pub fn iter_and_clear_removed(
&mut self,
) -> io::Result<impl IntoIterator<Item = io::Result<ExtractedGeoPoint>> + '_> {
@ -160,7 +160,7 @@ impl<'extractor> Extractor<'extractor> for GeoExtractor {
for change in changes {
if data_ref.spilled_removed.is_none()
&& max_memory.map_or(false, |mm| context.extractor_alloc.allocated_bytes() >= mm)
&& max_memory.is_some_and(|mm| context.extractor_alloc.allocated_bytes() >= mm)
{
// We must spill as we allocated too much memory
data_ref.spilled_removed = tempfile::tempfile().map(BufWriter::new).map(Some)?;
@ -258,9 +258,11 @@ pub fn extract_geo_coordinates(
Value::Null => return Ok(None),
Value::Object(map) => map,
value => {
return Err(
GeoError::NotAnObject { document_id: Value::from(external_id), value }.into()
)
return Err(Box::new(GeoError::NotAnObject {
document_id: Value::from(external_id),
value,
})
.into())
}
};
@ -269,23 +271,29 @@ pub fn extract_geo_coordinates(
if geo.is_empty() {
[lat, lng]
} else {
return Err(GeoError::UnexpectedExtraFields {
return Err(Box::new(GeoError::UnexpectedExtraFields {
document_id: Value::from(external_id),
value: Value::from(geo),
}
})
.into());
}
}
(Some(_), None) => {
return Err(GeoError::MissingLongitude { document_id: Value::from(external_id) }.into())
return Err(Box::new(GeoError::MissingLongitude {
document_id: Value::from(external_id),
})
.into())
}
(None, Some(_)) => {
return Err(GeoError::MissingLatitude { document_id: Value::from(external_id) }.into())
return Err(Box::new(GeoError::MissingLatitude {
document_id: Value::from(external_id),
})
.into())
}
(None, None) => {
return Err(GeoError::MissingLatitudeAndLongitude {
return Err(Box::new(GeoError::MissingLatitudeAndLongitude {
document_id: Value::from(external_id),
}
})
.into())
}
};
@ -293,16 +301,18 @@ pub fn extract_geo_coordinates(
match (extract_finite_float_from_value(lat), extract_finite_float_from_value(lng)) {
(Ok(lat), Ok(lng)) => Ok(Some([lat, lng])),
(Ok(_), Err(value)) => {
Err(GeoError::BadLongitude { document_id: Value::from(external_id), value }.into())
Err(Box::new(GeoError::BadLongitude { document_id: Value::from(external_id), value })
.into())
}
(Err(value), Ok(_)) => {
Err(GeoError::BadLatitude { document_id: Value::from(external_id), value }.into())
Err(Box::new(GeoError::BadLatitude { document_id: Value::from(external_id), value })
.into())
}
(Err(lat), Err(lng)) => Err(GeoError::BadLatitudeAndLongitude {
(Err(lat), Err(lng)) => Err(Box::new(GeoError::BadLatitudeAndLongitude {
document_id: Value::from(external_id),
lat,
lng,
}
})
.into()),
}
}

View file

@ -31,7 +31,7 @@ pub struct WordDocidsBalancedCaches<'extractor> {
current_docid: Option<DocumentId>,
}
unsafe impl<'extractor> MostlySend for WordDocidsBalancedCaches<'extractor> {}
unsafe impl MostlySend for WordDocidsBalancedCaches<'_> {}
impl<'extractor> WordDocidsBalancedCaches<'extractor> {
pub fn new_in(buckets: usize, max_memory: Option<usize>, alloc: &'extractor Bump) -> Self {
@ -78,7 +78,7 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
buffer.extend_from_slice(&position.to_be_bytes());
self.word_position_docids.insert_add_u32(&buffer, docid)?;
if self.current_docid.map_or(false, |id| docid != id) {
if self.current_docid.is_some_and(|id| docid != id) {
self.flush_fid_word_count(&mut buffer)?;
}
@ -123,7 +123,7 @@ impl<'extractor> WordDocidsBalancedCaches<'extractor> {
buffer.extend_from_slice(&position.to_be_bytes());
self.word_position_docids.insert_del_u32(&buffer, docid)?;
if self.current_docid.map_or(false, |id| docid != id) {
if self.current_docid.is_some_and(|id| docid != id) {
self.flush_fid_word_count(&mut buffer)?;
}
@ -212,7 +212,7 @@ pub struct WordDocidsExtractorData<'a> {
searchable_attributes: Option<Vec<&'a str>>,
}
impl<'a, 'extractor> Extractor<'extractor> for WordDocidsExtractorData<'a> {
impl<'extractor> Extractor<'extractor> for WordDocidsExtractorData<'_> {
type Data = RefCell<Option<WordDocidsBalancedCaches<'extractor>>>;
fn init_data(&self, extractor_alloc: &'extractor Bump) -> Result<Self::Data> {

View file

@ -25,7 +25,7 @@ pub struct WordPairProximityDocidsExtractorData<'a> {
buckets: usize,
}
impl<'a, 'extractor> Extractor<'extractor> for WordPairProximityDocidsExtractorData<'a> {
impl<'extractor> Extractor<'extractor> for WordPairProximityDocidsExtractorData<'_> {
type Data = RefCell<BalancedCaches<'extractor>>;
fn init_data(&self, extractor_alloc: &'extractor Bump) -> Result<Self::Data> {
@ -270,7 +270,7 @@ fn process_document_tokens<'doc>(
// drain the proximity window until the head word is considered close to the word we are inserting.
while word_positions
.front()
.map_or(false, |(_w, p)| index_proximity(*p as u32, pos as u32) >= MAX_DISTANCE)
.is_some_and(|(_w, p)| index_proximity(*p as u32, pos as u32) >= MAX_DISTANCE)
{
word_positions_into_word_pair_proximity(word_positions, word_pair_proximity);
}

View file

@ -22,7 +22,7 @@ pub struct DocumentTokenizer<'a> {
pub max_positions_per_attributes: u32,
}
impl<'a> DocumentTokenizer<'a> {
impl DocumentTokenizer<'_> {
pub fn tokenize_document<'doc>(
&self,
document: impl Document<'doc>,

View file

@ -43,7 +43,7 @@ pub struct EmbeddingExtractorData<'extractor>(
unsafe impl MostlySend for EmbeddingExtractorData<'_> {}
impl<'a, 'b, 'extractor> Extractor<'extractor> for EmbeddingExtractor<'a, 'b> {
impl<'extractor> Extractor<'extractor> for EmbeddingExtractor<'_, '_> {
type Data = RefCell<EmbeddingExtractorData<'extractor>>;
fn init_data<'doc>(&'doc self, extractor_alloc: &'extractor Bump) -> crate::Result<Self::Data> {

View file

@ -29,8 +29,8 @@ impl<'p, 'indexer, Mapper: MutFieldIdMapper> FieldAndDocidExtractor<'p, 'indexer
}
}
impl<'de, 'p, 'indexer: 'de, Mapper: MutFieldIdMapper> Visitor<'de>
for FieldAndDocidExtractor<'p, 'indexer, Mapper>
impl<'de, 'indexer: 'de, Mapper: MutFieldIdMapper> Visitor<'de>
for FieldAndDocidExtractor<'_, 'indexer, Mapper>
{
type Value =
Result<Result<DeOrBumpStr<'de, 'indexer>, DocumentIdExtractionError>, crate::UserError>;
@ -98,7 +98,7 @@ struct NestedPrimaryKeyVisitor<'a, 'bump> {
bump: &'bump Bump,
}
impl<'de, 'a, 'bump: 'de> Visitor<'de> for NestedPrimaryKeyVisitor<'a, 'bump> {
impl<'de, 'bump: 'de> Visitor<'de> for NestedPrimaryKeyVisitor<'_, 'bump> {
type Value = std::result::Result<Option<DeOrBumpStr<'de, 'bump>>, DocumentIdExtractionError>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -237,7 +237,7 @@ impl<'de, 'a, Mapper: MutFieldIdMapper> Visitor<'de> for MutFieldIdMapVisitor<'a
pub struct FieldIdMapVisitor<'a, Mapper: FieldIdMapper>(pub &'a Mapper);
impl<'de, 'a, Mapper: FieldIdMapper> Visitor<'de> for FieldIdMapVisitor<'a, Mapper> {
impl<'de, Mapper: FieldIdMapper> Visitor<'de> for FieldIdMapVisitor<'_, Mapper> {
type Value = Option<FieldId>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {

View file

@ -149,16 +149,11 @@ pub struct IndexingContext<
pub grenad_parameters: &'indexer GrenadParameters,
}
impl<
'fid, // invariant lifetime of fields ids map
'indexer, // covariant lifetime of objects that are borrowed during the entire indexing operation
'index, // covariant lifetime of the index
MSP,
> Copy
impl<MSP> Copy
for IndexingContext<
'fid, // invariant lifetime of fields ids map
'indexer, // covariant lifetime of objects that are borrowed during the entire indexing operation
'index, // covariant lifetime of the index
'_, // invariant lifetime of fields ids map
'_, // covariant lifetime of objects that are borrowed during the entire indexing operation
'_, // covariant lifetime of the index
MSP,
>
where
@ -166,16 +161,11 @@ where
{
}
impl<
'fid, // invariant lifetime of fields ids map
'indexer, // covariant lifetime of objects that are borrowed during the entire indexing operation
'index, // covariant lifetime of the index
MSP,
> Clone
impl<MSP> Clone
for IndexingContext<
'fid, // invariant lifetime of fields ids map
'indexer, // covariant lifetime of objects that are borrowed during the entire indexing operation
'index, // covariant lifetime of the index
'_, // invariant lifetime of fields ids map
'_, // covariant lifetime of objects that are borrowed during the entire indexing operation
'_, // covariant lifetime of the index
MSP,
>
where

View file

@ -110,7 +110,7 @@ mod test {
>,
}
unsafe impl<'extractor> MostlySend for DeletionWithData<'extractor> {}
unsafe impl MostlySend for DeletionWithData<'_> {}
struct TrackDeletion<'extractor>(PhantomData<&'extractor ()>);

View file

@ -210,14 +210,8 @@ fn extract_addition_payload_changes<'r, 'pl: 'r>(
primary_key.as_ref().unwrap()
};
let external_id = match retrieved_primary_key.extract_fields_and_docid(
doc,
new_fields_ids_map,
indexer,
) {
Ok(edi) => edi,
Err(e) => return Err(e),
};
let external_id =
retrieved_primary_key.extract_fields_and_docid(doc, new_fields_ids_map, indexer)?;
let external_id = external_id.to_de();
let current_offset = iter.byte_offset();
@ -580,12 +574,12 @@ impl<'pl> PayloadOperations<'pl> {
}
}
Some(InnerDocOp::Deletion) => {
return if self.is_new {
if self.is_new {
Ok(None)
} else {
let deletion = Deletion::create(self.docid, external_doc);
Ok(Some(DocumentChange::Deletion(deletion)))
};
}
}
None => unreachable!("We must not have an empty set of operations on a document"),
}

View file

@ -14,7 +14,7 @@ use crate::index::IndexEmbeddingConfig;
use crate::progress::Progress;
use crate::update::settings::InnerIndexSettings;
use crate::vector::{ArroyWrapper, Embedder, EmbeddingConfigs, Embeddings};
use crate::{Error, Index, InternalError, Result};
use crate::{Error, Index, InternalError, Result, UserError};
pub fn write_to_db(
mut writer_receiver: WriterBbqueueReceiver<'_>,
@ -219,7 +219,12 @@ pub fn write_from_bbqueue(
arroy_writers.get(&embedder_id).expect("requested a missing embedder");
let mut embeddings = Embeddings::new(*dimensions);
let all_embeddings = asvs.read_all_embeddings_into_vec(frame, aligned_embedding);
embeddings.append(all_embeddings.to_vec()).unwrap();
if embeddings.append(all_embeddings.to_vec()).is_err() {
return Err(Error::UserError(UserError::InvalidVectorDimensions {
expected: *dimensions,
found: all_embeddings.len(),
}));
}
writer.del_items(wtxn, *dimensions, docid)?;
writer.add_items(wtxn, docid, &embeddings)?;
}

View file

@ -149,7 +149,7 @@ impl<'a, 'rtxn> FrozenPrefixBitmaps<'a, 'rtxn> {
}
}
unsafe impl<'a, 'rtxn> Sync for FrozenPrefixBitmaps<'a, 'rtxn> {}
unsafe impl Sync for FrozenPrefixBitmaps<'_, '_> {}
struct WordPrefixIntegerDocids {
database: Database<Bytes, CboRoaringBitmapCodec>,
@ -302,7 +302,7 @@ impl<'a, 'rtxn> FrozenPrefixIntegerBitmaps<'a, 'rtxn> {
}
}
unsafe impl<'a, 'rtxn> Sync for FrozenPrefixIntegerBitmaps<'a, 'rtxn> {}
unsafe impl Sync for FrozenPrefixIntegerBitmaps<'_, '_> {}
#[tracing::instrument(level = "trace", skip_all, target = "indexing::prefix")]
fn delete_prefixes(

View file

@ -560,7 +560,7 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> {
// Does the new FST differ from the previous one?
if current
.map_or(true, |current| current.as_fst().as_bytes() != fst.as_fst().as_bytes())
.is_none_or(|current| current.as_fst().as_bytes() != fst.as_fst().as_bytes())
{
// we want to re-create our FST.
self.index.put_stop_words(self.wtxn, &fst)?;
@ -580,7 +580,7 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> {
let current = self.index.non_separator_tokens(self.wtxn)?;
// Does the new list differ from the previous one?
if current.map_or(true, |current| &current != non_separator_tokens) {
if current.is_none_or(|current| &current != non_separator_tokens) {
self.index.put_non_separator_tokens(self.wtxn, non_separator_tokens)?;
true
} else {
@ -605,7 +605,7 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> {
let current = self.index.separator_tokens(self.wtxn)?;
// Does the new list differ from the previous one?
if current.map_or(true, |current| &current != separator_tokens) {
if current.is_none_or(|current| &current != separator_tokens) {
self.index.put_separator_tokens(self.wtxn, separator_tokens)?;
true
} else {
@ -630,7 +630,7 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> {
let current = self.index.dictionary(self.wtxn)?;
// Does the new list differ from the previous one?
if current.map_or(true, |current| &current != dictionary) {
if current.is_none_or(|current| &current != dictionary) {
self.index.put_dictionary(self.wtxn, dictionary)?;
true
} else {
@ -1353,7 +1353,7 @@ impl InnerIndexSettingsDiff {
new_settings.embedding_configs.inner_as_ref()
{
let was_quantized =
old_settings.embedding_configs.get(embedder_name).map_or(false, |conf| conf.2);
old_settings.embedding_configs.get(embedder_name).is_some_and(|conf| conf.2);
// skip embedders that don't use document templates
if !config.uses_document_template() {
continue;

View file

@ -311,7 +311,7 @@ fn last_named_object<'a>(
last_named_object
}
impl<'a> std::fmt::Display for LastNamedObject<'a> {
impl std::fmt::Display for LastNamedObject<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LastNamedObject::Object { name } => write!(f, "`{name}`"),

View file

@ -59,7 +59,7 @@ impl ArroyWrapper {
&'a self,
rtxn: &'a RoTxn<'a>,
db: arroy::Database<D>,
) -> impl Iterator<Item = Result<arroy::Reader<D>, arroy::Error>> + 'a {
) -> impl Iterator<Item = Result<arroy::Reader<'a, D>, arroy::Error>> + 'a {
arroy_db_range_for_embedder(self.embedder_index).map_while(move |index| {
match arroy::Reader::open(rtxn, index, db) {
Ok(reader) => match reader.is_empty(rtxn) {

View file

@ -242,11 +242,11 @@ fn execute_filter(filter: &str, document: &TestDocument) -> Option<String> {
id = contains_key_rec(opt1, "opt2").then(|| document.id.clone());
}
} else if matches!(filter, "opt1 IS NULL" | "NOT opt1 IS NOT NULL") {
id = document.opt1.as_ref().map_or(false, |v| v.is_null()).then(|| document.id.clone());
id = document.opt1.as_ref().is_some_and(|v| v.is_null()).then(|| document.id.clone());
} else if matches!(filter, "NOT opt1 IS NULL" | "opt1 IS NOT NULL") {
id = document.opt1.as_ref().map_or(true, |v| !v.is_null()).then(|| document.id.clone());
id = document.opt1.as_ref().is_none_or(|v| !v.is_null()).then(|| document.id.clone());
} else if matches!(filter, "opt1.opt2 IS NULL") {
if document.opt1opt2.as_ref().map_or(false, |v| v.is_null()) {
if document.opt1opt2.as_ref().is_some_and(|v| v.is_null()) {
id = Some(document.id.clone());
} else if let Some(opt1) = &document.opt1 {
if !opt1.is_null() {
@ -254,15 +254,11 @@ fn execute_filter(filter: &str, document: &TestDocument) -> Option<String> {
}
}
} else if matches!(filter, "opt1 IS EMPTY" | "NOT opt1 IS NOT EMPTY") {
id = document.opt1.as_ref().map_or(false, is_empty_value).then(|| document.id.clone());
id = document.opt1.as_ref().is_some_and(is_empty_value).then(|| document.id.clone());
} else if matches!(filter, "NOT opt1 IS EMPTY" | "opt1 IS NOT EMPTY") {
id = document
.opt1
.as_ref()
.map_or(true, |v| !is_empty_value(v))
.then(|| document.id.clone());
id = document.opt1.as_ref().is_none_or(|v| !is_empty_value(v)).then(|| document.id.clone());
} else if matches!(filter, "opt1.opt2 IS EMPTY") {
if document.opt1opt2.as_ref().map_or(false, is_empty_value) {
if document.opt1opt2.as_ref().is_some_and(is_empty_value) {
id = Some(document.id.clone());
}
} else if matches!(