Commit 139911ef authored by Vincent Pelletier's avatar Vincent Pelletier

Add support for SearchText expression inside dict parameters.

parent c84825e9
...@@ -2019,12 +2019,13 @@ class Catalog(Folder, ...@@ -2019,12 +2019,13 @@ class Catalog(Folder,
return self.getColumnSearchKey(column)[0] is not None return self.getColumnSearchKey(column)[0] is not None
@profiler_decorator @profiler_decorator
def getColumnDefaultSearchKey(self, key): def getColumnDefaultSearchKey(self, key, search_key_name=None):
""" """
Return a SearchKey instance which would ultimately receive the value Return a SearchKey instance which would ultimately receive the value
associated with given key. associated with given key.
""" """
search_key, related_key_definition = self.getColumnSearchKey(key) search_key, related_key_definition = self.getColumnSearchKey(key,
search_key_name=search_key_name)
if search_key is None: if search_key is None:
result = None result = None
else: else:
...@@ -2062,13 +2063,13 @@ class Catalog(Folder, ...@@ -2062,13 +2063,13 @@ class Catalog(Folder,
return result return result
@profiler_decorator @profiler_decorator
def _buildQueryFromAbstractSyntaxTreeNode(self, node, search_key): def _buildQueryFromAbstractSyntaxTreeNode(self, node, search_key, wrap):
if search_key.dequoteParsedText(): if search_key.dequoteParsedText():
_dequote = dequote _dequote = dequote
else: else:
_dequote = lambda x: x _dequote = lambda x: x
if node.isLeaf(): if node.isLeaf():
result = search_key.buildQuery(_dequote(node.getValue()), result = search_key.buildQuery(wrap(_dequote(node.getValue())),
comparison_operator=node.getComparisonOperator()) comparison_operator=node.getComparisonOperator())
elif node.isColumn(): elif node.isColumn():
result = self.buildQueryFromAbstractSyntaxTreeNode(node.getSubNode(), node.getColumnName()) result = self.buildQueryFromAbstractSyntaxTreeNode(node.getSubNode(), node.getColumnName())
...@@ -2079,9 +2080,9 @@ class Catalog(Folder, ...@@ -2079,9 +2080,9 @@ class Catalog(Folder,
for subnode in node.getNodeList(): for subnode in node.getNodeList():
if subnode.isLeaf(): if subnode.isLeaf():
value_dict.setdefault(subnode.getComparisonOperator(), value_dict.setdefault(subnode.getComparisonOperator(),
[]).append(_dequote(subnode.getValue())) []).append(wrap(_dequote(subnode.getValue())))
else: else:
subquery = self._buildQueryFromAbstractSyntaxTreeNode(subnode, search_key) subquery = self._buildQueryFromAbstractSyntaxTreeNode(subnode, search_key, wrap)
if subquery is not None: if subquery is not None:
append(subquery) append(subquery)
logical_operator = node.getLogicalOperator() logical_operator = node.getLogicalOperator()
...@@ -2100,7 +2101,7 @@ class Catalog(Folder, ...@@ -2100,7 +2101,7 @@ class Catalog(Folder,
return result return result
@profiler_decorator @profiler_decorator
def buildQueryFromAbstractSyntaxTreeNode(self, node, key): def buildQueryFromAbstractSyntaxTreeNode(self, node, key, wrap=lambda x: x):
""" """
Build a query from given Abstract Syntax Tree (AST) node by recursing in Build a query from given Abstract Syntax Tree (AST) node by recursing in
its childs. its childs.
...@@ -2129,7 +2130,8 @@ class Catalog(Folder, ...@@ -2129,7 +2130,8 @@ class Catalog(Folder,
else: else:
build_key = search_key.getSearchKey(sql_catalog=self, build_key = search_key.getSearchKey(sql_catalog=self,
related_key_definition=related_key_definition) related_key_definition=related_key_definition)
result = self._buildQueryFromAbstractSyntaxTreeNode(node, build_key) result = self._buildQueryFromAbstractSyntaxTreeNode(node, build_key,
wrap)
if related_key_definition is not None: if related_key_definition is not None:
result = search_key.buildQuery(sql_catalog=self, result = search_key.buildQuery(sql_catalog=self,
related_key_definition=related_key_definition, related_key_definition=related_key_definition,
...@@ -2180,22 +2182,7 @@ class Catalog(Folder, ...@@ -2180,22 +2182,7 @@ class Catalog(Folder,
empty_value_dict[key] = value empty_value_dict[key] = value
else: else:
script = self.getScriptableKeyScript(key) script = self.getScriptableKeyScript(key)
if isinstance(value, _Query): if isinstance(value, dict):
# Query instance: use as such, ignore key.
result = value
elif script is not None:
result = script(value)
elif isinstance(value, basestring):
# String: parse using key's default search key.
search_key = self.getColumnDefaultSearchKey(key)
if search_key is not None:
abstract_syntax_tree = self._parseSearchText(search_key, value)
if abstract_syntax_tree is None:
# Parsing failed, create a query from the bare string.
result = self.buildSingleQuery(key, value)
else:
result = self.buildQueryFromAbstractSyntaxTreeNode(abstract_syntax_tree, key)
elif isinstance(value, dict):
# Dictionnary: might contain the search key to use. # Dictionnary: might contain the search key to use.
search_key_name = value.get('key') search_key_name = value.get('key')
# Backward compatibility: former "Keyword" key is now named # Backward compatibility: former "Keyword" key is now named
...@@ -2206,7 +2193,37 @@ class Catalog(Folder, ...@@ -2206,7 +2193,37 @@ class Catalog(Folder,
# as "RawKey" # as "RawKey"
elif search_key_name == 'ExactMatch': elif search_key_name == 'ExactMatch':
search_key_name = value['key'] = 'RawKey' search_key_name = value['key'] = 'RawKey'
result = self.buildSingleQuery(key, value, search_key_name) if isinstance(value, _Query):
# Query instance: use as such, ignore key.
result = value
elif script is not None:
result = script(value)
elif isinstance(value, (basestring, dict)):
# String: parse using key's default search key.
raw_value = value
if isinstance(value, dict):
# De-wrap value for parsing, and re-wrap when building queries.
def wrap(x):
result = raw_value.copy()
result['query'] = x
return result
value = value['query']
else:
wrap = lambda x: x
search_key_name = None
search_key = self.getColumnDefaultSearchKey(key,
search_key_name=search_key_name)
if search_key is not None:
if isinstance(value, basestring):
abstract_syntax_tree = self._parseSearchText(search_key, value)
else:
abstract_syntax_tree = None
if abstract_syntax_tree is None:
# Parsing failed, create a query from the bare string.
result = self.buildSingleQuery(key, raw_value, search_key_name)
else:
result = self.buildQueryFromAbstractSyntaxTreeNode(
abstract_syntax_tree, key, wrap)
else: else:
# Any other type, just create a query. (can be a DateTime, ...) # Any other type, just create a query. (can be a DateTime, ...)
result = self.buildSingleQuery(key, value) result = self.buildSingleQuery(key, value)
......
...@@ -258,8 +258,11 @@ class DateTimeKey(SearchKey): ...@@ -258,8 +258,11 @@ class DateTimeKey(SearchKey):
query_list = [] query_list = []
extend = query_list.extend extend = query_list.extend
for comparison_operator, value_list in operator_value_dict.iteritems(): for comparison_operator, value_list in operator_value_dict.iteritems():
reference_value = value_list[0]
if isinstance(reference_value, dict):
reference_value = reference_value['query']
try: try:
if parsed: if isinstance(reference_value, basestring):
subquery_list = operator_matcher_dict[comparison_operator]( subquery_list = operator_matcher_dict[comparison_operator](
self, group, column, value_list, comparison_operator, self, group, column, value_list, comparison_operator,
logical_operator) logical_operator)
......
...@@ -299,11 +299,16 @@ class TestSQLCatalog(ERP5TypeTestCase): ...@@ -299,11 +299,16 @@ class TestSQLCatalog(ERP5TypeTestCase):
def _testDateTimeKey(self, column, timezone): def _testDateTimeKey(self, column, timezone):
self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21')), operator='and'), self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21')), operator='and'),
{column: {'query': '>2008/10/01 12:10:20', 'format': '%Y/%m/%d'}}) {column: {'query': '>"2008/10/01 12:10:20"', 'format': '%Y/%m/%d', 'type': 'date'}})
self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21')),
ReferenceQuery(operator='<', date=DateTime('2008/10/02 10:00:00')),
operator='and'), operator='and'),
{column: {'query': '>"2008/10/01 12:10:20" AND <"2008/10/02 10:00:00"', 'format': '%Y/%m/%d', 'type': 'date'}})
self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21 CEST')), operator='and'), self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21 CEST')), operator='and'),
{column: {'query': '>2008/10/01 12:10:20 CEST', 'format': '%Y/%m/%d'}}) {column: {'query': '>"2008/10/01 12:10:20 CEST"', 'format': '%Y/%m/%d', 'type': 'date'}})
self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21 CET')), operator='and'), self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/10/01 12:10:21 CET')), operator='and'),
{column: {'query': '>2008/10/01 12:10:20 CET', 'format': '%Y/%m/%d'}}) {column: {'query': '>"2008/10/01 12:10:20 CET"', 'format': '%Y/%m/%d', 'type': 'date'}})
self.catalog(ReferenceQuery(ReferenceQuery( self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/10/01 %s' % timezone)), ReferenceQuery(operator='>=', date=DateTime('2008/10/01 %s' % timezone)),
ReferenceQuery(operator='<', date=DateTime('2008/10/02 %s' % timezone)) ReferenceQuery(operator='<', date=DateTime('2008/10/02 %s' % timezone))
...@@ -338,10 +343,31 @@ class TestSQLCatalog(ERP5TypeTestCase): ...@@ -338,10 +343,31 @@ class TestSQLCatalog(ERP5TypeTestCase):
ReferenceQuery(operator='<', date=DateTime('2008/10/02 %s' % timezone)) ReferenceQuery(operator='<', date=DateTime('2008/10/02 %s' % timezone))
, operator='and'), operator='and'), , operator='and'), operator='and'),
{column: {'type': 'date', 'query': '01/10/2008 %s' % timezone, 'format': '%d/%m/%Y'}}) {column: {'type': 'date', 'query': '01/10/2008 %s' % timezone, 'format': '%d/%m/%Y'}})
self.catalog(ReferenceQuery(ReferenceQuery(operator='in', date=[DateTime('2008/01/10 %s' % timezone), DateTime('2008/01/09 %s' % timezone)]), operator='and'), self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/01/10 ' + timezone)),
ReferenceQuery(operator='<', date=DateTime('2008/01/11 ' + timezone)),
operator='and'),
ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/01/09 ' + timezone)),
ReferenceQuery(operator='<', date=DateTime('2008/01/10 ' + timezone)),
operator='and'),
operator='or'), operator='and'),
{column: {'query': ['2008/01/10 %s' % timezone, '2008/01/09 %s' % timezone], 'operator': 'in'}}, {column: {'query': ['2008/01/10 %s' % timezone, '2008/01/09 %s' % timezone], 'operator': 'in'}},
check_search_text=False) check_search_text=False)
self.catalog(ReferenceQuery(ReferenceQuery(operator='>', date=DateTime('2008/01/10 %s' % timezone)), operator='and'), self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/01/10 ' + timezone)),
ReferenceQuery(operator='<', date=DateTime('2008/01/11 ' + timezone)),
operator='and'),
ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2008/01/09 ' + timezone)),
ReferenceQuery(operator='<', date=DateTime('2008/01/10 ' + timezone)),
operator='and'),
operator='or'), operator='and'),
{column: ['2008/01/10 %s' % timezone, '2008/01/09 %s' % timezone]},
check_search_text=False)
self.catalog(ReferenceQuery(ReferenceQuery(operator='>=', date=DateTime('2008/01/11 %s' % timezone)), operator='and'),
{column: {'query': '2008/01/10 %s' % timezone, 'range': 'nlt'}}, {column: {'query': '2008/01/10 %s' % timezone, 'range': 'nlt'}},
check_search_text=False) check_search_text=False)
self.catalog(ReferenceQuery(ReferenceQuery( self.catalog(ReferenceQuery(ReferenceQuery(
...@@ -707,6 +733,38 @@ class TestSQLCatalog(ERP5TypeTestCase): ...@@ -707,6 +733,38 @@ class TestSQLCatalog(ERP5TypeTestCase):
operator='and'), operator='and'),
{'default': 'AN OR ORB'}) {'default': 'AN OR ORB'})
def _searchTextInDictQuery(self, column):
self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2001/08/11')),
ReferenceQuery(operator='<', date=DateTime('2008/10/01')),
operator='and'), operator='and'),
{
column: {'query': '>2001/08/10 AND <2008/10/01', 'format': '%d/%m/%Y', 'type': 'date'},
}
)
# Ambiguous date representation with format: dmY
self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2001/08/11')),
ReferenceQuery(operator='<', date=DateTime('2008/10/01')),
operator='and'), operator='and'),
{
column: {'query': '>10/08/2001 AND <01/10/2008', 'format': '%d/%m/%Y', 'type': 'date'},
}
)
# Ambiguous date representation with format: mdY, same input as above
self.catalog(ReferenceQuery(ReferenceQuery(
ReferenceQuery(operator='>=', date=DateTime('2001/10/09')),
ReferenceQuery(operator='<', date=DateTime('2008/01/10')),
operator='and'), operator='and'),
{
column: {'query': '>10/08/2001 AND <01/10/2008', 'format': '%m/%d/%Y', 'type': 'date'},
}
)
def test_searchTextInDictQuery(self):
self._searchTextInDictQuery('date')
self._searchTextInDictQuery('related_date')
##return catalog(title=Query(title='a', operator='not')) ##return catalog(title=Query(title='a', operator='not'))
#return catalog(title={'query': 'a', 'operator': 'not'}) #return catalog(title={'query': 'a', 'operator': 'not'})
#return catalog(title={'query': ['a', 'b'], 'operator': 'not'}) #return catalog(title={'query': ['a', 'b'], 'operator': 'not'})
......
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