use super::super::{extract_identifier, Rule};
use crate::velesql::error::{ParseError, ParseErrorKind};
use crate::velesql::Parser;
impl Parser {
pub(crate) fn parse_using_fusion_clause(
pair: pest::iterators::Pair<Rule>,
) -> Result<crate::velesql::FusionClause, ParseError> {
let mut clause = crate::velesql::FusionClause {
strategy: crate::velesql::FusionStrategyType::Rrf,
k: None,
vector_weight: None,
graph_weight: None,
dense_weight: None,
sparse_weight: None,
};
for inner_pair in pair.into_inner() {
if inner_pair.as_rule() == Rule::fusion_options {
Self::parse_fusion_options(inner_pair, &mut clause)?;
}
}
Ok(clause)
}
fn parse_fusion_options(
pair: pest::iterators::Pair<Rule>,
clause: &mut crate::velesql::FusionClause,
) -> Result<(), ParseError> {
for opt_pair in pair.into_inner() {
if opt_pair.as_rule() == Rule::fusion_option_list {
for option in opt_pair.into_inner() {
if option.as_rule() == Rule::fusion_option {
Self::apply_fusion_option(option, clause)?;
}
}
}
}
Ok(())
}
fn extract_fusion_kv(option: pest::iterators::Pair<Rule>) -> (String, String) {
let mut key = String::new();
let mut value_str = String::new();
for part in option.into_inner() {
match part.as_rule() {
Rule::identifier => key = extract_identifier(&part).to_lowercase(),
Rule::fusion_value => {
if let Some(child) = part.into_inner().next() {
value_str = if child.as_rule() == Rule::string {
crate::velesql::parser::helpers::unescape_string_literal(child.as_str())
} else {
child.as_str().to_string()
};
}
}
_ => {}
}
}
(key, value_str)
}
fn apply_fusion_option(
option: pest::iterators::Pair<Rule>,
clause: &mut crate::velesql::FusionClause,
) -> Result<(), ParseError> {
let (key, value_str) = Self::extract_fusion_kv(option);
match key.as_str() {
"strategy" => clause.strategy = Self::parse_fusion_strategy_type(&value_str)?,
"k" => clause.k = value_str.parse().ok(),
"vector_weight" => clause.vector_weight = value_str.parse().ok(),
"graph_weight" => clause.graph_weight = value_str.parse().ok(),
"dense_w" | "dense_weight" => clause.dense_weight = value_str.parse().ok(),
"sparse_w" | "sparse_weight" => clause.sparse_weight = value_str.parse().ok(),
other => {
return Err(ParseError::new(
ParseErrorKind::SyntaxError,
0,
other.to_string(),
format!(
"Unknown USING FUSION option '{other}'. Valid keys: strategy, k, \
vector_weight, graph_weight, dense_weight, sparse_weight"
),
));
}
}
Ok(())
}
fn parse_fusion_strategy_type(
name: &str,
) -> Result<crate::velesql::FusionStrategyType, ParseError> {
match name.to_lowercase().as_str() {
"rrf" => Ok(crate::velesql::FusionStrategyType::Rrf),
"weighted" => Ok(crate::velesql::FusionStrategyType::Weighted),
"maximum" => Ok(crate::velesql::FusionStrategyType::Maximum),
"rsf" | "relative_score" => Ok(crate::velesql::FusionStrategyType::Rsf),
"average" => Ok(crate::velesql::FusionStrategyType::Average),
other => Err(ParseError::new(
ParseErrorKind::SyntaxError,
0,
other.to_string(),
format!(
"Unknown USING FUSION strategy '{other}'. Valid strategies: rrf, weighted, \
maximum, rsf (relative_score), average"
),
)),
}
}
}