use crate::ast::*;
use crate::error::QqlError;
use crate::parser::Parser;
use alloc::format;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
pub fn format(source: &str) -> Result<String, QqlError> {
let statements = Parser::parse_all(source)?;
Ok(format_script(&statements))
}
pub fn format_script(statements: &[Stmt]) -> String {
let mut out = String::new();
for (i, statement) in statements.iter().enumerate() {
if i > 0 {
out.push('\n');
}
out.push_str(&format_stmt(statement));
out.push(';');
}
out
}
pub fn format_stmt(statement: &Stmt) -> String {
match statement {
Stmt::Query(query) => render_query_body(query),
Stmt::Scroll(statement) => {
let mut out = format!("SCROLL FROM {}", render_name(&statement.collection));
if let Some(filter) = &statement.filter {
out.push_str(&format!(" WHERE {}", render_filter(filter)));
}
if let Some(after) = &statement.after {
out.push_str(&format!(" AFTER {}", render_point_id(after)));
}
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
if let Some(selector) = &statement.with_vector {
out.push_str(&format!(
" WITH VECTOR {}",
render_vector_selector(selector)
));
}
out.push_str(&format!(" LIMIT {}", statement.limit));
out
}
Stmt::Upsert(statement) => {
let mut out = format!("UPSERT INTO {} VALUES", render_name(&statement.collection));
for (i, point) in statement.points.iter().enumerate() {
if i > 0 {
out.push_str(", ");
} else {
out.push(' ');
}
out.push_str(&render_point(point));
}
if let Some(embedding) = &statement.embedding {
out.push_str(&format!(" USING {}", render_embedding_spec(embedding)));
}
if !statement.embed.is_empty() {
out.push_str(" EMBED ");
for (i, directive) in statement.embed.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
out.push_str(&render_embed_directive(directive));
}
}
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::CreateCollection(statement) => {
let has_vectors = !statement.vectors.is_empty() || !statement.sparse_vectors.is_empty();
let mode = render_collection_mode(&statement.mode);
let mut out = format!("CREATE COLLECTION {}", render_name(&statement.collection));
if !mode.is_empty() && (!has_vectors || statement.vectors.is_empty()) {
out.push_str(&format!(" {}", mode));
}
if has_vectors {
out.push_str(" (");
let mut defs = Vec::new();
for vector in &statement.vectors {
defs.push(render_vector_def(vector));
}
for sparse in &statement.sparse_vectors {
defs.push(render_sparse_vector_def(sparse));
}
out.push_str(&defs.join(", "));
out.push(')');
}
if let Some(config) = &statement.config {
out.push_str(&render_collection_config(config));
}
out
}
Stmt::CreateIndex(statement) => {
let mut out = format!(
"CREATE INDEX ON COLLECTION {} FOR {} TYPE {}",
render_name(&statement.collection),
render_name(&statement.field),
statement.field_type
);
if !statement.options.is_empty() {
let options: Vec<String> = statement
.options
.iter()
.map(|(key, value)| format!("{} = {}", render_name(key), render_value(value)))
.collect();
out.push_str(&format!(" WITH ({})", options.join(", ")));
}
out
}
Stmt::DropIndex(statement) => format!(
"DROP INDEX ON COLLECTION {} FOR {}",
render_name(&statement.collection),
render_name(&statement.field)
),
Stmt::CreateShardKey(statement) => {
let mut out = format!(
"CREATE SHARD KEY '{}' ON COLLECTION {}",
escape_string(&statement.shard_key),
render_name(&statement.collection)
);
let mut options = Vec::new();
if let Some(value) = statement.shards_number {
options.push(format!("shards_number = {}", value));
}
if let Some(value) = statement.replication_factor {
options.push(format!("replication_factor = {}", value));
}
if !options.is_empty() {
out.push_str(&format!(" WITH ({})", options.join(", ")));
}
out
}
Stmt::DropShardKey(statement) => format!(
"DROP SHARD KEY '{}' ON COLLECTION {}",
escape_string(&statement.shard_key),
render_name(&statement.collection)
),
Stmt::AlterCollection(statement) => {
let mut out = format!("ALTER COLLECTION {}", render_name(&statement.collection));
if let Some(config) = &statement.config {
out.push_str(&render_collection_config(config));
}
out
}
Stmt::DropCollection(statement) => {
format!("DROP COLLECTION {}", render_name(&statement.collection))
}
Stmt::ShowCollections => "SHOW COLLECTIONS".into(),
Stmt::ShowCollection(collection) => format!("SHOW COLLECTION {}", render_name(collection)),
Stmt::ShowShardKeys(collection) => {
format!("SHOW SHARD KEYS ON COLLECTION {}", render_name(collection))
}
Stmt::ShowQuotas => "SHOW QUOTAS".into(),
Stmt::SetQuota(stmt) => {
let config: Vec<String> = stmt
.config
.iter()
.map(|(key, value)| format!("{} = {}", render_name(key), render_value(value)))
.collect();
let mut out = format!("SET QUOTA ({})", config.join(", "));
if let Some(wait) = stmt.wait {
out.push_str(&format!(" WAIT {}", wait));
}
out
}
Stmt::Delete(statement) => {
let mut out = format!(
"DELETE FROM {} WHERE {}",
render_name(&statement.collection),
render_point_selector(&statement.selector)
);
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::ClearPayload(statement) => {
let mut out = format!(
"CLEAR PAYLOAD FROM {} WHERE {}",
render_name(&statement.collection),
render_point_selector(&statement.selector)
);
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::DeletePayload(statement) => {
let keys: Vec<String> = statement.keys.iter().map(|k| render_name(k)).collect();
let mut out = format!(
"DELETE PAYLOAD {} FROM {} WHERE {}",
keys.join(", "),
render_name(&statement.collection),
render_point_selector(&statement.selector)
);
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::DeleteVector(statement) => {
let names: Vec<String> = statement
.vector_names
.iter()
.map(|n| render_name(n))
.collect();
let mut out = format!(
"DELETE VECTOR {} FROM {} WHERE {}",
names.join(", "),
render_name(&statement.collection),
render_point_selector(&statement.selector)
);
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::UpdateVector(statement) => {
let mut out = format!("UPDATE {} SET VECTOR", render_name(&statement.collection));
if let Some(name) = &statement.vector_name {
out.push_str(&format!(" {}", render_name(name)));
}
out.push_str(&format!(
" = {} WHERE id = {}",
render_vector_value(&statement.vector),
render_point_id(&statement.point_id)
));
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::UpdatePayload(statement) => {
let payload: Vec<String> = statement
.payload
.iter()
.map(|(key, value)| format!("{}: {}", render_name(key), render_value(value)))
.collect();
let mut out = format!(
"UPDATE {} SET PAYLOAD = {{{}}} WHERE {}",
render_name(&statement.collection),
payload.join(", "),
render_point_selector(&statement.selector)
);
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
out
}
Stmt::Count(statement) => {
let mut out = match &statement.collection {
QueryCollection::Explicit(collection) => {
format!("COUNT FROM {}", render_name(collection))
}
QueryCollection::Inherited => "COUNT FROM".into(),
};
if let Some(filter) = &statement.filter {
out.push_str(&format!(" WHERE {}", render_filter(filter)));
}
if let Some(key) = &statement.shard_key {
out.push_str(&format!(" SHARD '{}'", escape_string(key)));
}
if let Some(exact) = statement.exact {
out.push_str(&format!(" WITH (exact = {})", exact));
}
out
}
}
}
fn render_query_body(query: &QueryStmt) -> String {
let mut out = String::new();
if !query.ctes.is_empty() {
out.push_str("WITH ");
for (i, cte) in query.ctes.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
out.push_str(&format!("{} AS (", render_name(&cte.name)));
out.push_str(&render_query_body_inner(&cte.query));
out.push(')');
}
out.push(' ');
}
out.push_str(&render_query_body_inner(query));
out
}
fn render_query_body_inner(query: &QueryStmt) -> String {
let mut out = String::from("QUERY ");
out.push_str(&render_query_expr(&query.expression));
if let QueryCollection::Explicit(collection) = &query.collection {
out.push_str(&format!(" FROM {}", render_name(collection)));
}
let tail = render_query_tail(query);
if !tail.is_empty() {
out.push(' ');
out.push_str(&tail);
}
out
}
fn render_query_tail(query: &QueryStmt) -> String {
let mut parts: Vec<String> = Vec::new();
if let Some(using) = query_expr_using(&query.expression) {
parts.push(format!("USING {}", render_vector_target(using)));
}
let prefetch = query_expr_prefetch(&query.expression);
if !prefetch.is_empty() {
parts.push(format!(
"PREFETCH ({})",
prefetch
.iter()
.map(render_prefetch)
.collect::<Vec<_>>()
.join(", ")
));
}
if let Some(filter) = &query.filter {
parts.push(format!("WHERE {}", render_filter(filter)));
}
if let Some(key) = &query.shard_key {
parts.push(format!("SHARD '{}'", escape_string(key)));
}
if let Some(params) = &query.params {
parts.push(format!("PARAMS ({})", render_search_params(params)));
}
if let Some(score) = query.score_threshold {
parts.push(format!("SCORE THRESHOLD {}", render_f64(score)));
}
if let Some(group) = &query.group {
let mut clause = format!("GROUP BY {}", render_name(&group.field));
if let Some(size) = group.size {
clause.push_str(&format!(" SIZE {}", size));
}
if let Some(lookup) = &group.lookup {
clause.push_str(&format!(" LOOKUP FROM {}", render_name(lookup)));
}
parts.push(clause);
}
if let Some(selector) = &query.output.payload {
parts.push(format!(
"WITH PAYLOAD {}",
render_payload_selector(selector)
));
}
if let Some(selector) = &query.output.vectors {
parts.push(format!("WITH VECTOR {}", render_vector_selector(selector)));
}
if let Some(limit) = query.page.limit {
parts.push(format!("LIMIT {}", limit));
}
if let Some(offset) = query.page.offset {
parts.push(format!("OFFSET {}", offset));
}
parts.join(" ")
}
fn render_query_expr(expression: &QueryExpr) -> String {
match expression {
QueryExpr::Points { ids } => format!(
"POINTS ({})",
ids.iter()
.map(render_point_id)
.collect::<Vec<_>>()
.join(", ")
),
QueryExpr::Nearest {
input,
mmr: Some(mmr),
..
} => format!(
"MMR {} DIVERSITY {} CANDIDATES {}",
render_query_input(input, false),
render_f64(mmr.diversity),
mmr.candidates
),
QueryExpr::Nearest { input, .. } => render_query_input(input, true),
QueryExpr::Recommend {
positive,
negative,
strategy,
..
} => {
let mut out = format!(
"RECOMMEND POSITIVE ({})",
positive
.iter()
.map(render_recommend_input)
.collect::<Vec<_>>()
.join(", ")
);
if !negative.is_empty() {
out.push_str(&format!(
" NEGATIVE ({})",
negative
.iter()
.map(render_recommend_input)
.collect::<Vec<_>>()
.join(", ")
));
}
if let Some(strategy) = strategy {
out.push_str(&format!(
" STRATEGY {}",
render_recommend_strategy(*strategy)
));
}
out
}
QueryExpr::Context { pairs, .. } => format!(
"CONTEXT ({})",
pairs
.iter()
.map(render_context_pair)
.collect::<Vec<_>>()
.join(", ")
),
QueryExpr::Discover {
target, context, ..
} => format!(
"DISCOVER TARGET {} CONTEXT ({})",
render_query_input(target, true),
context
.iter()
.map(render_context_pair)
.collect::<Vec<_>>()
.join(", ")
),
QueryExpr::OrderBy { field, direction } => format!(
"ORDER BY {} {}",
render_name(field),
match direction {
OrderDirection::Asc => "ASC",
OrderDirection::Desc => "DESC",
}
),
QueryExpr::SampleRandom => "SAMPLE RANDOM".into(),
QueryExpr::Fusion { method, .. } => format!("FUSION {}", render_fusion_method(*method)),
QueryExpr::Formula {
expression,
defaults,
..
} => {
let mut out = format!("FORMULA {}", render_formula(expression));
if !defaults.is_empty() {
let entries: Vec<String> = defaults
.iter()
.map(|(key, value)| format!("{} = {}", render_name(key), render_value(value)))
.collect();
out.push_str(&format!(" DEFAULTS ({})", entries.join(", ")));
}
out
}
QueryExpr::RelevanceFeedback {
target,
feedback,
strategy,
..
} => {
let items: Vec<String> = feedback
.iter()
.map(|item| {
format!(
"({}, {})",
render_query_input(&item.example, true),
render_f64(item.score)
)
})
.collect();
format!(
"RELEVANCE FEEDBACK TARGET {} FEEDBACK ({}) STRATEGY NAIVE (a = {}, b = {}, c = {})",
render_query_input(target, true),
items.join(", "),
render_f64(strategy.a),
render_f64(strategy.b),
render_f64(strategy.c)
)
}
QueryExpr::Hybrid {
text,
model,
dense_vector,
sparse_vector,
fusion,
} => {
let mut out = format!("HYBRID TEXT '{}'", escape_string(text));
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
if let Some(vector) = dense_vector {
out.push_str(&format!(" DENSE {}", render_name(vector)));
}
if let Some(vector) = sparse_vector {
out.push_str(&format!(" SPARSE {}", render_name(vector)));
}
out.push_str(&format!(" FUSION {}", render_fusion_method(*fusion)));
out
}
QueryExpr::Rerank { input, model, .. } => format!(
"RERANK {} MODEL '{}'",
render_query_input(input, false),
escape_string(model)
),
QueryExpr::CrossRerank {
query,
model,
field,
..
} => {
let mut out = format!(
"CROSS RERANK TEXT '{}' MODEL '{}'",
escape_string(query),
escape_string(model)
);
if let Some(field) = field {
out.push_str(&format!(" ON FIELD {}", render_name(field)));
}
out
}
}
}
fn query_expr_using(expression: &QueryExpr) -> Option<&VectorTarget> {
match expression {
QueryExpr::Nearest { using, .. }
| QueryExpr::Recommend { using, .. }
| QueryExpr::Context { using, .. }
| QueryExpr::Discover { using, .. }
| QueryExpr::RelevanceFeedback { using, .. }
| QueryExpr::Rerank { using, .. } => using.as_ref(),
_ => None,
}
}
fn query_expr_prefetch(expression: &QueryExpr) -> &[Prefetch] {
match expression {
QueryExpr::Nearest { prefetch, .. }
| QueryExpr::Recommend { prefetch, .. }
| QueryExpr::Context { prefetch, .. }
| QueryExpr::Discover { prefetch, .. }
| QueryExpr::Fusion { prefetch, .. }
| QueryExpr::Formula { prefetch, .. }
| QueryExpr::RelevanceFeedback { prefetch, .. }
| QueryExpr::Rerank { prefetch, .. }
| QueryExpr::CrossRerank { prefetch, .. } => prefetch,
_ => &[],
}
}
fn render_prefetch(prefetch: &Prefetch) -> String {
let mut out = match &prefetch.source {
PrefetchSource::Cte(name) => render_name(name),
PrefetchSource::Query(query) => render_query_body_inner(query),
};
if let Some(filter) = &prefetch.filter {
out.push_str(&format!(" WHERE {}", render_filter(filter)));
}
if let Some(score) = prefetch.score_threshold {
out.push_str(&format!(" SCORE THRESHOLD {}", render_f64(score)));
}
if let Some(lookup) = &prefetch.lookup {
out.push_str(&format!(" LOOKUP FROM {}", render_name(&lookup.collection)));
if let Some(vector) = &lookup.vector {
out.push_str(&format!(" VECTOR {}", render_name(vector)));
}
}
out
}
fn render_query_input(input: &QueryInput, allow_bare: bool) -> String {
match input {
QueryInput::Text { text, model: None } if allow_bare => {
format!("'{}'", escape_string(text))
}
QueryInput::Text { text, model } => {
let mut out = format!("TEXT '{}'", escape_string(text));
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
out
}
QueryInput::Image { source, model } => {
let mut out = format!("IMAGE '{}'", escape_string(source));
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
out
}
QueryInput::Vector(value) => format!("VECTOR {}", render_vector_value(value)),
QueryInput::Point(point) => format!("POINT {}", render_point_id(point)),
}
}
fn render_recommend_input(input: &QueryInput) -> String {
match input {
QueryInput::Point(point) => render_point_id(point),
other => render_query_input(other, true),
}
}
fn render_context_pair(pair: &ContextPair) -> String {
format!(
"POSITIVE {} NEGATIVE {}",
render_query_input(&pair.positive, true),
render_query_input(&pair.negative, true)
)
}
fn render_vector_target(target: &VectorTarget) -> String {
let mut out = render_name(&target.name);
if target.multi {
out.push_str(" AS MULTI");
} else if let Some(kind) = target.kind {
out.push_str(match kind {
VectorKind::Dense => " AS DENSE",
VectorKind::Sparse => " AS SPARSE",
});
}
out
}
fn render_recommend_strategy(strategy: RecommendStrategy) -> &'static str {
match strategy {
RecommendStrategy::AverageVector => "average_vector",
RecommendStrategy::BestScore => "best_score",
RecommendStrategy::SumScores => "sum_scores",
}
}
fn render_fusion_method(method: FusionMethod) -> &'static str {
match method {
FusionMethod::Rrf => "RRF",
FusionMethod::Dbsf => "DBSF",
}
}
pub(crate) fn render_search_params(params: &SearchParams) -> String {
let mut parts = Vec::new();
if let Some(value) = params.hnsw_ef {
parts.push(format!("hnsw_ef = {}", value));
}
if let Some(value) = params.exact {
parts.push(format!("exact = {}", value));
}
if let Some(value) = params.acorn {
parts.push(format!("acorn = {}", value));
}
if let Some(value) = params.max_selectivity {
parts.push(format!("max_selectivity = {}", render_f64(value)));
}
if let Some(value) = params.indexed_only {
parts.push(format!("indexed_only = {}", value));
}
if let Some(value) = params.rrf_k {
parts.push(format!("rrf_k = {}", value));
}
if let Some(values) = ¶ms.rrf_weights {
parts.push(format!(
"rrf_weights = [{}]",
values
.iter()
.map(|v| render_f64(*v))
.collect::<Vec<_>>()
.join(", ")
));
}
if let Some(quantization) = ¶ms.quantization {
let mut entries = Vec::new();
if let Some(value) = quantization.ignore {
entries.push(format!("ignore: {}", value));
}
if let Some(value) = quantization.rescore {
entries.push(format!("rescore: {}", value));
}
if let Some(value) = quantization.oversampling {
entries.push(format!("oversampling: {}", render_f64(value)));
}
parts.push(format!("quantization = {{{}}}", entries.join(", ")));
}
if let Some(idf) = ¶ms.idf {
match &idf.corpus {
None => parts.push("idf = 'global'".into()),
Some(filter) => parts.push(format!("idf = WHERE {}", render_filter(filter))),
}
}
if let Some(value) = params.timeout {
parts.push(format!("timeout = {}", value));
}
if let Some(consistency) = ¶ms.consistency {
parts.push(format!(
"consistency = {}",
render_read_consistency(consistency)
));
}
parts.join(", ")
}
fn render_read_consistency(consistency: &ReadConsistency) -> String {
match consistency {
ReadConsistency::Factor(value) => value.to_string(),
ReadConsistency::Majority => "majority".into(),
ReadConsistency::Quorum => "quorum".into(),
ReadConsistency::All => "all".into(),
}
}
fn render_payload_selector(selector: &PayloadSelector) -> String {
match selector {
PayloadSelector::All => "true".into(),
PayloadSelector::None => "false".into(),
PayloadSelector::Include(names) => format!(
"INCLUDE ({})",
names
.iter()
.map(|n| render_name(n))
.collect::<Vec<_>>()
.join(", ")
),
PayloadSelector::Exclude(names) => format!(
"EXCLUDE ({})",
names
.iter()
.map(|n| render_name(n))
.collect::<Vec<_>>()
.join(", ")
),
}
}
fn render_vector_selector(selector: &VectorSelector) -> String {
match selector {
VectorSelector::All => "true".into(),
VectorSelector::None => "false".into(),
VectorSelector::Names(names) => format!(
"({})",
names
.iter()
.map(|n| render_name(n))
.collect::<Vec<_>>()
.join(", ")
),
}
}
pub(crate) fn render_filter(filter: &FilterExpr) -> String {
match filter {
FilterExpr::And { operands } => operands
.iter()
.map(|operand| {
if matches!(operand, FilterExpr::And { .. } | FilterExpr::Or { .. }) {
format!("({})", render_filter(operand))
} else {
render_filter(operand)
}
})
.collect::<Vec<_>>()
.join(" AND "),
FilterExpr::Or { operands } => operands
.iter()
.map(|operand| {
if matches!(operand, FilterExpr::Or { .. }) {
format!("({})", render_filter(operand))
} else {
render_filter(operand)
}
})
.collect::<Vec<_>>()
.join(" OR "),
FilterExpr::Not { operand } => {
if matches!(
operand.as_ref(),
FilterExpr::And { .. } | FilterExpr::Or { .. }
) {
format!("NOT ({})", render_filter(operand))
} else {
format!("NOT {}", render_filter(operand))
}
}
predicate => render_filter_predicate(predicate),
}
}
fn render_filter_predicate(filter: &FilterExpr) -> String {
match filter {
FilterExpr::PointId(PointIdPredicate::Eq(point)) => {
format!("id = {}", render_point_id(point))
}
FilterExpr::PointId(PointIdPredicate::In(points)) => format!(
"id IN ({})",
points
.iter()
.map(render_point_id)
.collect::<Vec<_>>()
.join(", ")
),
FilterExpr::Compare { field, op, value } => format!(
"{} {} {}",
render_name(field),
render_comparison_op(*op),
render_value(value)
),
FilterExpr::Between { field, low, high } => format!(
"{} BETWEEN {} AND {}",
render_name(field),
render_value(low),
render_value(high)
),
FilterExpr::In { field, values } => format!(
"{} IN ({})",
render_name(field),
values
.iter()
.map(render_value)
.collect::<Vec<_>>()
.join(", ")
),
FilterExpr::IsNull { field } => format!("{} IS NULL", render_name(field)),
FilterExpr::IsEmpty { field } => format!("{} IS EMPTY", render_name(field)),
FilterExpr::MatchText { field, text } => {
format!("{} MATCH '{}'", render_name(field), escape_string(text))
}
FilterExpr::MatchAny { field, values } => format!(
"{} MATCH ANY ({})",
render_name(field),
values
.iter()
.map(render_value)
.collect::<Vec<_>>()
.join(", ")
),
FilterExpr::MatchPhrase { field, text } => format!(
"{} MATCH PHRASE '{}'",
render_name(field),
escape_string(text)
),
FilterExpr::MatchPrefix { field, prefix } => format!(
"{} MATCH PREFIX '{}'",
render_name(field),
escape_string(prefix)
),
FilterExpr::Nested { path, filter } => format!(
"NESTED('{}', {})",
escape_string(path),
render_filter(filter)
),
FilterExpr::HasVector { name } => format!("HAS_VECTOR {}", render_name(name)),
FilterExpr::Slice { total, index } => format!("SLICE ({}, {})", total, index),
FilterExpr::ValuesCount { field, op, count } => format!(
"{} VALUES_COUNT {} {}",
render_name(field),
render_comparison_op(*op),
count
),
FilterExpr::GeoBoundingBox {
field,
top_left,
bottom_right,
} => format!(
"{} GEO_BBOX {{top_left: {{lat: {}, lon: {}}}, bottom_right: {{lat: {}, lon: {}}}}}",
render_name(field),
render_f64(top_left.lat),
render_f64(top_left.lon),
render_f64(bottom_right.lat),
render_f64(bottom_right.lon)
),
FilterExpr::GeoRadius {
field,
center,
radius,
} => format!(
"{} GEO_RADIUS {{center: {{lat: {}, lon: {}}}, radius: {}}}",
render_name(field),
render_f64(center.lat),
render_f64(center.lon),
render_f64(*radius)
),
FilterExpr::GeoPolygon {
field,
exterior,
interiors,
} => {
let mut out = format!(
"{} GEO_POLYGON {{exterior: [{}]",
render_name(field),
render_geo_ring(exterior)
);
if !interiors.is_empty() {
out.push_str(&format!(
", interiors: [{}]",
interiors
.iter()
.map(|ring| format!("[{}]", render_geo_ring(ring)))
.collect::<Vec<_>>()
.join(", ")
));
}
out.push('}');
out
}
_ => render_filter(filter),
}
}
fn render_geo_ring(points: &[GeoPoint]) -> String {
points
.iter()
.map(|point| {
format!(
"{{lat: {}, lon: {}}}",
render_f64(point.lat),
render_f64(point.lon)
)
})
.collect::<Vec<_>>()
.join(", ")
}
fn render_comparison_op(op: ComparisonOp) -> &'static str {
match op {
ComparisonOp::Eq => "=",
ComparisonOp::Gt => ">",
ComparisonOp::Gte => ">=",
ComparisonOp::Lt => "<",
ComparisonOp::Lte => "<=",
}
}
fn render_formula(formula: &FormulaExpr) -> String {
render_formula_min(formula, 0)
}
fn formula_precedence(formula: &FormulaExpr) -> u8 {
match formula {
FormulaExpr::Sum { .. } | FormulaExpr::Sub { .. } => 1,
FormulaExpr::Mul { .. } | FormulaExpr::Div { .. } => 2,
FormulaExpr::Neg { .. } => 3,
_ => 4,
}
}
fn render_formula_min(formula: &FormulaExpr, min_precedence: u8) -> String {
let precedence = formula_precedence(formula);
let rendered = match formula {
FormulaExpr::Constant { value } => render_f64(*value),
FormulaExpr::Variable { name } => name.clone(),
FormulaExpr::Sum { left, right } => format!(
"{} + {}",
render_formula_min(left, 1),
render_formula_min(right, 2)
),
FormulaExpr::Sub { left, right } => format!(
"{} - {}",
render_formula_min(left, 1),
render_formula_min(right, 2)
),
FormulaExpr::Mul { left, right } => format!(
"{} * {}",
render_formula_min(left, 2),
render_formula_min(right, 3)
),
FormulaExpr::Div {
left,
right,
by_zero_default,
} => {
let mut out = format!(
"{} / {}",
render_formula_min(left, 2),
render_formula_min(right, 3)
);
if let Some(default) = by_zero_default {
out.push_str(&format!(" [DEFAULT = {}]", render_f64(*default)));
}
out
}
FormulaExpr::Neg { operand } => format!("-{}", render_formula_min(operand, 3)),
FormulaExpr::Abs { x } => format!("ABS({})", render_formula_min(x, 0)),
FormulaExpr::Sqrt { x } => format!("SQRT({})", render_formula_min(x, 0)),
FormulaExpr::Log { x } => format!("LOG({})", render_formula_min(x, 0)),
FormulaExpr::Ln { x } => format!("LN({})", render_formula_min(x, 0)),
FormulaExpr::Exp { x } => format!("EXP({})", render_formula_min(x, 0)),
FormulaExpr::Pow { base, exponent } => format!(
"POW({}, {})",
render_formula_min(base, 0),
render_formula_min(exponent, 0)
),
FormulaExpr::GeoDistance { lat, lon, field } => format!(
"GEO_DISTANCE({}, {}, {})",
render_f64(*lat),
render_f64(*lon),
render_name(field)
),
FormulaExpr::Decay {
kind,
x,
target,
scale,
midpoint,
} => {
let mut out = format!("{}(", kind.to_ascii_uppercase());
out.push_str(&render_formula_min(x, 0));
if let Some(target) = target {
out.push_str(&format!(", TARGET = {}", render_formula_min(target, 0)));
}
if let Some(scale) = scale {
out.push_str(&format!(", SCALE = {}", render_f64(*scale)));
}
if let Some(midpoint) = midpoint {
out.push_str(&format!(", MIDPOINT = {}", render_f64(*midpoint)));
}
out.push(')');
out
}
FormulaExpr::Case { cond, then_, else_ } => format!(
"CASE WHEN {} THEN {} ELSE {} END",
render_filter(cond),
render_formula_min(then_, 0),
render_formula_min(else_, 0)
),
FormulaExpr::MatchCondition { field, values } => {
if values.len() == 1 {
format!(
"MATCH({}, {})",
render_name(field),
render_value(&values[0])
)
} else {
format!(
"MATCH({}, [{}])",
render_name(field),
values
.iter()
.map(render_value)
.collect::<Vec<_>>()
.join(", ")
)
}
}
FormulaExpr::Datetime { value } => format!("datetime('{}')", escape_string(value)),
FormulaExpr::DatetimeKey { key } => format!("datetime_key('{}')", escape_string(key)),
};
if precedence < min_precedence {
format!("({})", rendered)
} else {
rendered
}
}
fn render_collection_config(config: &CollectionConfig) -> String {
let mut out = String::new();
if let Some(hnsw) = &config.hnsw {
if let Some(body) = render_hnsw_block(hnsw) {
out.push_str(&format!(" WITH HNSW ({})", body));
}
}
if let Some(vectors) = &config.vectors {
if let Some(body) = render_vectors_options(vectors) {
out.push_str(&format!(" WITH VECTOR ({})", body));
}
}
if let Some(optimizers) = &config.optimizers {
if let Some(body) = render_optimizers_block(optimizers) {
out.push_str(&format!(" WITH OPTIMIZERS ({})", body));
}
}
if let Some(params) = &config.params {
if let Some(body) = render_params_block(params) {
out.push_str(&format!(" WITH PARAMS ({})", body));
}
}
if let Some(quantization) = &config.quantization {
if let Some(body) = render_quantization_block(quantization) {
out.push_str(&format!(" WITH QUANTIZATION ({})", body));
}
}
if let Some(update) = &config.quantization_update {
if update.disabled {
out.push_str(" WITH QUANTIZATION (disabled = true)");
} else if config.quantization.is_none() {
if let Some(config) = &update.config {
if let Some(body) = render_quantization_block(config) {
out.push_str(&format!(" WITH QUANTIZATION ({})", body));
}
}
}
}
out
}
fn render_collection_mode(mode: &CollectionMode) -> String {
match mode {
CollectionMode::Dense { model: Some(model) } => {
format!("USING DENSE MODEL '{}'", escape_string(model))
}
CollectionMode::Dense { model: None } => String::new(),
CollectionMode::Hybrid {
dense_vector,
sparse_vector,
} => {
let mut out = String::from("HYBRID");
if let Some(vector) = dense_vector {
out.push_str(&format!(" DENSE VECTOR {}", render_name(vector)));
}
if let Some(vector) = sparse_vector {
out.push_str(&format!(" SPARSE VECTOR {}", render_name(vector)));
}
out
}
CollectionMode::Rerank => "HYBRID RERANK".into(),
}
}
fn render_vector_def(vector: &VectorDef) -> String {
let mut out = format!(
"{} VECTOR({}, {})",
render_name(&vector.name),
vector.size,
render_distance(vector.distance)
);
if let Some(hnsw) = &vector.hnsw {
if let Some(body) = render_hnsw_block(hnsw) {
out.push_str(&format!(" WITH HNSW ({})", body));
}
}
if let Some(quantization) = &vector.quantization {
if let Some(body) = render_quantization_block(quantization) {
out.push_str(&format!(" WITH QUANTIZATION ({})", body));
}
}
if let Some(multivector) = &vector.multivector {
out.push_str(&format!(
" WITH MULTIVECTOR (comparator = '{}')",
match multivector.comparator {
MultivectorComparator::MaxSim => "max_sim",
}
));
}
if let Some(vectors) = &vector.vectors {
if let Some(body) = render_vectors_options(vectors) {
out.push_str(&format!(" WITH VECTOR ({})", body));
}
}
out
}
fn render_vectors_options(vectors: &VectorsConfig) -> Option<String> {
let mut options = Vec::new();
if let Some(value) = vectors.on_disk {
options.push(format!("on_disk = {}", value));
}
if let Some(value) = vectors.memory {
options.push(format!("memory = '{}'", value.as_str()));
}
if let Some(value) = vectors.datatype {
options.push(format!("datatype = '{}'", value.as_str()));
}
if options.is_empty() {
None
} else {
Some(options.join(", "))
}
}
fn render_sparse_vector_def(vector: &SparseVectorDef) -> String {
let mut out = format!("{} SPARSE", render_name(&vector.name));
let mut options = Vec::new();
if let Some(modifier) = &vector.modifier {
options.push(format!(
"modifier = '{}'",
escape_string(&modifier.to_ascii_lowercase())
));
}
if let Some(index) = &vector.index {
if let Some(value) = index.full_scan_threshold {
options.push(format!("full_scan_threshold = {}", value));
}
if let Some(value) = index.on_disk {
options.push(format!("on_disk = {}", value));
}
if let Some(value) = index.datatype {
options.push(format!("datatype = '{}'", value.as_str()));
}
if let Some(value) = index.memory {
options.push(format!("memory = '{}'", value.as_str()));
}
}
if !options.is_empty() {
out.push_str(&format!(" WITH SPARSE ({})", options.join(", ")));
}
out
}
fn render_hnsw_block(hnsw: &HnswRuntimeConfig) -> Option<String> {
let mut options = Vec::new();
if let Some(value) = hnsw.m {
options.push(format!("m = {}", value));
}
if let Some(value) = hnsw.ef_construct {
options.push(format!("ef_construct = {}", value));
}
if let Some(value) = hnsw.full_scan_threshold {
options.push(format!("full_scan_threshold = {}", value));
}
if let Some(value) = hnsw.max_indexing_threads {
options.push(format!("max_indexing_threads = {}", value));
}
if let Some(value) = hnsw.on_disk {
options.push(format!("on_disk = {}", value));
}
if let Some(value) = hnsw.payload_m {
options.push(format!("payload_m = {}", value));
}
if let Some(value) = hnsw.inline_storage {
options.push(format!("inline_storage = {}", value));
}
if let Some(value) = hnsw.memory {
options.push(format!("memory = '{}'", value.as_str()));
}
if options.is_empty() {
None
} else {
Some(options.join(", "))
}
}
fn render_optimizers_block(optimizers: &OptimizersRuntimeConfig) -> Option<String> {
let mut options = Vec::new();
if let Some(value) = optimizers.deleted_threshold {
options.push(format!("deleted_threshold = {}", render_f64(value)));
}
if let Some(value) = optimizers.vacuum_min_vector_number {
options.push(format!("vacuum_min_vector_number = {}", value));
}
if let Some(value) = optimizers.default_segment_number {
options.push(format!("default_segment_number = {}", value));
}
if let Some(value) = optimizers.max_segment_size {
options.push(format!("max_segment_size = {}", value));
}
if let Some(value) = optimizers.memmap_threshold {
options.push(format!("memmap_threshold = {}", value));
}
if let Some(value) = optimizers.indexing_threshold {
options.push(format!("indexing_threshold = {}", value));
}
if let Some(value) = optimizers.flush_interval_sec {
options.push(format!("flush_interval_sec = {}", value));
}
if let Some(threads) = &optimizers.max_optimization_threads {
if threads.auto_ {
options.push("max_optimization_threads = 'auto'".into());
} else {
options.push(format!("max_optimization_threads = {}", threads.value));
}
}
if let Some(value) = optimizers.prevent_unoptimized {
options.push(format!("prevent_unoptimized = {}", value));
}
if options.is_empty() {
None
} else {
Some(options.join(", "))
}
}
fn render_params_block(params: &CollectionParamsConfig) -> Option<String> {
let mut options = Vec::new();
if let Some(value) = params.replication_factor {
options.push(format!("replication_factor = {}", value));
}
if let Some(value) = params.write_consistency_factor {
options.push(format!("write_consistency_factor = {}", value));
}
if let Some(value) = params.read_fan_out_factor {
options.push(format!("read_fan_out_factor = {}", value));
}
if let Some(value) = params.read_fan_out_delay_ms {
options.push(format!("read_fan_out_delay_ms = {}", value));
}
if let Some(value) = params.on_disk_payload {
options.push(format!("on_disk_payload = {}", value));
}
if let Some(value) = params.payload_memory {
options.push(format!("payload_memory = '{}'", value.as_str()));
}
if let Some(value) = params.shard_number {
options.push(format!("shard_number = {}", value));
}
if let Some(value) = ¶ms.sharding_method {
options.push(format!(
"sharding_method = '{}'",
escape_string(&value.to_ascii_lowercase())
));
}
if let Some(values) = ¶ms.shard_keys {
options.push(format!(
"shard_keys = [{}]",
values
.iter()
.map(|s| format!("'{}'", escape_string(s)))
.collect::<Vec<_>>()
.join(", ")
));
}
if options.is_empty() {
None
} else {
Some(options.join(", "))
}
}
fn render_quantization_block(quantization: &QuantizationConfig) -> Option<String> {
let mut options = vec![format!(
"type = '{}'",
render_quantization_type(quantization.qtype)
)];
if quantization.always_ram {
options.push("always_ram = true".into());
}
if let Some(value) = quantization.quantile {
options.push(format!("quantile = {}", render_f64(value)));
}
if let Some(value) = quantization.bits {
options.push(format!("bits = {}", render_f64(value)));
}
if let Some(value) = &quantization.compression {
options.push(format!("compression = '{}'", escape_string(value)));
}
if let Some(value) = &quantization.encoding {
options.push(format!("encoding = '{}'", escape_string(value)));
}
if let Some(value) = &quantization.query_encoding {
options.push(format!("query_encoding = '{}'", escape_string(value)));
}
if let Some(value) = quantization.memory {
options.push(format!("memory = '{}'", value.as_str()));
}
Some(options.join(", "))
}
fn render_quantization_type(kind: QuantizationType) -> &'static str {
match kind {
QuantizationType::Scalar => "scalar",
QuantizationType::Binary => "binary",
QuantizationType::Product => "product",
QuantizationType::Turbo => "turbo",
}
}
fn render_distance(distance: VectorDistance) -> &'static str {
match distance {
VectorDistance::Cosine => "COSINE",
VectorDistance::Dot => "DOT",
VectorDistance::Euclid => "EUCLID",
VectorDistance::Manhattan => "MANHATTAN",
}
}
fn render_point_selector(selector: &PointSelector) -> String {
match selector {
PointSelector::Id(point) => format!("id = {}", render_point_id(point)),
PointSelector::Ids(points) => format!(
"id IN ({})",
points
.iter()
.map(render_point_id)
.collect::<Vec<_>>()
.join(", ")
),
PointSelector::Filter(filter) => render_filter(filter),
}
}
fn render_point(point: &UpsertPoint) -> String {
let mut parts = vec![format!("id: {}", render_point_id(&point.id))];
if let Some(vectors) = &point.vectors {
parts.push(format!("vector: {}", render_point_vectors(vectors)));
}
for (key, value) in &point.payload {
parts.push(format!("{}: {}", render_name(key), render_value(value)));
}
format!("{{{}}}", parts.join(", "))
}
fn render_point_vectors(vectors: &PointVectors) -> String {
match vectors {
PointVectors::Unnamed(value) => render_vector_value(value),
PointVectors::Named(pairs) => {
let entries: Vec<String> = pairs
.iter()
.map(|(name, value)| {
format!("{}: {}", render_name(name), render_vector_value(value))
})
.collect();
format!("{{{}}}", entries.join(", "))
}
}
}
fn render_point_id(point: &PointId) -> String {
match point {
PointId::Number(value) => value.to_string(),
PointId::String(value) => format!("'{}'", escape_string(value)),
}
}
fn render_vector_value(value: &VectorValue) -> String {
match value {
VectorValue::Dense(values) => format!(
"[{}]",
values
.iter()
.map(|v| render_f64(*v as f64))
.collect::<Vec<_>>()
.join(", ")
),
VectorValue::Sparse { indices, values } => format!(
"{{indices: [{}], values: [{}]}}",
indices
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(", "),
values
.iter()
.map(|v| render_f64(*v as f64))
.collect::<Vec<_>>()
.join(", ")
),
VectorValue::MultiDense(rows) => format!(
"[{}]",
rows.iter()
.map(|row| format!(
"[{}]",
row.iter()
.map(|v| render_f64(*v as f64))
.collect::<Vec<_>>()
.join(", ")
))
.collect::<Vec<_>>()
.join(", ")
),
}
}
fn render_value(value: &Value) -> String {
match value {
Value::Str(value) => format!("'{}'", escape_string(value)),
Value::Int(value) => value.to_string(),
Value::Float(value) => render_f64(*value),
Value::Bool(value) => value.to_string(),
Value::Null => "null".into(),
Value::Dict(entries) => {
let items: Vec<String> = entries
.iter()
.map(|(key, value)| format!("{}: {}", render_name(key), render_value(value)))
.collect();
format!("{{{}}}", items.join(", "))
}
Value::List(items) => {
let values: Vec<String> = items.iter().map(render_value).collect();
format!("[{}]", values.join(", "))
}
}
}
fn render_f64(value: f64) -> String {
if value.fract() == 0.0 && value.abs() < 1e15 {
format!("{:.1}", value)
} else {
let rendered = value.to_string();
if rendered.contains('.') || rendered.contains('e') || rendered.contains('E') {
rendered
} else {
format!("{}.0", rendered)
}
}
}
fn render_embedding_spec(spec: &EmbeddingSpec) -> String {
match spec {
EmbeddingSpec::Dense {
model,
vector,
field,
} => render_embedding_spec_part("DENSE", model, vector, field, false),
EmbeddingSpec::Sparse {
model,
vector,
field,
} => render_embedding_spec_part("SPARSE", model, vector, field, false),
EmbeddingSpec::MultiVector {
model,
vector,
field,
} => render_embedding_spec_part("MULTIVECTOR", model, vector, field, false),
EmbeddingSpec::Image {
model,
vector,
field,
} => render_embedding_spec_part("IMAGE", model, vector, field, false),
EmbeddingSpec::Hybrid {
dense_model,
dense_vector,
dense_field,
sparse_model,
sparse_vector,
sparse_field,
} => {
let mut parts = vec!["HYBRID".to_string()];
if dense_model.is_some() || dense_vector.is_some() || dense_field.is_some() {
parts.push(render_embedding_spec_part(
"DENSE",
dense_model,
dense_vector,
dense_field,
true,
));
}
if sparse_model.is_some() || sparse_vector.is_some() || sparse_field.is_some() {
parts.push(render_embedding_spec_part(
"SPARSE",
sparse_model,
sparse_vector,
sparse_field,
true,
));
}
parts.join(" ")
}
EmbeddingSpec::Multi(specs) => specs
.iter()
.map(render_embedding_spec)
.collect::<Vec<_>>()
.join(", "),
}
}
fn render_embedding_spec_part(
kind: &str,
model: &Option<String>,
vector: &Option<String>,
field: &Option<String>,
hybrid: bool,
) -> String {
let mut parts = vec![kind.to_string()];
if let Some(model) = model {
parts.push(format!("MODEL '{}'", escape_string(model)));
}
if let Some(field) = field {
parts.push(format!("ON FIELD {}", render_name(field)));
}
if let Some(vector) = vector {
parts.push(format!(
"{} {}",
if hybrid { "VECTOR" } else { "INTO" },
render_name(vector)
));
}
parts.join(" ")
}
fn render_embed_directive(directive: &EmbedDirective) -> String {
let mut out = format!(
"{} INTO {} USING ",
render_name(&directive.source_field),
render_name(&directive.target_vector)
);
match &directive.kind {
EmbedKind::Dense { model } => {
out.push_str("DENSE");
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
}
EmbedKind::Sparse { model } => {
out.push_str("SPARSE");
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
}
EmbedKind::Multi { model } => {
out.push_str("MULTI");
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
}
EmbedKind::Image { model } => {
out.push_str("IMAGE");
if let Some(model) = model {
out.push_str(&format!(" MODEL '{}'", escape_string(model)));
}
}
}
out
}
fn render_name(name: &str) -> String {
if is_simple_ident(name) {
name.to_string()
} else {
format!("'{}'", escape_string(name))
}
}
fn is_simple_ident(name: &str) -> bool {
let mut chars = name.chars();
match chars.next() {
Some(c) if c.is_ascii_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
fn escape_string(value: &str) -> String {
let mut out = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'\\' => out.push_str("\\\\"),
'\'' => out.push_str("\\'"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
'\0' => {}
c => out.push(c),
}
}
out
}