From 009d9c3f961502939f8b939682b533edd7700f12 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bar=C4=B1=C5=9F=20Metin?= Date: Mon, 5 Oct 2009 16:18:23 +0000 Subject: [PATCH] * a field starting with '&' or '|' should refer to a sequence type the semantic is then that the object value (expected to be a list) should contain all (&) or any (|) value specified in the corresponding filter value. See other examples below. example : filter = { '|role_ids' : [ 20, 40 ] } example : filter = { '|roles' : ['tech', 'pi'] } example : filter = { '&roles' : ['admin', 'tech'] } example : filter = { '&roles' : 'tech' } --- PLC/Filter.py | 73 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/PLC/Filter.py b/PLC/Filter.py index 8a7ba31..3f04da9 100644 --- a/PLC/Filter.py +++ b/PLC/Filter.py @@ -20,8 +20,9 @@ class Filter(Parameter, dict): as well as sorting and clipping. - fields should be a dictionary of field names and types - Only filters on non-sequence type fields are supported. + fields should be a dictionary of field names and types. + As of PLCAPI-4.3-26, we provide support for filtering on + sequence types as well, with the special '&' and '|' modifiers. example : fields = {'node_id': Parameter(int, "Node identifier"), 'hostname': Parameter(int, "Fully qualified hostname", max = 255), ...} @@ -36,6 +37,15 @@ class Filter(Parameter, dict): Special features: + * a field starting with '&' or '|' should refer to a sequence type + the semantic is then that the object value (expected to be a list) + should contain all (&) or any (|) value specified in the corresponding + filter value. See other examples below. + example : filter = { '|role_ids' : [ 20, 40 ] } + example : filter = { '|roles' : ['tech', 'pi'] } + example : filter = { '&roles' : ['admin', 'tech'] } + example : filter = { '&roles' : 'tech' } + * a field starting with the ~ character means negation. example : filter = { '~peer_id' : None } @@ -55,7 +65,7 @@ class Filter(Parameter, dict): SQL wildcard character. example : filter = { 'hostname' : '*.jp' } - * fields starting with - are special and relate to row selection, i.e. sorting and clipping + * the filter's keys starting with '-' are special and relate to sorting and clipping * '-SORT' : a field name, or an ordered list of field names that are used for sorting these fields may start with + (default) or - for denoting increasing or decreasing order example : filter = { '-SORT' : [ '+node_id', '-hostname' ] } @@ -63,9 +73,19 @@ class Filter(Parameter, dict): * '-LIMIT' : the amount of rows to be returned example : filter = { '-OFFSET' : 100, '-LIMIT':25} - A realistic example would read + Here are a few realistic examples + GetNodes ( { 'node_type' : 'regular' , 'hostname' : '*.edu' , '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 } ) - and that would return regular (usual) nodes matching '*.edu' in alphabetical order from 31th to 55th + would return regular (usual) nodes matching '*.edu' in alphabetical order from 31th to 55th + + GetPersons ( { '|role_ids' : [ 20 , 40] } ) + would return all persons that have either pi (20) or tech (40) roles + + GetPersons ( { '&role_ids' : 10 } ) + GetPersons ( { '&role_ids' : 10 } ) + GetPersons ( { '|role_ids' : [ 10 ] } ) + GetPersons ( { '|role_ids' : [ 10 ] } ) + all 4 forms are equivalent and would return all admin users in the system """ def __init__(self, fields = {}, filter = {}, doc = "Attribute filter"): @@ -76,8 +96,7 @@ class Filter(Parameter, dict): # either a value or a list of values for each of the specified # fields. self.fields = dict ( [ ( field, Mixed (expected, [expected])) - for (field,expected) in fields.iteritems() - if python_type(expected) not in (list, tuple, set) ] ) + for (field,expected) in fields.iteritems() ] ) # Null filter means no filter Parameter.__init__(self, self.fields, doc = doc, nullok = True) @@ -107,32 +126,50 @@ class Filter(Parameter, dict): '<' : False, '>' : False, '[' : False, ']' : False, '-' : False, + '&' : False, '|' : False, } - - for char in modifiers.keys(): - if field[0] == char: - modifiers[char]=True; - field = field[1:] - break + def check_modifiers(field): + if field[0] in modifiers.keys(): + modifiers[field[0]] = True + field = field[1:] + return check_modifiers(field) + return field + field = check_modifiers(field) # filter on fields if not modifiers['-']: if field not in self.fields: raise PLCInvalidArgument, "Invalid filter field '%s'" % field + # handling array fileds always as compound values + if modifiers['&'] or modifiers['|']: + if not isinstance(value, (list, tuple, set)): + value = [value,] + if isinstance(value, (list, tuple, set)): # handling filters like '~slice_id':[] # this should return true, as it's the opposite of 'slice_id':[] which is false # prior to this fix, 'slice_id':[] would have returned ``slice_id IN (NULL) '' which is unknown # so it worked by coincidence, but the negation '~slice_ids':[] would return false too if not value: - field="" - operator="" - value = "FALSE" + if modifiers['&'] or modifiers['|']: + operator = "=" + value = "'{}'" + else: + field="" + operator="" + value = "FALSE" else: - operator = "IN" value = map(str, map(api.db.quote, value)) - value = "(%s)" % ", ".join(value) + if modifiers['&']: + operator = "@>" + value = "ARRAY[%s]" % ", ".join(value) + elif modifiers['|']: + operator = "&&" + value = "ARRAY[%s]" % ", ".join(value) + else: + operator = "IN" + value = "(%s)" % ", ".join(value) else: if value is None: operator = "IS" -- 2.43.0