@@ -9,7 +9,7 @@ use graph::prelude::*;
99use graph:: { components:: store:: EntityType , data:: graphql:: ObjectOrInterface } ;
1010
1111use crate :: execution:: ast as a;
12- use crate :: schema:: ast as sast;
12+ use crate :: schema:: ast:: { self as sast, FilterOp } ;
1313
1414use super :: prefetch:: SelectedAttributes ;
1515
@@ -118,7 +118,7 @@ fn build_filter(
118118) -> Result < Option < EntityFilter > , QueryExecutionError > {
119119 match field. argument_value ( "where" ) {
120120 Some ( r:: Value :: Object ( object) ) => match build_filter_from_object ( entity, object, schema) {
121- Ok ( filter) => Ok ( Some ( filter) ) ,
121+ Ok ( filter) => Ok ( Some ( EntityFilter :: And ( filter) ) ) ,
122122 Err ( e) => Err ( e) ,
123123 } ,
124124 Some ( r:: Value :: Null ) => Ok ( None ) ,
@@ -161,91 +161,164 @@ fn parse_change_block_filter(value: &r::Value) -> Result<BlockNumber, QueryExecu
161161 }
162162}
163163
164+ /// Parses a GraphQL Filter Value into an EntityFilter.
165+ fn build_entity_filter (
166+ field_name : String ,
167+ operation : FilterOp ,
168+ store_value : Value ,
169+ ) -> Result < EntityFilter , QueryExecutionError > {
170+ return match operation {
171+ FilterOp :: Not => Ok ( EntityFilter :: Not ( field_name, store_value) ) ,
172+ FilterOp :: GreaterThan => Ok ( EntityFilter :: GreaterThan ( field_name, store_value) ) ,
173+ FilterOp :: LessThan => Ok ( EntityFilter :: LessThan ( field_name, store_value) ) ,
174+ FilterOp :: GreaterOrEqual => Ok ( EntityFilter :: GreaterOrEqual ( field_name, store_value) ) ,
175+ FilterOp :: LessOrEqual => Ok ( EntityFilter :: LessOrEqual ( field_name, store_value) ) ,
176+ FilterOp :: In => Ok ( EntityFilter :: In (
177+ field_name,
178+ list_values ( store_value, "_in" ) ?,
179+ ) ) ,
180+ FilterOp :: NotIn => Ok ( EntityFilter :: NotIn (
181+ field_name,
182+ list_values ( store_value, "_not_in" ) ?,
183+ ) ) ,
184+ FilterOp :: Contains => Ok ( EntityFilter :: Contains ( field_name, store_value) ) ,
185+ FilterOp :: ContainsNoCase => Ok ( EntityFilter :: ContainsNoCase ( field_name, store_value) ) ,
186+ FilterOp :: NotContains => Ok ( EntityFilter :: NotContains ( field_name, store_value) ) ,
187+ FilterOp :: NotContainsNoCase => Ok ( EntityFilter :: NotContainsNoCase ( field_name, store_value) ) ,
188+ FilterOp :: StartsWith => Ok ( EntityFilter :: StartsWith ( field_name, store_value) ) ,
189+ FilterOp :: StartsWithNoCase => Ok ( EntityFilter :: StartsWithNoCase ( field_name, store_value) ) ,
190+ FilterOp :: NotStartsWith => Ok ( EntityFilter :: NotStartsWith ( field_name, store_value) ) ,
191+ FilterOp :: NotStartsWithNoCase => {
192+ Ok ( EntityFilter :: NotStartsWithNoCase ( field_name, store_value) )
193+ }
194+ FilterOp :: EndsWith => Ok ( EntityFilter :: EndsWith ( field_name, store_value) ) ,
195+ FilterOp :: EndsWithNoCase => Ok ( EntityFilter :: EndsWithNoCase ( field_name, store_value) ) ,
196+ FilterOp :: NotEndsWith => Ok ( EntityFilter :: NotEndsWith ( field_name, store_value) ) ,
197+ FilterOp :: NotEndsWithNoCase => Ok ( EntityFilter :: NotEndsWithNoCase ( field_name, store_value) ) ,
198+ FilterOp :: Equal => Ok ( EntityFilter :: Equal ( field_name, store_value) ) ,
199+ _ => unreachable ! ( ) ,
200+ } ;
201+ }
202+
203+ /// Iterate over the list and generate an EntityFilter from it
204+ fn build_list_filter_from_value (
205+ entity : ObjectOrInterface ,
206+ schema : & ApiSchema ,
207+ value : & r:: Value ,
208+ ) -> Result < Vec < EntityFilter > , QueryExecutionError > {
209+ return match value {
210+ r:: Value :: List ( list) => Ok ( list
211+ . iter ( )
212+ . map ( |item| {
213+ return match item {
214+ r:: Value :: Object ( object) => {
215+ Ok ( build_filter_from_object ( entity, object, schema) ?)
216+ }
217+ _ => Err ( QueryExecutionError :: InvalidFilterError ) ,
218+ } ;
219+ } )
220+ . collect :: < Result < Vec < Vec < EntityFilter > > , QueryExecutionError > > ( ) ?
221+ // Flatten all different EntityFilters into one list
222+ . into_iter ( )
223+ . flatten ( )
224+ . collect :: < Vec < EntityFilter > > ( ) ) ,
225+ _ => Err ( QueryExecutionError :: InvalidFilterError ) ,
226+ } ;
227+ }
228+
229+ /// build a filter which has list of nested filters
230+ fn build_list_filter_from_object (
231+ entity : ObjectOrInterface ,
232+ object : & Object ,
233+ schema : & ApiSchema ,
234+ ) -> Result < Vec < EntityFilter > , QueryExecutionError > {
235+ Ok ( object
236+ . iter ( )
237+ . map ( |( _, value) | {
238+ return build_list_filter_from_value ( entity, schema, value) ;
239+ } )
240+ . collect :: < Result < Vec < Vec < EntityFilter > > , QueryExecutionError > > ( ) ?
241+ . into_iter ( )
242+ // We iterate an object so all entity filters are flattened into one list
243+ . flatten ( )
244+ . collect :: < Vec < EntityFilter > > ( ) )
245+ }
246+
164247/// Parses a GraphQL input object into an EntityFilter, if present.
165248fn build_filter_from_object (
166249 entity : ObjectOrInterface ,
167250 object : & Object ,
168251 schema : & ApiSchema ,
169- ) -> Result < EntityFilter , QueryExecutionError > {
170- Ok ( EntityFilter :: And ( {
171- object
172- . iter ( )
173- . map ( |( key, value) | {
174- // Special handling for _change_block input filter since its not a
175- // standard entity filter that is based on entity structure/fields
176- if key == "_change_block" {
177- return match parse_change_block_filter ( value) {
178- Ok ( block_number) => Ok ( EntityFilter :: ChangeBlockGte ( block_number) ) ,
179- Err ( e) => Err ( e) ,
180- } ;
181- }
182-
183- use self :: sast:: FilterOp :: * ;
184- let ( field_name, op) = sast:: parse_field_as_filter ( key) ;
252+ ) -> Result < Vec < EntityFilter > , QueryExecutionError > {
253+ Ok ( object
254+ . iter ( )
255+ . map ( |( key, value) | {
256+ // Special handling for _change_block input filter since its not a
257+ // standard entity filter that is based on entity structure/fields
258+ if key == "_change_block" {
259+ return match parse_change_block_filter ( value) {
260+ Ok ( block_number) => Ok ( EntityFilter :: ChangeBlockGte ( block_number) ) ,
261+ Err ( e) => Err ( e) ,
262+ } ;
263+ }
264+ use self :: sast:: FilterOp :: * ;
265+ let ( field_name, op) = sast:: parse_field_as_filter ( key) ;
185266
186- let field = sast:: get_field ( entity, & field_name) . ok_or_else ( || {
187- QueryExecutionError :: EntityFieldError (
188- entity. name ( ) . to_owned ( ) ,
189- field_name. clone ( ) ,
190- )
191- } ) ?;
267+ Ok ( match op {
268+ And => {
269+ if ENV_VARS . graphql . disable_bool_filters {
270+ return Err ( QueryExecutionError :: NotSupported (
271+ "Boolean filters are not supported" . to_string ( ) ,
272+ ) ) ;
273+ }
192274
193- let ty = & field. field_type ;
275+ return Ok ( EntityFilter :: And ( build_list_filter_from_object (
276+ entity, object, schema,
277+ ) ?) ) ;
278+ }
279+ Or => {
280+ if ENV_VARS . graphql . disable_bool_filters {
281+ return Err ( QueryExecutionError :: NotSupported (
282+ "Boolean filters are not supported" . to_string ( ) ,
283+ ) ) ;
284+ }
194285
195- Ok ( match op {
196- Child => match value {
197- DataValue :: Object ( obj) => {
198- build_child_filter_from_object ( entity, field_name, obj, schema) ?
199- }
200- _ => {
201- return Err ( QueryExecutionError :: AttributeTypeError (
202- value. to_string ( ) ,
203- ty. to_string ( ) ,
204- ) )
205- }
206- } ,
286+ return Ok ( EntityFilter :: Or ( build_list_filter_from_object (
287+ entity, object, schema,
288+ ) ?) ) ;
289+ }
290+ Child => match value {
291+ DataValue :: Object ( obj) => {
292+ build_child_filter_from_object ( entity, field_name, obj, schema) ?
293+ }
207294 _ => {
208- let store_value = Value :: from_query_value ( value, ty) ?;
209-
210- match op {
211- Not => EntityFilter :: Not ( field_name, store_value) ,
212- GreaterThan => EntityFilter :: GreaterThan ( field_name, store_value) ,
213- LessThan => EntityFilter :: LessThan ( field_name, store_value) ,
214- GreaterOrEqual => EntityFilter :: GreaterOrEqual ( field_name, store_value) ,
215- LessOrEqual => EntityFilter :: LessOrEqual ( field_name, store_value) ,
216- In => EntityFilter :: In ( field_name, list_values ( store_value, "_in" ) ?) ,
217- NotIn => EntityFilter :: NotIn (
218- field_name,
219- list_values ( store_value, "_not_in" ) ?,
220- ) ,
221- Contains => EntityFilter :: Contains ( field_name, store_value) ,
222- ContainsNoCase => EntityFilter :: ContainsNoCase ( field_name, store_value) ,
223- NotContains => EntityFilter :: NotContains ( field_name, store_value) ,
224- NotContainsNoCase => {
225- EntityFilter :: NotContainsNoCase ( field_name, store_value)
226- }
227- StartsWith => EntityFilter :: StartsWith ( field_name, store_value) ,
228- StartsWithNoCase => {
229- EntityFilter :: StartsWithNoCase ( field_name, store_value)
230- }
231- NotStartsWith => EntityFilter :: NotStartsWith ( field_name, store_value) ,
232- NotStartsWithNoCase => {
233- EntityFilter :: NotStartsWithNoCase ( field_name, store_value)
234- }
235- EndsWith => EntityFilter :: EndsWith ( field_name, store_value) ,
236- EndsWithNoCase => EntityFilter :: EndsWithNoCase ( field_name, store_value) ,
237- NotEndsWith => EntityFilter :: NotEndsWith ( field_name, store_value) ,
238- NotEndsWithNoCase => {
239- EntityFilter :: NotEndsWithNoCase ( field_name, store_value)
240- }
241- Equal => EntityFilter :: Equal ( field_name, store_value) ,
242- _ => unreachable ! ( ) ,
243- }
295+ let field = sast:: get_field ( entity, & field_name) . ok_or_else ( || {
296+ QueryExecutionError :: EntityFieldError (
297+ entity. name ( ) . to_owned ( ) ,
298+ field_name. clone ( ) ,
299+ )
300+ } ) ?;
301+ let ty = & field. field_type ;
302+ return Err ( QueryExecutionError :: AttributeTypeError (
303+ value. to_string ( ) ,
304+ ty. to_string ( ) ,
305+ ) ) ;
244306 }
245- } )
307+ } ,
308+ _ => {
309+ let field = sast:: get_field ( entity, & field_name) . ok_or_else ( || {
310+ QueryExecutionError :: EntityFieldError (
311+ entity. name ( ) . to_owned ( ) ,
312+ field_name. clone ( ) ,
313+ )
314+ } ) ?;
315+ let ty = & field. field_type ;
316+ let store_value = Value :: from_query_value ( value, ty) ?;
317+ return build_entity_filter ( field_name, op, store_value) ;
318+ }
246319 } )
247- . collect :: < Result < Vec < EntityFilter > , QueryExecutionError > > ( ) ?
248- } ) )
320+ } )
321+ . collect :: < Result < Vec < EntityFilter > , QueryExecutionError > > ( ) ? )
249322}
250323
251324fn build_child_filter_from_object (
@@ -261,7 +334,11 @@ fn build_child_filter_from_object(
261334 let child_entity = schema
262335 . object_or_interface ( type_name)
263336 . ok_or ( QueryExecutionError :: InvalidFilterError ) ?;
264- let filter = Box :: new ( build_filter_from_object ( child_entity, object, schema) ?) ;
337+ let filter = Box :: new ( EntityFilter :: And ( build_filter_from_object (
338+ child_entity,
339+ object,
340+ schema,
341+ ) ?) ) ;
265342 let derived = field. is_derived ( ) ;
266343 let attr = match derived {
267344 true => sast:: get_derived_from_field ( child_entity, field)
0 commit comments