Commit 9bcb5a93 authored by Jean-Paul Smets's avatar Jean-Paul Smets

Minor changes in syntax (space, tabs, etc.)

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@19215 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 7fd695dd
...@@ -33,7 +33,7 @@ class ComplexQuery(QueryMixin): ...@@ -33,7 +33,7 @@ class ComplexQuery(QueryMixin):
""" """
Used in order to concatenate many queries Used in order to concatenate many queries
""" """
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
self.query_list = args self.query_list = args
self.operator = kw.pop('operator', 'AND') self.operator = kw.pop('operator', 'AND')
......
...@@ -34,10 +34,10 @@ class QueryMixin: ...@@ -34,10 +34,10 @@ class QueryMixin:
operator = None operator = None
format = None format = None
type = None type = None
def __call__(self, **kw): def __call__(self, **kw):
return self.asSQLExpression(**kw) return self.asSQLExpression(**kw)
def getOperator(self): def getOperator(self):
return self.operator return self.operator
......
...@@ -30,29 +30,29 @@ from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query ...@@ -30,29 +30,29 @@ from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query
from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery
from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance
from DateTime import DateTime from DateTime import DateTime
from Key import BaseKey from SearchKey import SearchKey
from pprint import pprint from pprint import pprint
class DateTimeKey(BaseKey): class DateTimeKey(SearchKey):
""" DateTimeKey key is an ERP5 portal_catalog search key which is used to render """ DateTimeKey key is an ERP5 portal_catalog search key which is used to render
SQL expression that will try to match values in DateTime MySQL columns. SQL expression that will try to match values in DateTime MySQL columns.
It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in
addition to main logical operators like ['OR', 'or', 'AND', 'and']. addition to main logical operators like ['OR', 'or', 'AND', 'and'].
Note: because all ERP5 datetime values are indexed in MySQL in 'UTC' Note: because all ERP5 datetime values are indexed in MySQL in 'UTC'
the respective passed date will be first converted to 'UTC' before inserted into the respective passed date will be first converted to 'UTC' before inserted into
respective SQL query! respective SQL query!
Examples (GMT+02, Bulgaria/Sofia for 'delivery.start_date'): Examples (GMT+02, Bulgaria/Sofia for 'delivery.start_date'):
* '15/01/2008' --> "delivery.start_date = '2008-01-14 22:00'" * '15/01/2008' --> "delivery.start_date = '2008-01-14 22:00'"
* '>=15/01/2008' --> "delivery.start_date >= '2008-01-14 22:00'" * '>=15/01/2008' --> "delivery.start_date >= '2008-01-14 22:00'"
* '>=15/01/2008 or <=20/01/2008' * '>=15/01/2008 or <=20/01/2008'
--> "delivery.start_date >= '2008-01-14 22:00' or delivery.start_date<='2008-01-19 22:00'" --> "delivery.start_date >= '2008-01-14 22:00' or delivery.start_date<='2008-01-19 22:00'"
* '>=15/01/2008 10:00 GMT+02 OR <=20/01/2008 05:12 Universal' * '>=15/01/2008 10:00 GMT+02 OR <=20/01/2008 05:12 Universal'
--> -->
"delivery.start_date >= '2008-01-15 08:00 Universal' "delivery.start_date >= '2008-01-15 08:00 Universal'
...@@ -60,14 +60,14 @@ class DateTimeKey(BaseKey): ...@@ -60,14 +60,14 @@ class DateTimeKey(BaseKey):
delivery.start_date <= '2008-01-20 05:12 Universal' delivery.start_date <= '2008-01-20 05:12 Universal'
" "
""" """
tokens = ('DATE', 'OR', 'AND', 'NOT', 'EQUAL', tokens = ('DATE', 'OR', 'AND', 'NOT', 'EQUAL',
'GREATERTHAN', 'GREATERTHANEQUAL', 'GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL') 'LESSTHAN', 'LESSTHANEQUAL')
sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL', sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL', 'NOT', 'EQUAL',) 'LESSTHAN', 'LESSTHANEQUAL', 'NOT', 'EQUAL',)
def t_OR(self, t): def t_OR(self, t):
r'(\s+OR\s+|\s+or\s+)' r'(\s+OR\s+|\s+or\s+)'
# operator has leading and trailing ONLY one white space character # operator has leading and trailing ONLY one white space character
...@@ -78,21 +78,21 @@ class DateTimeKey(BaseKey): ...@@ -78,21 +78,21 @@ class DateTimeKey(BaseKey):
r'(\s+AND\s+|\s+and\s+)' r'(\s+AND\s+|\s+and\s+)'
# operator has leading and trailing ONLY one white space character # operator has leading and trailing ONLY one white space character
t.value = 'AND' t.value = 'AND'
return t return t
def t_NOT(self, t): def t_NOT(self, t):
r'(\s+NOT\s+|\s+not\s+|!=)' r'(\s+NOT\s+|\s+not\s+|!=)'
# operator has leading and trailing ONLY one white space character # operator has leading and trailing ONLY one white space character
t.value = t.value.upper().strip() t.value = t.value.upper().strip()
return t return t
t_GREATERTHANEQUAL = r'>=' t_GREATERTHANEQUAL = r'>='
t_LESSTHANEQUAL = r'<=' t_LESSTHANEQUAL = r'<='
t_GREATERTHAN = r'>' t_GREATERTHAN = r'>'
t_LESSTHAN = r'<' t_LESSTHAN = r'<'
t_EQUAL = r'=' t_EQUAL = r'='
t_DATE = r'\d{1,4}[(/|\.|\-) /.]\d{1,4}[(/|\.|\-) /.]\d{1,4}((\s.)*\d{0,2}:\d{0,2}(:\d{0,2})?)?(\sUniversal|\sGMT\+\d\d)?|\d\d\d\d%?' t_DATE = r'\d{1,4}[(/|\.|\-) /.]\d{1,4}[(/|\.|\-) /.]\d{1,4}((\s.)*\d{0,2}:\d{0,2}(:\d{0,2})?)?(\sUniversal|\sGMT\+\d\d)?|\d\d\d\d%?'
def quoteSQLString(self, value, format): def quoteSQLString(self, value, format):
""" Return a quoted string of the value. """ Return a quoted string of the value.
Make sure to convert it to UTC first.""" Make sure to convert it to UTC first."""
...@@ -101,7 +101,7 @@ class DateTimeKey(BaseKey): ...@@ -101,7 +101,7 @@ class DateTimeKey(BaseKey):
else: else:
value = "'%s'" %DateTime(value).toZone('UTC').ISO() value = "'%s'" %DateTime(value).toZone('UTC').ISO()
return value return value
def buildQueryForTokenList(self, tokens, key, value, format): def buildQueryForTokenList(self, tokens, key, value, format):
""" Build a ComplexQuery for a token list """ """ Build a ComplexQuery for a token list """
query_list = [] query_list = []
...@@ -125,7 +125,7 @@ class DateTimeKey(BaseKey): ...@@ -125,7 +125,7 @@ class DateTimeKey(BaseKey):
days_offset_map = {'=' : 366, '>' : 366, days_offset_map = {'=' : 366, '>' : 366,
'>=' : 366, '<': -366, '<=':-366} '>=' : 366, '<': -366, '<=':-366}
days_offset = days_offset_map[sub_operator] days_offset = days_offset_map[sub_operator]
# convert to UTC in given format # convert to UTC in given format
is_valid_date = 1 is_valid_date = 1
try: try:
...@@ -137,8 +137,8 @@ class DateTimeKey(BaseKey): ...@@ -137,8 +137,8 @@ class DateTimeKey(BaseKey):
date_value = DateTime(date_value).toZone('UTC') date_value = DateTime(date_value).toZone('UTC')
except: except:
is_valid_date = 0 is_valid_date = 0
query_kw = None query_kw = None
if is_valid_date: if is_valid_date:
if sub_operator == '=': if sub_operator == '=':
# transform to range 'key >= date AND date < key' # transform to range 'key >= date AND date < key'
...@@ -147,7 +147,7 @@ class DateTimeKey(BaseKey): ...@@ -147,7 +147,7 @@ class DateTimeKey(BaseKey):
else: else:
query_kw = {key: date_value + days_offset, query_kw = {key: date_value + days_offset,
'range': sub_operator} 'range': sub_operator}
query_kw['type'] = 'date' query_kw['type'] = 'date'
else: else:
# not a valid date, try to get an year range # not a valid date, try to get an year range
is_year = 1 is_year = 1
...@@ -160,17 +160,17 @@ class DateTimeKey(BaseKey): ...@@ -160,17 +160,17 @@ class DateTimeKey(BaseKey):
query_kw = {key: (date_value, date_value + 366,), query_kw = {key: (date_value, date_value + 366,),
'type': 'date', 'type': 'date',
'range': 'minmax'} 'range': 'minmax'}
# append only if it was possible to generate query # append only if it was possible to generate query
if query_kw is not None: if query_kw is not None:
query_list.append(Query(**query_kw)) query_list.append(Query(**query_kw))
# join query list in one really big ComplexQuery # join query list in one really big ComplexQuery
if len(query_list): if len(query_list):
complex_query = ComplexQuery(*query_list, complex_query = ComplexQuery(*query_list,
**{'operator': 'AND'}) **{'operator': 'AND'})
return complex_query return complex_query
## def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__): ## def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__):
## """ Tokenize/analyze passed string value and generate SQL query expressions. """ ## """ Tokenize/analyze passed string value and generate SQL query expressions. """
## where_expression = '' ## where_expression = ''
......
...@@ -26,15 +26,15 @@ ...@@ -26,15 +26,15 @@
# #
############################################################################## ##############################################################################
from Key import BaseKey from SearchKey import SearchKey
from pprint import pprint from pprint import pprint
class DefaultKey(BaseKey): class DefaultKey(SearchKey):
""" DefaultKey key is an ERP5 portal_catalog search key which is used to render """ DefaultKey key is an ERP5 portal_catalog search key which is used to render
SQL expression that will try to exactly one value. SQL expression that will try to exactly one value.
It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in
addition to main logical operators like ['OR', 'or', 'AND', 'and']. addition to main logical operators like ['OR', 'or', 'AND', 'and'].
Examples for title column: Examples for title column:
* 'foo or bar' --> "title = 'foo' OR title = 'bar'" * 'foo or bar' --> "title = 'foo' OR title = 'bar'"
* 'foo or =bar' --> "title = 'foo' OR title = 'bar'" * 'foo or =bar' --> "title = 'foo' OR title = 'bar'"
...@@ -43,17 +43,17 @@ class DefaultKey(BaseKey): ...@@ -43,17 +43,17 @@ class DefaultKey(BaseKey):
* '"Organisation Module"' --> "title = 'Organisation Module'" * '"Organisation Module"' --> "title = 'Organisation Module'"
* '="Organisation Module"' --> "title = 'Organisation Module'" * '="Organisation Module"' --> "title = 'Organisation Module'"
""" """
# default type of sub Queries to be generated out fo a search string # default type of sub Queries to be generated out fo a search string
default_key_type = 'default' default_key_type = 'default'
tokens = ('OR', 'AND', 'NOT', 'WORDSET', 'WORD', tokens = ('OR', 'AND', 'NOT', 'WORDSET', 'WORD',
'GREATERTHAN', 'GREATERTHANEQUAL', 'GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL') 'LESSTHAN', 'LESSTHANEQUAL')
sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL', sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL', 'NOT') 'LESSTHAN', 'LESSTHANEQUAL', 'NOT')
# Note: Order of placing rules (t_WORD for example) is very important # Note: Order of placing rules (t_WORD for example) is very important
def t_OR(self, t): def t_OR(self, t):
...@@ -69,14 +69,14 @@ class DefaultKey(BaseKey): ...@@ -69,14 +69,14 @@ class DefaultKey(BaseKey):
# otherwise it's treated as a WORD # otherwise it's treated as a WORD
t.value = 'AND' t.value = 'AND'
return t return t
def t_NOT(self, t): def t_NOT(self, t):
r'(\s+NOT\s+|\s+not\s+|!=)' r'(\s+NOT\s+|\s+not\s+|!=)'
# operator must have leading and trailing ONLY one white space character # operator must have leading and trailing ONLY one white space character
# otherwise it's treated as a WORD # otherwise it's treated as a WORD
t.value = '!=' t.value = '!='
return t return t
t_GREATERTHANEQUAL = r'>=' t_GREATERTHANEQUAL = r'>='
t_LESSTHANEQUAL = r'<=' t_LESSTHANEQUAL = r'<='
t_GREATERTHAN = r'>' t_GREATERTHAN = r'>'
...@@ -89,8 +89,8 @@ class DefaultKey(BaseKey): ...@@ -89,8 +89,8 @@ class DefaultKey(BaseKey):
# WORD may contain '%' but not at the beginning or end (otherwise it's KEYWORD) # WORD may contain '%' but not at the beginning or end (otherwise it's KEYWORD)
value = t.value.strip() value = t.value.strip()
t.value = "%s" %value t.value = "%s" %value
return t return t
def t_WORDSET(self, t): def t_WORDSET(self, t):
r'"[\x7F-\xFF\w\d\s\/~!@#$%^&*()_+][\x7F-\xFF\w\d\s\/~!@#$%^&*()_+]*"' r'"[\x7F-\xFF\w\d\s\/~!@#$%^&*()_+][\x7F-\xFF\w\d\s\/~!@#$%^&*()_+]*"'
#r'"[\x7F-\xFF\w\d\s/%][\x7F-\xFF\w\d\s/%]*"' #r'"[\x7F-\xFF\w\d\s/%][\x7F-\xFF\w\d\s/%]*"'
...@@ -99,21 +99,21 @@ class DefaultKey(BaseKey): ...@@ -99,21 +99,21 @@ class DefaultKey(BaseKey):
value = t.value.replace('"', '').strip() value = t.value.replace('"', '').strip()
t.value = "%s" %value t.value = "%s" %value
return t return t
def quoteSQLString(self, value, format): def quoteSQLString(self, value, format):
""" Return a quoted string of the value. """ """ Return a quoted string of the value. """
if isinstance(value, (int, long,)): if isinstance(value, (int, long,)):
return str(value) return str(value)
return "'%s'" %value return "'%s'" %value
## def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__): ## def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__):
## """ Tokenize/analyze passed string value and generate SQL query expressions. """ ## """ Tokenize/analyze passed string value and generate SQL query expressions. """
## where_expressions = [] ## where_expressions = []
## select_expressions = [] ## select_expressions = []
## tokens = self.tokenize(value) ## tokens = self.tokenize(value)
## operators_mapping_list = self.groupByOperator(tokens) ## operators_mapping_list = self.groupByOperator(tokens)
## ##
## # find if any logical operator exists ## # find if any logical operator exists
## tokens_values = [] ## tokens_values = []
## logical_operator_found = 0 ## logical_operator_found = 0
...@@ -122,7 +122,7 @@ class DefaultKey(BaseKey): ...@@ -122,7 +122,7 @@ class DefaultKey(BaseKey):
## logical_operator_found = 1 ## logical_operator_found = 1
## break ## break
## tokens_values.append(token.value.replace("'", "")) ## tokens_values.append(token.value.replace("'", ""))
## ##
## # build expressions ## # build expressions
## if not logical_operator_found: ## if not logical_operator_found:
## # no logical operator found so we assume that we search for a combination of words ## # no logical operator found so we assume that we search for a combination of words
......
...@@ -26,22 +26,22 @@ ...@@ -26,22 +26,22 @@
# #
############################################################################## ##############################################################################
from Key import BaseKey from SearchKey import SearchKey
class FloatKey(BaseKey): class FloatKey(SearchKey):
""" FloatKey key is an ERP5 portal_catalog search key which is used to render """ FloatKey key is an ERP5 portal_catalog search key which is used to render
float like SQL expression. float like SQL expression.
""" """
# default type of sub Queries to be generated out fo a search string # default type of sub Queries to be generated out fo a search string
default_key_type = 'float' default_key_type = 'float'
tokens = ('OR', 'AND', 'NOT', 'FLOAT', tokens = ('OR', 'AND', 'NOT', 'FLOAT',
'GREATERTHAN', 'GREATERTHANEQUAL', 'GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL') 'LESSTHAN', 'LESSTHANEQUAL')
sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL', sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL', 'NOT') 'LESSTHAN', 'LESSTHANEQUAL', 'NOT')
# Note: Order of placing rules (t_WORD for example) is very important # Note: Order of placing rules (t_WORD for example) is very important
def t_OR(self, t): def t_OR(self, t):
...@@ -75,12 +75,12 @@ class FloatKey(BaseKey): ...@@ -75,12 +75,12 @@ class FloatKey(BaseKey):
# FLOAT is a float number # FLOAT is a float number
value = t.value.replace('"', '').strip() value = t.value.replace('"', '').strip()
t.value = "%s" %value t.value = "%s" %value
return t return t
def quoteSQLString(self, value, format): def quoteSQLString(self, value, format):
""" Return a quoted string of the value. """ """ Return a quoted string of the value. """
# Make sure there is no space in float values # Make sure there is no space in float values
return "'%s'" %str(value).replace(' ', '') return "'%s'" %str(value).replace(' ', '')
def quoteSQLKey(self, key, format): def quoteSQLKey(self, key, format):
""" Return a quoted string of the value. """ """ Return a quoted string of the value. """
......
...@@ -26,12 +26,12 @@ ...@@ -26,12 +26,12 @@
# #
############################################################################## ##############################################################################
from Key import BaseKey from SearchKey import SearchKey
SEARCH_MODE_MAPPING = {'in_boolean_mode': 'IN BOOLEAN MODE', SEARCH_MODE_MAPPING = {'in_boolean_mode': 'IN BOOLEAN MODE',
'with_query_expansion': 'WITH QUERY EXPANSION'} 'with_query_expansion': 'WITH QUERY EXPANSION'}
class FullTextKey(BaseKey): class FullTextKey(SearchKey):
""" FullTextKey key is an ERP5 portal_catalog search key which is used to render """ FullTextKey key is an ERP5 portal_catalog search key which is used to render
SQL expression that will try match all possible values using SQL expression that will try match all possible values using
MySQL's fulltext search support. MySQL's fulltext search support.
...@@ -41,12 +41,12 @@ class FullTextKey(BaseKey): ...@@ -41,12 +41,12 @@ class FullTextKey(BaseKey):
tokens = ('PLUS', 'MINUS', 'WORD', 'GREATERTHAN', 'LESSTHAN', 'LEFTPARENTHES', tokens = ('PLUS', 'MINUS', 'WORD', 'GREATERTHAN', 'LESSTHAN', 'LEFTPARENTHES',
'RIGHTPARENTHES', 'TILDE', 'ASTERISK', 'DOUBLEQUOTE',) 'RIGHTPARENTHES', 'TILDE', 'ASTERISK', 'DOUBLEQUOTE',)
# SQL expressions patterns # SQL expressions patterns
relevance = '%s_relevance' relevance = '%s_relevance'
where_match_against = "MATCH %s AGAINST ('%s' %s)" where_match_against = "MATCH %s AGAINST ('%s' %s)"
select_match_against_as = "MATCH %s AGAINST ('%s' %s) AS %s" select_match_against_as = "MATCH %s AGAINST ('%s' %s) AS %s"
t_PLUS = r'(\+)' t_PLUS = r'(\+)'
t_MINUS = r'(\-)' t_MINUS = r'(\-)'
t_GREATERTHAN = r'(\>)' t_GREATERTHAN = r'(\>)'
...@@ -56,7 +56,7 @@ class FullTextKey(BaseKey): ...@@ -56,7 +56,7 @@ class FullTextKey(BaseKey):
t_TILDE = r'(\~)' t_TILDE = r'(\~)'
t_ASTERISK = r'(\*)' t_ASTERISK = r'(\*)'
t_DOUBLEQUOTE = r'(\")' t_DOUBLEQUOTE = r'(\")'
def t_WORD(self, t): def t_WORD(self, t):
r'[\x7F-\xFF\w\d\/!@#$%^&_][\x7F-\xFF\w\d\/!@#$%^&_]*' r'[\x7F-\xFF\w\d\/!@#$%^&_][\x7F-\xFF\w\d\/!@#$%^&_]*'
#r'[\x7F-\xFF\w\d][\x7F-\xFF\w\d]*' #r'[\x7F-\xFF\w\d][\x7F-\xFF\w\d]*'
......
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Ivan Tyagov <ivan@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License,] or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query
from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery
from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance
import ply.yacc as yacc
import ply.lex as lex
class BaseKey:
""" BaseKey is a base class that implements a parser of
search grammar used in ERP5. It also implements all generic
search key class methods."""
# main logical operators
operators = ('OR', 'AND',)
default_operator = '='
# in ERP5 search grammer white space is extremely important
# so we can not ignore it.
#t_ignore = ' \t'
# no need to rack down line numbers
#def t_newline(self, t):
# r'\n+'
# #t.lexer.lineno += len(t.value)
def t_error(self, t):
#print "Illegal character '%s'" % t.value[0]
t.lexer.skip(1)
def p_error(self, p):
pass
def build(self, **kwargs):
""" This method will initialize respective search key class with
tokens' definitions. """
self.lexer = lex.lex(object = self, **kwargs)
def tokenize(self, data):
""" Return list of tokens according to respective
search key tokens' definitions. """
result = []
self.lexer.input(data)
while 1:
tok = self.lexer.token()
if not tok:
break
result.append(tok)
return result
# Grouping of tokens
def getOperatorForTokenList(self, tokens):
""" Generic implementation that will return respective
operator for a token list. The first found occurence wins."""
token = tokens[0]
token_type = token.type
if token_type in self.sub_operators:
return token.value, tokens[1:]
else:
return self.default_operator, tokens
def groupByLogicalOperator(self, tokens, logical_operator ='OR'):
""" Split tokens list into one or many OR concatanated tokens list
"""
sub_tokens_or_groups = []
tmp_token_list = []
for token in tokens:
if token.type != logical_operator:
tmp_token_list.append(token)
else:
sub_tokens_or_groups.append(tmp_token_list)
tmp_token_list = []
# append remainig last tokens
sub_tokens_or_groups.append(tmp_token_list)
return sub_tokens_or_groups
# SQL quoting (each search key should override them it if needed)
def quoteSQLKey(self, key, format):
""" Return a quoted string of the value. """
return key
def quoteSQLString(self, value, format):
""" Return a quoted string of the value. """
return "'%s'" %value
# SQL generation
def buildSQLExpression(self, key, value,
format = None, mode = None, range_value = None, stat__=0):
""" Generic implementation. Leave details to respective key. """
if range_value is not None:
# if range_value we handle directly (i.e no parsing of search string)
where_expressions, select_expressions = \
self.buildSQLExpressionFromRange(key, value,
format, mode, range_value, stat__)
else:
# search string parsing is needed
where_expressions, select_expressions = \
self.buildSQLExpressionFromSearchString(key, str(value),
format, mode, range_value, stat__)
return where_expressions, select_expressions
def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__):
complex_query = self.buildQuery(key, value, format, mode, range_value, stat__)
if complex_query is None:
# Query could not be generated from search string
sql_expression = {'where_expression': '1',
'select_expression_list': []}
else:
sql_expression = complex_query(keyword_search_keys = [],
datetime_search_keys = [],
full_text_search_keys = [])
return sql_expression['where_expression'], sql_expression['select_expression_list']
def buildQuery(self, key, value, format, mode, range_value, stat__):
""" Build Query """
query_list = []
# tokenize searchs string into tokens for Search Key
tokens = self.tokenize(value)
# split tokens list into one or more 'OR' tokens lists
tokens_or_groups = self.groupByLogicalOperator(tokens, 'OR')
# remove empty tokens lists
tokens_or_groups = filter(lambda x: len(x), tokens_or_groups)
# get a ComplexQuery for a sub token list
for tokens_or_group in tokens_or_groups:
query = self.buildQueryForTokenList(tokens_or_group, key, value, format)
if query is not None:
# query could be generated for token list
query_list.append(query)
if len(query_list):
# join query list in one really big ComplexQuery
return ComplexQuery(*query_list,
**{'operator':'OR'})
def buildQueryForTokenList(self, tokens, key, value, format):
""" Build a ComplexQuery for a token list """
query_list = []
logical_groups = self.groupByLogicalOperator(tokens, 'AND')
for group_tokens in logical_groups:
token_values = [x.value for x in group_tokens]
sub_operator, sub_tokens = self.getOperatorForTokenList(group_tokens)
sub_tokens_values = [x.value for x in sub_tokens]
query_kw = {key: ' '.join(sub_tokens_values),
'type': self.default_key_type,
'format': format,
'range': sub_operator}
query_list.append(Query(**query_kw))
# join query list in one really big ComplexQuery
complex_query = ComplexQuery(*query_list,
**{'operator': 'AND'})
return complex_query
def buildSQLExpressionFromRange(self, key, value, format, mode, range_value, stat__):
""" This method will generate SQL expressions
from explicitly passed list of values and
range_value in ('min', 'max', ..)"""
key = self.quoteSQLKey(key, format)
where_expression = ''
select_expressions = []
if isinstance(value, (list, tuple)):
if len(value) > 1:
# value should contain at least two items
query_min = self.quoteSQLString(value[0], format)
query_max = self.quoteSQLString(value[1], format)
else:
# value contains only one item
query_min = query_max = self.quoteSQLString(value[0], format)
else:
query_min = query_max = self.quoteSQLString(value, format)
if range_value == 'min':
where_expression = "%s >= %s" % (key, query_min)
elif range_value == 'max':
where_expression = "%s < %s" % (key, query_max)
elif range_value == 'minmax' :
where_expression = "%s >= %s AND %s < %s" % (key, query_min, key, query_max)
elif range_value == 'minngt' :
where_expression = "%s >= %s AND %s <= %s" % (key, query_min, key, query_max)
elif range_value == 'ngt':
where_expression = "%s <= %s" % (key, query_max)
elif range_value == 'nlt':
where_expression = "%s > %s" % (key, query_max)
elif range_value == 'like':
where_expression = "%s LIKE %s" % (key, query_max)
elif range_value == 'not_like':
where_expression = "%s NOT LIKE %s" % (key, query_max)
elif range_value in ('=', '>', '<', '>=', '<=','!=',):
where_expression = "%s %s %s" % (key, range_value, query_max)
return where_expression, select_expressions
## def groupByOperator(self, tokens, group_by_operators_list = operators):
## """ Generic implementation of splitting tokens into logical
## groups defided by respective list of logical operator
## defined for respective search key. """
## items = []
## last_operator = None
## operators_mapping_list = []
## last_operator = {'operator': None,
## 'tokens': []}
## for token in tokens:
## token_type = token.type
## token_value = token.value
## if token_type in group_by_operators_list:
## # (re) init it
## last_operator = {'operator': token,
## 'tokens': []}
## operators_mapping_list.append(last_operator)
## else:
## # not an operator just a value token
## last_operator['tokens'].append(token)
## if last_operator not in operators_mapping_list:
## operators_mapping_list.append(last_operator)
## return operators_mapping_list
...@@ -29,34 +29,34 @@ ...@@ -29,34 +29,34 @@
from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query
from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery
from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance
from Key import BaseKey from SearchKey import SearchKey
from pprint import pprint from pprint import pprint
class KeyWordKey(BaseKey): class KeyWordKey(SearchKey):
""" KeyWordKey key is an ERP5 portal_catalog search key which is used to render """ KeyWordKey key is an ERP5 portal_catalog search key which is used to render
SQL expression that will try to match all possible values in a greedy manner. SQL expression that will try to match all possible values in a greedy manner.
It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in It supports following special operator ['=', '%', '>' , '>=', '<', '<='] in
addition to main logical operators like ['OR', 'or', 'AND', 'and']. addition to main logical operators like ['OR', 'or', 'AND', 'and'].
Examples for title column: Examples for title column:
* 'foo or bar' --> "title LIKE '%foo%' OR title LIKE '%bar%'" * 'foo or bar' --> "title LIKE '%foo%' OR title LIKE '%bar%'"
* 'foo or =bar' --> "title LIKE '%foo%' OR title = 'bar'" * 'foo or =bar' --> "title LIKE '%foo%' OR title = 'bar'"
* 'Organisation Module' --> "title LIKE '%Organisation Module%'" * 'Organisation Module' --> "title LIKE '%Organisation Module%'"
* '"Organisation Module"' --> "title LIKE '%Organisation Module%'" * '"Organisation Module"' --> "title LIKE '%Organisation Module%'"
* '="Organisation Module"' --> "title = 'Organisation Module'" * '="Organisation Module"' --> "title = 'Organisation Module'"
""" """
tokens = ('OR', 'AND', 'NOT', tokens = ('OR', 'AND', 'NOT',
'KEYWORD', 'WORDSET', 'WORD', 'EXPLICITEQUALLITYWORD', 'KEYWORD', 'WORDSET', 'WORD', 'EXPLICITEQUALLITYWORD',
'GREATERTHAN', 'GREATERTHANEQUAL', 'GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL') 'LESSTHAN', 'LESSTHANEQUAL')
sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL', sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL', 'NOT') 'LESSTHAN', 'LESSTHANEQUAL', 'NOT')
# this is the default operator # this is the default operator
default_operator = 'like' default_operator = 'like'
# if token's list starts with left sided operator # if token's list starts with left sided operator
# use this map to transfer it to range operator # use this map to transfer it to range operator
...@@ -98,8 +98,8 @@ class KeyWordKey(BaseKey): ...@@ -98,8 +98,8 @@ class KeyWordKey(BaseKey):
value = t.value.strip() value = t.value.strip()
# get rid of leading '=' # get rid of leading '='
t.value = value[1:] t.value = value[1:]
return t return t
def t_KEYWORD(self, t): def t_KEYWORD(self, t):
r'%?[\x7F-\xFF\w\d/~!@#$%^&*()_+][\x7F-\xFF\w\d/~!@#$%^&*()_+]*%?' r'%?[\x7F-\xFF\w\d/~!@#$%^&*()_+][\x7F-\xFF\w\d/~!@#$%^&*()_+]*%?'
# KEYWORD may starts(1) and may ends (2) with '%' but always must either #1 or #2 # KEYWORD may starts(1) and may ends (2) with '%' but always must either #1 or #2
......
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
# #
############################################################################## ##############################################################################
class RawKey: from SearchKey import SearchKey
class RawKey(SearchKey):
""" RawKey key is an ERP5 portal_catalog search key which is used to render """ RawKey key is an ERP5 portal_catalog search key which is used to render
SQL expression that will match exactly what's passed to it using equality .""" SQL expression that will match exactly what's passed to it using equality ."""
...@@ -34,8 +36,8 @@ class RawKey: ...@@ -34,8 +36,8 @@ class RawKey:
# this key doesn't require parsing # this key doesn't require parsing
# It's required to implement it as it's used ONLY for ExactMath # It's required to implement it as it's used ONLY for ExactMath
pass pass
def buildSQLExpression(self, key, value, def buildSQLExpression(self, key, value,
format=None, mode=None, range_value=None, stat__=None): format=None, mode=None, range_value=None, stat__=None):
where_expression = "%s = '%s'" %(key, value) where_expression = "%s = '%s'" %(key, value)
return where_expression, [] return where_expression, []
...@@ -31,43 +31,42 @@ from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery ...@@ -31,43 +31,42 @@ from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery
from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance
from Products.PythonScripts.Utility import allow_class from Products.PythonScripts.Utility import allow_class
from Key import BaseKey from SearchKey import SearchKey
from pprint import pprint from pprint import pprint
# these keys are used to build query in case for ScriptableKey # these keys are used to build query in case for ScriptableKey
# when no key was specified in fornt of value # when no key was specified in fornt of value
DEFAULT_SEARCH_KEYS = ('SearchableText', 'reference', 'title',) DEFAULT_SEARCH_KEYS = ('SearchableText', 'reference', 'title',)
class KeyMappingKey(BaseKey): class KeyMappingKey(SearchKey):
""" Usable lexer class used (internally) by ScriptableKey lexer than can parse following: """ Usable lexer class used (internally) by ScriptableKey lexer than can parse following:
VALUE OPERATOR VALUE VALUE OPERATOR VALUE
Examples: Examples:
* "portal_type : Person" * "portal_type : Person"
* "creation_date > 2007-01-01" * "creation_date > 2007-01-01"
""" """
tokens = ('OPERATOR', 'COLONOPERATOR', 'VALUE',) tokens = ('OPERATOR', 'COLONOPERATOR', 'VALUE',)
t_OPERATOR = r'>=|<=|>|<' t_OPERATOR = r'>=|<=|>|<'
t_VALUE = r'[\x7F-\xFF\w\d\/~!@#$^&*()_+-][\x7F-\xFF\w\d\/~!@#$^&*()_+-]*' t_VALUE = r'[\x7F-\xFF\w\d\/~!@#$^&*()_+-][\x7F-\xFF\w\d\/~!@#$^&*()_+-]*'
def t_COLONOPERATOR(self, t): def t_COLONOPERATOR(self, t):
r':' r':'
# ':' is the same as '=' (equality) # ':' is the same as '=' (equality)
t.value = '=' t.value = '='
return t return t
class ScriptableKey(SearchKey):
class ScriptableKey(BaseKey):
""" KeyWordKey key is an ERP5 portal_catalog search key which is used to generate a """ KeyWordKey key is an ERP5 portal_catalog search key which is used to generate a
ComplexQuery instance out of an arbitrary search string. ComplexQuery instance out of an arbitrary search string.
Examples: Examples:
* "John Doe AND portal_type:Person AND creation_date > 2007-01-01" * "John Doe AND portal_type:Person AND creation_date > 2007-01-01"
would be turned into following ComplexQuery: would be turned into following ComplexQuery:
* ComplexQuery(Query(portal_type='Person'), * ComplexQuery(Query(portal_type='Person'),
Query(creation_date='2007-01-01', operator='>'), Query(creation_date='2007-01-01', operator='>'),
ComplexQuery(Query(searchable_text='John Doe'), ComplexQuery(Query(searchable_text='John Doe'),
...@@ -78,18 +77,18 @@ class ScriptableKey(BaseKey): ...@@ -78,18 +77,18 @@ class ScriptableKey(BaseKey):
""" """
sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL', sub_operators = ('GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL',) 'LESSTHAN', 'LESSTHANEQUAL',)
tokens = ('OR', 'AND', tokens = ('OR', 'AND',
'DATE', 'WORD', 'KEYMAPPING', 'DATE', 'WORD', 'KEYMAPPING',
'GREATERTHAN', 'GREATERTHANEQUAL', 'GREATERTHAN', 'GREATERTHANEQUAL',
'LESSTHAN', 'LESSTHANEQUAL', 'EQUAL') 'LESSTHAN', 'LESSTHANEQUAL', 'EQUAL')
t_GREATERTHANEQUAL = r'>=' t_GREATERTHANEQUAL = r'>='
t_LESSTHANEQUAL = r'<=' t_LESSTHANEQUAL = r'<='
t_GREATERTHAN = r'>' t_GREATERTHAN = r'>'
t_LESSTHAN = r'<' t_LESSTHAN = r'<'
t_EQUAL = r'=' t_EQUAL = r'='
# Note: Order of placing rules (t_WORD for example) is very important # Note: Order of placing rules (t_WORD for example) is very important
def t_OR(self, t): def t_OR(self, t):
r'(\s+OR\s+|\s+or\s+)' r'(\s+OR\s+|\s+or\s+)'
...@@ -113,14 +112,14 @@ class ScriptableKey(BaseKey): ...@@ -113,14 +112,14 @@ class ScriptableKey(BaseKey):
value = t.value.strip() value = t.value.strip()
t.value = value t.value = value
return t return t
def t_WORD(self, t): def t_WORD(self, t):
r'[\x7F-\xFF\w\d\/~!@#$^&*()_+][\x7F-\xFF\w\d\/~!@#$^&*()_+]*' r'[\x7F-\xFF\w\d\/~!@#$^&*()_+][\x7F-\xFF\w\d\/~!@#$^&*()_+]*'
# WORD may contain arbitrary letters and numbers without white space # WORD may contain arbitrary letters and numbers without white space
# WORD may contain '%' but not at the beginning or end (otherwise it's KEYWORD) # WORD may contain '%' but not at the beginning or end (otherwise it's KEYWORD)
value = t.value.strip() value = t.value.strip()
t.value = value t.value = value
return t return t
def buildQueryForTokenList(self, tokens): def buildQueryForTokenList(self, tokens):
""" Build a ComplexQuery for a token list """ """ Build a ComplexQuery for a token list """
...@@ -164,31 +163,31 @@ class ScriptableKey(BaseKey): ...@@ -164,31 +163,31 @@ class ScriptableKey(BaseKey):
**{'operator':'AND'}) **{'operator':'AND'})
return complex_query return complex_query
def buildQuery(self, key, value, def buildQuery(self, key, value,
format=None, mode=None, range_value=None, stat__=None): format=None, mode=None, range_value=None, stat__=None):
""" Build ComplexQuery from passed search string value. """ Build ComplexQuery from passed search string value.
When grouping expressions we use the following assumptions When grouping expressions we use the following assumptions
that 'OR' operator has higher priority in a sense: that 'OR' operator has higher priority in a sense:
* "John Doe AND portal_type:Person OR creation_date>=2005/12/12" * "John Doe AND portal_type:Person OR creation_date>=2005/12/12"
is considered as: is considered as:
* (John Doe AND portal_type:Person) OR (creation_date>=2005/12/12)" * (John Doe AND portal_type:Person) OR (creation_date>=2005/12/12)"
""" """
query_list = [] query_list = []
tokens = self.tokenize(value) tokens = self.tokenize(value)
# split tokens list into one or many OR concatanated expressions # split tokens list into one or many OR concatanated expressions
sub_tokens_or_groups = self.groupByLogicalOperator(tokens, 'OR') sub_tokens_or_groups = self.groupByLogicalOperator(tokens, 'OR')
# get a ComplexQuery for a sub token list # get a ComplexQuery for a sub token list
for tokens_or_group in sub_tokens_or_groups: for tokens_or_group in sub_tokens_or_groups:
query_list.append(self.buildQueryForTokenList(tokens_or_group)) query_list.append(self.buildQueryForTokenList(tokens_or_group))
# join query list in one really big ComplexQuery # join query list in one really big ComplexQuery
complex_query = ComplexQuery(*query_list, complex_query = ComplexQuery(*query_list,
**{'operator':'OR'}) **{'operator':'OR'})
return complex_query return complex_query
allow_class(ScriptableKey) allow_class(ScriptableKey)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment