use std::collections::HashMap;
use std::sync::Arc;
use ahash::{AHashMap, AHashSet};
use atomic_refcell::AtomicRefCell;
use crate::common::condition_checker::{
CheckItem, ConditionChecker, ConstantConditionChecker, Rest, Select, default_check_batched,
};
use crate::common::counter::hardware_counter::HardwareCounterCell;
use crate::common::types::{DeferredBehavior, PointOffsetType};
use serde_json::Value;
use super::StructPayloadIndexReadView;
use crate::segment::common::operation_error::{OperationError, OperationResult};
use crate::segment::id_tracker::IdTrackerRead;
use crate::segment::index::condition_checker::ConditionCheckerEnum;
use crate::segment::index::field_index::FieldIndexRead;
use crate::segment::index::query_optimization::payload_provider::PayloadProvider;
use crate::segment::json_path::JsonPath;
use crate::segment::payload_storage::PayloadStorageRead;
use crate::segment::payload_storage::query_checker::{
check_field_condition, check_is_empty_condition, check_is_null_condition, check_payload,
select_nested_indexes,
};
use crate::segment::types::{Condition, FieldCondition, OwnedPayloadRef, PayloadContainer, Slice};
use crate::segment::vector_storage::VectorStorageRead;
impl<'a, P, I, V, F> StructPayloadIndexReadView<'a, P, I, V, F>
where
P: PayloadStorageRead,
I: IdTrackerRead,
V: VectorStorageRead,
F: FieldIndexRead,
{
pub fn condition_converter<'b, S: PayloadStorageRead + 'b>(
&'b self,
condition: &'b Condition,
payload_provider: PayloadProvider<S>,
deferred_behavior: DeferredBehavior,
hw_counter: &HardwareCounterCell,
) -> OperationResult<ConditionCheckerEnum<'b>> {
let id_tracker = self.id_tracker;
let field_indexes = self.field_indexes;
Ok(match condition {
Condition::Field(field_condition) => field_condition_checker(
field_indexes,
&field_condition.key,
hw_counter,
field_condition,
payload_provider,
|payload, hw| check_field_condition(field_condition, &payload, field_indexes, hw),
)?,
Condition::IsEmpty(is_empty) => {
let key = &is_empty.is_empty.key;
field_condition_checker(
field_indexes,
key,
hw_counter,
&FieldCondition::new_is_empty(key.clone(), true),
payload_provider,
|payload, _| Ok(check_is_empty_condition(is_empty, &payload)),
)?
}
Condition::IsNull(is_null) => {
let key = &is_null.is_null.key;
field_condition_checker(
field_indexes,
key,
hw_counter,
&FieldCondition::new_is_null(key.clone(), true),
payload_provider,
|payload, _| Ok(check_is_null_condition(is_null, &payload)),
)?
}
Condition::HasId(has_id) => {
let mut segment_ids = AHashSet::with_capacity(has_id.has_id.len());
id_tracker.resolve_external_ids(
has_id.has_id.iter().copied(),
deferred_behavior,
|_, offset| {
segment_ids.insert(offset);
},
)?;
ConditionCheckerEnum::Ids(IdsConditionChecker(segment_ids))
}
Condition::HasVector(has_vector) => {
if let Some(vector_storage) =
self.vector_storages.get(&has_vector.has_vector).cloned()
{
ConditionCheckerEnum::Dyn(Box::new(HasVectorConditionChecker(vector_storage)))
} else {
ConditionCheckerEnum::Constant(ConstantConditionChecker::MATCH_NONE)
}
}
Condition::Nested(nested) => {
let nested_path = nested.array_key();
let nested_indexes = select_nested_indexes(&nested_path, field_indexes);
ConditionCheckerEnum::Dyn(Box::new(PayloadConditionChecker {
payload_provider,
hw_counter: hw_counter.fork(),
check: move |payload, point_id, hw| {
let field_values = payload.get_value(&nested_path);
for value in field_values {
if let Value::Object(object) = value {
let get_payload = || OwnedPayloadRef::from(object);
if check_payload(
Box::new(get_payload),
None,
&HashMap::new(),
&nested.nested.filter,
point_id,
&nested_indexes,
hw,
) {
return Ok(true);
}
}
}
Ok(false)
},
}))
}
Condition::Slice(slice_condition) => {
ConditionCheckerEnum::Dyn(Box::new(SliceConditionChecker {
id_tracker,
slice: slice_condition.slice,
}))
}
Condition::CustomIdChecker(cond) => {
let segment_ids: AHashSet<_> = id_tracker
.point_mappings()
.iter_external()
.filter(|&point_id| cond.0.check(point_id))
.filter_map(|external_id| {
id_tracker.internal_id_with_behavior(external_id, deferred_behavior)
})
.collect();
ConditionCheckerEnum::Ids(IdsConditionChecker(segment_ids))
}
Condition::Filter(_) => unreachable!(),
})
}
}
fn field_condition_checker<'a>(
field_indexes: &'a AHashMap<JsonPath, Vec<impl FieldIndexRead>>,
key: &JsonPath,
hw_counter: &HardwareCounterCell,
field_condition: &FieldCondition,
payload_provider: PayloadProvider<impl PayloadStorageRead + 'a>,
check: impl Fn(OwnedPayloadRef, &HardwareCounterCell) -> OperationResult<bool> + 'a,
) -> OperationResult<ConditionCheckerEnum<'a>> {
if let Some(indexes) = field_indexes.get(key) {
for index in indexes {
let hw_acc = hw_counter.new_accumulator();
if let Some(checker) = index.condition_checker(field_condition, hw_acc)? {
return Ok(checker);
}
}
}
Ok(ConditionCheckerEnum::Dyn(Box::new(
PayloadConditionChecker {
payload_provider,
hw_counter: hw_counter.fork(),
check: move |payload, _, hw| check(payload, hw),
},
)))
}
struct PayloadConditionChecker<S, F>
where
S: PayloadStorageRead,
F: Fn(OwnedPayloadRef, PointOffsetType, &HardwareCounterCell) -> OperationResult<bool>,
{
payload_provider: PayloadProvider<S>,
hw_counter: HardwareCounterCell,
check: F,
}
impl<S, F> ConditionChecker for PayloadConditionChecker<S, F>
where
S: PayloadStorageRead,
F: Fn(OwnedPayloadRef, PointOffsetType, &HardwareCounterCell) -> OperationResult<bool>,
{
type Error = OperationError;
fn check(&self, point_id: PointOffsetType) -> OperationResult<bool> {
self.payload_provider.with_payload(
point_id,
|payload| (self.check)(payload, point_id, &self.hw_counter),
&self.hw_counter,
)
}
fn check_batched<K>(&self, ids: &mut [K], select: Select, rest: Rest) -> OperationResult<usize>
where
K: CheckItem,
{
default_check_batched(ids, select, rest, |id| self.check(id))
}
}
pub struct IdsConditionChecker(AHashSet<PointOffsetType>);
impl ConditionChecker for IdsConditionChecker {
type Error = OperationError;
fn check(&self, point_id: PointOffsetType) -> OperationResult<bool> {
Ok(self.0.contains(&point_id))
}
fn check_batched<K>(&self, ids: &mut [K], select: Select, rest: Rest) -> OperationResult<usize>
where
K: CheckItem,
{
default_check_batched(ids, select, rest, |id| self.check(id))
}
}
struct SliceConditionChecker<'a, I: IdTrackerRead> {
id_tracker: &'a I,
slice: Slice,
}
impl<I: IdTrackerRead> ConditionChecker for SliceConditionChecker<'_, I> {
type Error = OperationError;
fn check(&self, point_id: PointOffsetType) -> OperationResult<bool> {
Ok(self
.id_tracker
.external_id(point_id)
.is_some_and(|external_id| self.slice.check(external_id)))
}
fn check_batched<K>(&self, ids: &mut [K], select: Select, rest: Rest) -> OperationResult<usize>
where
K: CheckItem,
{
default_check_batched(ids, select, rest, |id| self.check(id))
}
}
struct HasVectorConditionChecker<V: VectorStorageRead>(Arc<AtomicRefCell<V>>);
impl<V: VectorStorageRead> ConditionChecker for HasVectorConditionChecker<V> {
type Error = OperationError;
fn check(&self, point_id: PointOffsetType) -> OperationResult<bool> {
Ok(!self.0.borrow().is_deleted_vector(point_id))
}
fn check_batched<K: CheckItem>(
&self,
ids: &mut [K],
select: Select,
rest: Rest,
) -> OperationResult<usize> {
default_check_batched(ids, select, rest, |id| self.check(id))
}
}