X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=PLC%2FFilter.py;h=41f89a628f42ad1ec4420143bec5ec4428fe3409;hb=2262dad38e7699a98c3df2a8c0de3ccf3d6e6373;hp=5dc9f9b3df0c3ab03f7959eb3e5ee55d1a8a9920;hpb=396345c6da341555b1cfb3a2fc265687cd4a235b;p=plcapi.git diff --git a/PLC/Filter.py b/PLC/Filter.py index 5dc9f9b..41f89a6 100644 --- a/PLC/Filter.py +++ b/PLC/Filter.py @@ -1,23 +1,18 @@ -# $Id$ -# $URL$ -from types import StringTypes -try: - set -except NameError: - from sets import Set - set = Set - +# +# Thierry Parmentelat - INRIA +# import time from PLC.Faults import * from PLC.Parameter import Parameter, Mixed, python_type +from PLC.Logger import logger class Filter(Parameter, dict): """ A type of parameter that represents a filter on one or more columns of a database table. Special features provide support for negation, upper and lower bounds, - as well as sorting and clipping. + sorting and clipping and more... fields should be a dictionary of field names and types. @@ -33,26 +28,8 @@ class Filter(Parameter, dict): example : filter = { 'hostname' : '*.edu' , site_id : [34,54] } - Whether the filter represents an intersection (AND) or a union (OR) - of these criteria is determined as follows: - * if the dictionnary has the '-AND' or the '-OR' key, this is chosen - * otherwise, the join_with argument, as provided to the sql method below, - is expected to hold the 'AND' or 'OR' string - this argument defaults to 'AND' and in most of the code, this default applies - as the join_with argument is left unspecified - - 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 } @@ -72,14 +49,29 @@ class Filter(Parameter, dict): SQL wildcard character. example : filter = { 'hostname' : '*.jp' } + * a field starting with '&' or '|' should refer to a sequence type + the semantics 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' } + * 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 + * '-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' ] } - * '-OFFSET' : the number of first rows to be ommitted - * '-LIMIT' : the amount of rows to be returned + * '-OFFSET' : the number of first rows to be ommitted + * '-LIMIT' : the amount of rows to be returned example : filter = { '-OFFSET' : 100, '-LIMIT':25} + * similarly the two special keys below allow to change the semantics of multi-keys filters + * '-AND' : select rows that match ALL the criteria (default) + * '-OR' : select rows that match ANY criteria + The value attached to these keys is ignored. + Please note however that because a Filter is a dict, you cannot provide two criteria on a given key. + Here are a few realistic examples @@ -111,7 +103,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() ] ) + for (field,expected) in fields.items() ] ) # Null filter means no filter Parameter.__init__(self, self.fields, doc = doc, nullok = True) @@ -121,10 +113,10 @@ class Filter(Parameter, dict): Returns a SQL conditional that represents this filter. """ - if self.has_key('-AND'): + if '-AND' in self: del self['-AND'] join_with='AND' - if self.has_key('-OR'): + if '-OR' in self: del self['-OR'] join_with='OR' @@ -142,7 +134,7 @@ class Filter(Parameter, dict): sorts = [] clips = [] - for field, value in self.iteritems(): + for field, value in self.items(): # handle negation, numeric comparisons # simple, 1-depth only mechanism @@ -153,7 +145,7 @@ class Filter(Parameter, dict): '&' : False, '|' : False, } def check_modifiers(field): - if field[0] in modifiers.keys(): + if field[0] in list(modifiers.keys()): modifiers[field[0]] = True field = field[1:] return check_modifiers(field) @@ -163,7 +155,7 @@ class Filter(Parameter, dict): # filter on fields if not modifiers['-']: if field not in self.fields: - raise PLCInvalidArgument, "Invalid filter field '%s'" % field + raise PLCInvalidArgument("Invalid filter field '%s'" % field) # handling array fileds always as compound values if modifiers['&'] or modifiers['|']: @@ -174,7 +166,7 @@ class Filter(Parameter, dict): if value is None: operator = "IS" value = "NULL" - elif isinstance(value, StringTypes) and \ + elif isinstance(value, str) and \ (value.find("*") > -1 or value.find("%") > -1): operator = "ILIKE" # insert *** in pattern instead of either * or % @@ -219,7 +211,7 @@ class Filter(Parameter, dict): else: vals[base_op] = [val] subclauses = [] - for operator in vals.keys(): + for operator in list(vals.keys()): if operator == '=': if modifiers['&']: subclauses.append("(%s @> ARRAY[%s])" % (field, ",".join(vals[operator]))) @@ -245,7 +237,7 @@ class Filter(Parameter, dict): # sorting and clipping else: if field not in ('SORT','OFFSET','LIMIT'): - raise PLCInvalidArgument, "Invalid filter, unknown sort and clip field %r"%field + raise PLCInvalidArgument("Invalid filter, unknown sort and clip field %r"%field) # sorting if field == 'SORT': if not isinstance(value,(list,tuple,set)): @@ -258,7 +250,7 @@ class Filter(Parameter, dict): field = field[1:] order = 'DESC' if field not in self.fields: - raise PLCInvalidArgument, "Invalid field %r in SORT filter"%field + raise PLCInvalidArgument("Invalid field %r in SORT filter"%field) sorts.append("%s %s"%(field,order)) # clipping elif field == 'OFFSET': @@ -273,5 +265,7 @@ class Filter(Parameter, dict): clip_part += " ORDER BY " + ",".join(sorts) if clips: clip_part += " " + " ".join(clips) - if Filter.debug: print 'Filter.sql: where_part=',where_part,'clip_part',clip_part - return (where_part,clip_part) + if Filter.debug: + logger.debug('Filter.sql: where_part={} - clip_part={}' + .format(where_part, clip_part)) + return where_part, clip_part