mirror of
https://github.com/qdrant/qdrant.git
synced 2026-08-05 17:40:55 -05:00
* allow custom K parameter for RRF * generate grpc docs and openapi * use tagged type approach for parametrized fusions * use params approach in grpc * simplify api structure * upd schema * nits * rest: parameterized rrf as query variant * consistency in doc comments --------- Co-authored-by: Andrey Vasnetsov <andrey@vasnetsov.com>
274 lines
9.6 KiB
Rust
274 lines
9.6 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use common::validation::validate_multi_vector;
|
|
use segment::index::query_optimization::rescore_formula::parsed_formula::VariableId;
|
|
use validator::{Validate, ValidationError, ValidationErrors};
|
|
|
|
use super::{
|
|
Batch, BatchVectorStruct, ContextInput, Expression, FormulaQuery, Fusion, NamedVectorStruct,
|
|
OrderByInterface, PointVectors, Query, QueryInterface, RecommendInput, Sample, VectorInput,
|
|
};
|
|
|
|
impl Validate for NamedVectorStruct {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
NamedVectorStruct::Default(_) => Ok(()),
|
|
NamedVectorStruct::Dense(_) => Ok(()),
|
|
NamedVectorStruct::Sparse(v) => v.validate(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for QueryInterface {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
QueryInterface::Nearest(vector) => vector.validate(),
|
|
QueryInterface::Query(query) => query.validate(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for Query {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
Query::Nearest(vector) => vector.validate(),
|
|
Query::Recommend(recommend) => recommend.validate(),
|
|
Query::Discover(discover) => discover.validate(),
|
|
Query::Context(context) => context.validate(),
|
|
Query::Fusion(fusion) => fusion.validate(),
|
|
Query::Rrf(rrf) => rrf.validate(),
|
|
Query::Formula(formula) => formula.validate(),
|
|
Query::OrderBy(order_by) => order_by.validate(),
|
|
Query::Sample(sample) => sample.validate(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for VectorInput {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
VectorInput::Id(_id) => Ok(()),
|
|
VectorInput::DenseVector(_dense) => Ok(()),
|
|
VectorInput::SparseVector(sparse) => sparse.validate(),
|
|
VectorInput::MultiDenseVector(multi) => validate_multi_vector(multi),
|
|
VectorInput::Document(doc) => doc.validate(),
|
|
VectorInput::Image(image) => image.validate(),
|
|
VectorInput::Object(obj) => obj.validate(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for RecommendInput {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
let no_positives = self.positive.as_ref().map(|p| p.is_empty()).unwrap_or(true);
|
|
let no_negatives = self.negative.as_ref().map(|n| n.is_empty()).unwrap_or(true);
|
|
|
|
if no_positives && no_negatives {
|
|
let mut errors = validator::ValidationErrors::new();
|
|
errors.add(
|
|
"positives, negatives",
|
|
ValidationError::new(
|
|
"At least one positive or negative vector/id must be provided",
|
|
),
|
|
);
|
|
return Err(errors);
|
|
}
|
|
|
|
for item in self.iter() {
|
|
item.validate()?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Validate for ContextInput {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
for item in self.0.iter().flatten().flat_map(|item| item.iter()) {
|
|
item.validate()?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Validate for Fusion {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
Fusion::Rrf | Fusion::Dbsf => Ok(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for FormulaQuery {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
let Self { formula, defaults } = self;
|
|
|
|
// validate formula Expression
|
|
formula.validate()?;
|
|
let mut errors = validator::ValidationErrors::new();
|
|
|
|
for (key, value) in defaults.iter() {
|
|
let var_id = match key.parse() {
|
|
Ok(var_id) => var_id,
|
|
Err(err) => {
|
|
let validation =
|
|
ValidationError::new("Invalid variable name").with_message(Cow::Owned(err));
|
|
errors.add("defaults", validation);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
match var_id {
|
|
VariableId::Score(_) if value.as_number().is_none() => {
|
|
let validation = ValidationError::new("Score default must be a number");
|
|
errors.add("defaults", validation);
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Validate for OrderByInterface {
|
|
fn validate(&self) -> Result<(), validator::ValidationErrors> {
|
|
match self {
|
|
OrderByInterface::Key(_key) => Ok(()), // validated during parsing
|
|
OrderByInterface::Struct(order_by) => order_by.validate(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for Sample {
|
|
fn validate(&self) -> Result<(), ValidationErrors> {
|
|
match self {
|
|
Sample::Random => Ok(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for BatchVectorStruct {
|
|
fn validate(&self) -> Result<(), ValidationErrors> {
|
|
match self {
|
|
BatchVectorStruct::Single(_) => Ok(()),
|
|
BatchVectorStruct::MultiDense(vectors) => {
|
|
for vector in vectors {
|
|
validate_multi_vector(vector)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
BatchVectorStruct::Named(v) => {
|
|
common::validation::validate_iter(v.values().flat_map(|batch| batch.iter()))
|
|
}
|
|
BatchVectorStruct::Document(_) => Ok(()),
|
|
BatchVectorStruct::Image(_) => Ok(()),
|
|
BatchVectorStruct::Object(_) => Ok(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for Batch {
|
|
fn validate(&self) -> Result<(), ValidationErrors> {
|
|
let batch = self;
|
|
|
|
let bad_input_description = |ids: usize, vecs: usize| -> String {
|
|
format!("number of ids and vectors must be equal ({ids} != {vecs})")
|
|
};
|
|
let create_error = |message: String| -> ValidationErrors {
|
|
let mut errors = ValidationErrors::new();
|
|
errors.add("batch", {
|
|
let mut error = ValidationError::new("point_insert_operation");
|
|
error.message.replace(Cow::from(message));
|
|
error
|
|
});
|
|
errors
|
|
};
|
|
|
|
self.vectors.validate()?;
|
|
match &batch.vectors {
|
|
BatchVectorStruct::Single(vectors) => {
|
|
if batch.ids.len() != vectors.len() {
|
|
return Err(create_error(bad_input_description(
|
|
batch.ids.len(),
|
|
vectors.len(),
|
|
)));
|
|
}
|
|
}
|
|
BatchVectorStruct::MultiDense(vectors) => {
|
|
if batch.ids.len() != vectors.len() {
|
|
return Err(create_error(bad_input_description(
|
|
batch.ids.len(),
|
|
vectors.len(),
|
|
)));
|
|
}
|
|
}
|
|
BatchVectorStruct::Named(named_vectors) => {
|
|
for vectors in named_vectors.values() {
|
|
if batch.ids.len() != vectors.len() {
|
|
return Err(create_error(bad_input_description(
|
|
batch.ids.len(),
|
|
vectors.len(),
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
BatchVectorStruct::Document(_) => {}
|
|
BatchVectorStruct::Image(_) => {}
|
|
BatchVectorStruct::Object(_) => {}
|
|
}
|
|
if let Some(payload_vector) = &batch.payloads
|
|
&& payload_vector.len() != batch.ids.len()
|
|
{
|
|
return Err(create_error(format!(
|
|
"number of ids and payloads must be equal ({} != {})",
|
|
batch.ids.len(),
|
|
payload_vector.len(),
|
|
)));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Validate for PointVectors {
|
|
fn validate(&self) -> Result<(), ValidationErrors> {
|
|
if self.vector.is_empty() {
|
|
let mut err = ValidationError::new("length");
|
|
err.message = Some(Cow::from("must specify vectors to update for point"));
|
|
err.add_param(Cow::from("min"), &1);
|
|
let mut errors = ValidationErrors::new();
|
|
errors.add("vector", err);
|
|
Err(errors)
|
|
} else {
|
|
self.vector.validate()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Validate for Expression {
|
|
fn validate(&self) -> Result<(), ValidationErrors> {
|
|
match self {
|
|
Expression::Constant(_) => Ok(()),
|
|
Expression::Variable(_) => Ok(()),
|
|
Expression::Condition(condition) => condition.validate(),
|
|
Expression::GeoDistance(_) => Ok(()),
|
|
Expression::Datetime(_) => Ok(()),
|
|
Expression::DatetimeKey(_) => Ok(()),
|
|
Expression::Mult(mult_expression) => mult_expression.validate(),
|
|
Expression::Sum(sum_expression) => sum_expression.validate(),
|
|
Expression::Neg(neg_expression) => neg_expression.validate(),
|
|
Expression::Abs(abs_expression) => abs_expression.validate(),
|
|
Expression::Div(div_expression) => div_expression.validate(),
|
|
Expression::Sqrt(sqrt_expression) => sqrt_expression.validate(),
|
|
Expression::Pow(pow_expression) => pow_expression.validate(),
|
|
Expression::Exp(exp_expression) => exp_expression.validate(),
|
|
Expression::Log10(log10_expression) => log10_expression.validate(),
|
|
Expression::Ln(ln_expression) => ln_expression.validate(),
|
|
Expression::LinDecay(lin_decay_expression) => lin_decay_expression.validate(),
|
|
Expression::ExpDecay(exp_decay_expression) => exp_decay_expression.validate(),
|
|
Expression::GaussDecay(gauss_decay_expression) => gauss_decay_expression.validate(),
|
|
}
|
|
}
|
|
}
|