Skip to content

Commit 0d44806

Browse files
authored
Merge pull request #172 from AdaWorldAPI/claude/review-lance-graph-architecture-i6TKf
fix: kill serde_json from lance_parser — Python is dead
2 parents 788bfed + 1a5a993 commit 0d44806

3 files changed

Lines changed: 58 additions & 49 deletions

File tree

src/query/lance_parser/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
// crate::graph::spo, or any mesh-side module. If you find yourself adding one, STOP.
99

1010
pub mod ast;
11+
pub mod case_insensitive;
12+
pub mod config;
1113
pub mod error;
14+
pub mod parameter_substitution;
1215
pub mod parser;
16+
pub mod semantic;
1317

14-
// Re-export the main entry point
18+
// Re-export the main entry points
1519
pub use ast::*;
1620
pub use error::{GraphError, Result};
21+
pub use parameter_substitution::ParamValue;
1722
pub use parser::parse_cypher_query;

src/query/lance_parser/parameter_substitution.rs

Lines changed: 49 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,23 @@ use super::ast::*;
22
use super::super::error::{QueryError, Result};
33
use std::collections::HashMap;
44

5+
/// Query parameter value — no JSON, no Python, no serialization.
6+
/// Comes from MCP tool input or BindSpace property lookup.
7+
///
8+
/// If a parameter isn't set, it's not in the HashMap. No Null variant.
9+
#[derive(Debug, Clone, PartialEq)]
10+
pub enum ParamValue {
11+
String(String),
12+
Int(i64),
13+
Float(f64),
14+
Bool(bool),
15+
List(Vec<ParamValue>),
16+
}
17+
518
/// Substitute parameters with literal values in the AST
619
pub fn substitute_parameters(
720
query: &mut CypherQuery,
8-
parameters: &HashMap<String, serde_json::Value>,
21+
parameters: &HashMap<String, ParamValue>,
922
) -> Result<()> {
1023
// Substitute in READING clauses
1124
for reading_clause in &mut query.reading_clauses {
@@ -45,7 +58,7 @@ pub fn substitute_parameters(
4558

4659
fn substitute_in_reading_clause(
4760
clause: &mut ReadingClause,
48-
parameters: &HashMap<String, serde_json::Value>,
61+
parameters: &HashMap<String, ParamValue>,
4962
) -> Result<()> {
5063
match clause {
5164
ReadingClause::Match(match_clause) => {
@@ -62,7 +75,7 @@ fn substitute_in_reading_clause(
6275

6376
fn substitute_in_graph_pattern(
6477
pattern: &mut GraphPattern,
65-
parameters: &HashMap<String, serde_json::Value>,
78+
parameters: &HashMap<String, ParamValue>,
6679
) -> Result<()> {
6780
match pattern {
6881
GraphPattern::Node(node) => {
@@ -83,7 +96,7 @@ fn substitute_in_graph_pattern(
8396

8497
fn substitute_in_node_pattern(
8598
node: &mut NodePattern,
86-
parameters: &HashMap<String, serde_json::Value>,
99+
parameters: &HashMap<String, ParamValue>,
87100
) -> Result<()> {
88101
for value in node.properties.values_mut() {
89102
substitute_in_property_value(value, parameters)?;
@@ -93,7 +106,7 @@ fn substitute_in_node_pattern(
93106

94107
fn substitute_in_relationship_pattern(
95108
rel: &mut RelationshipPattern,
96-
parameters: &HashMap<String, serde_json::Value>,
109+
parameters: &HashMap<String, ParamValue>,
97110
) -> Result<()> {
98111
for value in rel.properties.values_mut() {
99112
substitute_in_property_value(value, parameters)?;
@@ -103,7 +116,7 @@ fn substitute_in_relationship_pattern(
103116

104117
fn substitute_in_property_value(
105118
value: &mut PropertyValue,
106-
parameters: &HashMap<String, serde_json::Value>,
119+
parameters: &HashMap<String, ParamValue>,
107120
) -> Result<()> {
108121
if let PropertyValue::Parameter(name) = value {
109122
let param_value =
@@ -114,21 +127,21 @@ fn substitute_in_property_value(
114127
location: snafu::Location::new(file!(), line!(), column!()),
115128
})?;
116129

117-
*value = json_to_property_value(param_value)?;
130+
*value = param_to_property_value(param_value)?;
118131
}
119132
Ok(())
120133
}
121134

122135
fn substitute_in_where_clause(
123136
where_clause: &mut WhereClause,
124-
parameters: &HashMap<String, serde_json::Value>,
137+
parameters: &HashMap<String, ParamValue>,
125138
) -> Result<()> {
126139
substitute_in_boolean_expression(&mut where_clause.expression, parameters)
127140
}
128141

129142
fn substitute_in_with_clause(
130143
with_clause: &mut WithClause,
131-
parameters: &HashMap<String, serde_json::Value>,
144+
parameters: &HashMap<String, ParamValue>,
132145
) -> Result<()> {
133146
for item in &mut with_clause.items {
134147
substitute_in_value_expression(&mut item.expression, parameters)?;
@@ -141,7 +154,7 @@ fn substitute_in_with_clause(
141154

142155
fn substitute_in_return_clause(
143156
return_clause: &mut ReturnClause,
144-
parameters: &HashMap<String, serde_json::Value>,
157+
parameters: &HashMap<String, ParamValue>,
145158
) -> Result<()> {
146159
for item in &mut return_clause.items {
147160
substitute_in_value_expression(&mut item.expression, parameters)?;
@@ -151,7 +164,7 @@ fn substitute_in_return_clause(
151164

152165
fn substitute_in_order_by_clause(
153166
order_by: &mut OrderByClause,
154-
parameters: &HashMap<String, serde_json::Value>,
167+
parameters: &HashMap<String, ParamValue>,
155168
) -> Result<()> {
156169
for item in &mut order_by.items {
157170
substitute_in_value_expression(&mut item.expression, parameters)?;
@@ -161,7 +174,7 @@ fn substitute_in_order_by_clause(
161174

162175
fn substitute_in_boolean_expression(
163176
expr: &mut BooleanExpression,
164-
parameters: &HashMap<String, serde_json::Value>,
177+
parameters: &HashMap<String, ParamValue>,
165178
) -> Result<()> {
166179
match expr {
167180
BooleanExpression::Comparison { left, right, .. } => {
@@ -197,7 +210,7 @@ fn substitute_in_boolean_expression(
197210

198211
fn substitute_in_value_expression(
199212
expr: &mut ValueExpression,
200-
parameters: &HashMap<String, serde_json::Value>,
213+
parameters: &HashMap<String, ParamValue>,
201214
) -> Result<()> {
202215
match expr {
203216
ValueExpression::Parameter(name) => {
@@ -209,28 +222,30 @@ fn substitute_in_value_expression(
209222
location: snafu::Location::new(file!(), line!(), column!()),
210223
})?;
211224

212-
// Check for array to VectorLiteral conversion
213-
if let serde_json::Value::Array(arr) = param_value {
225+
// Check for float list → VectorLiteral conversion
226+
if let ParamValue::List(items) = param_value {
214227
let mut floats = Vec::new();
215-
for v in arr {
216-
if let Some(f) = v.as_f64() {
217-
floats.push(f as f32);
218-
} else {
219-
return Err(QueryError::PlanError {
220-
message: format!(
221-
"Parameter ${} is a list but contains non-numeric values. Only float vectors are supported as list parameters currently.",
222-
name
223-
),
224-
location: snafu::Location::new(file!(), line!(), column!()),
225-
});
228+
for v in items {
229+
match v {
230+
ParamValue::Float(f) => floats.push(*f as f32),
231+
ParamValue::Int(i) => floats.push(*i as f32),
232+
_ => {
233+
return Err(QueryError::PlanError {
234+
message: format!(
235+
"Parameter ${} is a list but contains non-numeric values. Only float vectors are supported as list parameters currently.",
236+
name
237+
),
238+
location: snafu::Location::new(file!(), line!(), column!()),
239+
});
240+
}
226241
}
227242
}
228243
*expr = ValueExpression::VectorLiteral(floats);
229244
return Ok(());
230245
}
231246

232247
// Scalar conversion
233-
let prop_val = json_to_property_value(param_value)?;
248+
let prop_val = param_to_property_value(param_value)?;
234249
*expr = ValueExpression::Literal(prop_val);
235250
}
236251
ValueExpression::ScalarFunction { args, .. }
@@ -253,26 +268,15 @@ fn substitute_in_value_expression(
253268
Ok(())
254269
}
255270

256-
fn json_to_property_value(value: &serde_json::Value) -> Result<PropertyValue> {
271+
fn param_to_property_value(value: &ParamValue) -> Result<PropertyValue> {
257272
match value {
258-
serde_json::Value::Null => Ok(PropertyValue::Null),
259-
serde_json::Value::Bool(b) => Ok(PropertyValue::Boolean(*b)),
260-
serde_json::Value::Number(n) => {
261-
if let Some(i) = n.as_i64() {
262-
Ok(PropertyValue::Integer(i))
263-
} else if let Some(f) = n.as_f64() {
264-
Ok(PropertyValue::Float(f))
265-
} else {
266-
Err(QueryError::PlanError {
267-
message: format!("Number parameter could not be converted to i64 or f64: {}", n),
268-
location: snafu::Location::new(file!(), line!(), column!()),
269-
})
270-
}
271-
}
272-
serde_json::Value::String(s) => Ok(PropertyValue::String(s.clone())),
273-
serde_json::Value::Array(_) | serde_json::Value::Object(_) => {
273+
ParamValue::Bool(b) => Ok(PropertyValue::Boolean(*b)),
274+
ParamValue::Int(i) => Ok(PropertyValue::Integer(*i)),
275+
ParamValue::Float(f) => Ok(PropertyValue::Float(*f)),
276+
ParamValue::String(s) => Ok(PropertyValue::String(s.clone())),
277+
ParamValue::List(_) => {
274278
Err(QueryError::PlanError {
275-
message: "Complex types (List, Map) are not fully supported as parameters yet (except float vectors).".to_string(),
279+
message: "List parameters are only supported as float vectors in value expressions.".to_string(),
276280
location: snafu::Location::new(file!(), line!(), column!()),
277281
})
278282
}

src/query/lance_parser/semantic.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl SemanticAnalyzer {
7474
pub fn analyze(
7575
&mut self,
7676
query: &CypherQuery,
77-
parameters: &HashMap<String, serde_json::Value>,
77+
parameters: &HashMap<String, super::parameter_substitution::ParamValue>,
7878
) -> Result<SemanticResult> {
7979
// Clone the query to perform parameter substitution
8080
let mut analyzed_query = query.clone();
@@ -747,7 +747,7 @@ impl SemanticAnalyzer {
747747
fn substitute_parameters(
748748
&self,
749749
query: &mut CypherQuery,
750-
parameters: &HashMap<String, serde_json::Value>,
750+
parameters: &HashMap<String, super::parameter_substitution::ParamValue>,
751751
) -> Result<()> {
752752
super::parameter_substitution::substitute_parameters(query, parameters)
753753
}
@@ -1200,7 +1200,7 @@ mod tests {
12001200
};
12011201

12021202
let mut parameters = HashMap::new();
1203-
parameters.insert("min_age".to_string(), serde_json::json!(18));
1203+
parameters.insert("min_age".to_string(), crate::query::lance_parser::ParamValue::Int(18));
12041204

12051205
let mut analyzer = SemanticAnalyzer::new(test_config());
12061206
let result = analyzer

0 commit comments

Comments
 (0)