@@ -2,10 +2,23 @@ use super::ast::*;
22use super :: super :: error:: { QueryError , Result } ;
33use 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
619pub 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
4659fn 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
6376fn 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
8497fn 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
94107fn 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
104117fn 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
122135fn 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
129142fn 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
142155fn 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
152165fn 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
162175fn 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
198211fn 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 }
0 commit comments