filter should be a dictionary of field names and values
representing the criteria for filtering.
example : filter = { 'hostname' : '*.edu' , site_id : [34,54] }
+
+
Whether the filter represents an intersection (AND) or a union (OR)
- of these criteria is determined by the join_with argument
- provided to the sql method below
+ 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:
* '-LIMIT' : the amount of rows to be returned
example : filter = { '-OFFSET' : 100, '-LIMIT':25}
+
Here are a few realistic examples
- GetNodes ( { 'node_type' : 'regular' , 'hostname' : '*.edu' , '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 } )
+ GetNodes ( { 'node_type' : 'regular' , 'hostname' : '*.edu' ,
+ '-SORT' : 'hostname' , '-OFFSET' : 30 , '-LIMIT' : 25 } )
would return regular (usual) nodes matching '*.edu' in alphabetical order from 31th to 55th
+ GetNodes ( { '~peer_id' : None } )
+ returns the foreign nodes - that have an integer peer_id
+
GetPersons ( { '|role_ids' : [ 20 , 40] } )
would return all persons that have either pi (20) or tech (40) roles
all 4 forms are equivalent and would return all admin users in the system
"""
+ debug=False
+# debug=True
+
def __init__(self, fields = {}, filter = {}, doc = "Attribute filter"):
# Store the filter in our dict instance
dict.__init__(self, filter)
Returns a SQL conditional that represents this filter.
"""
+ if self.has_key('-AND'):
+ del self['-AND']
+ join_with='AND'
+ if self.has_key('-OR'):
+ del self['-OR']
+ join_with='OR'
+
+ self.join_with=join_with
+
# So that we always return something
if join_with == "AND":
conditionals = ["True"]
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:
- if modifiers['&'] or modifiers['|']:
- operator = "="
- value = "'{}'"
- else:
- field=""
- operator=""
- value = "FALSE"
- else:
- value = map(str, map(api.db.quote, 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:
+ def get_op_and_val(value):
if value is None:
operator = "IS"
value = "NULL"
operator='<='
if modifiers[']']:
operator='>='
+ value = str(api.db.quote(value))
+ return (operator, 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:
+ if modifiers['&'] or modifiers['|']:
+ operator = "="
+ value = "'{}'"
+ else:
+ field=""
+ operator=""
+ value = "FALSE"
+ clause = "%s %s %s" % (field, operator, value)
+ else:
+ value = map(str, map(api.db.quote, value))
+ do_join = True
+ vals = {}
+ for val in value:
+ base_op, val = get_op_and_val(val)
+ if base_op != '=':
+ do_join = False
+ if base_op in vals:
+ vals[base_op].append(val)
+ else:
+ vals[base_op] = [val]
+ if do_join:
+ if modifiers['&']:
+ operator = "@>"
+ value = "ARRAY[%s]" % ", ".join(value)
+ elif modifiers['|']:
+ operator = "&&"
+ value = "ARRAY[%s]" % ", ".join(value)
+ else:
+ operator = "IN"
+ value = "(%s)" % ", ".join(value)
+ clause = "%s %s %s" % (field, operator, value)
else:
- value = str(api.db.quote(value))
-
- clause = "%s %s %s" % (field, operator, value)
+ # We need something more complex
+ subclauses = []
+ for operator in vals.keys():
+ if operator == '=':
+ subclauses.append("(%s IN (%s))" % (field, ",".join(vals[operator])))
+ elif operator == 'IS':
+ subclauses.append("(%s IS NULL)" % field)
+ else:
+ for value in vals[operator]:
+ subclauses.append("(%s %s %s)" % (field, operator, value))
+ clause = "(" + " OR ".join(subclauses) + ")"
+ else:
+ operator, value = get_op_and_val(value)
+ clause = "%s %s %s" % (field, operator, value)
if modifiers['~']:
clause = " ( NOT %s ) " % (clause)
clip_part += " ORDER BY " + ",".join(sorts)
if clips:
clip_part += " " + " ".join(clips)
-# print 'where_part=',where_part,'clip_part',clip_part
+ if Filter.debug: print 'Filter.sql: where_part=',where_part,'clip_part',clip_part
return (where_part,clip_part)