Commit d2e055cf authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

support sort on both the full text search score and the column value itself.

sort_on=(('title',),) => sort by title
sort_on=(('title__score__',),) => sort by full text search score for title
parent a37abd00
...@@ -80,6 +80,12 @@ class TestI18NSearch(ERP5TypeTestCase): ...@@ -80,6 +80,12 @@ class TestI18NSearch(ERP5TypeTestCase):
# check fulltext search for automatically generated related keys. # check fulltext search for automatically generated related keys.
self.assertTrue('MATCH' in self.portal.portal_catalog(destination_title='Faure', src__=1)) self.assertTrue('MATCH' in self.portal.portal_catalog(destination_title='Faure', src__=1))
# check sort on fulltext column
self.assertFalse('ORDER BY\n MATCH' in self.portal.portal_catalog(title='Faure', sort_on=(('title', 'ascending'),), src__=1))
# check sort on fulltext search score
self.assertTrue('ORDER BY\n MATCH' in self.portal.portal_catalog(title='Faure', sort_on=(('title__score__', 'ascending'),), src__=1))
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestI18NSearch)) suite.addTest(unittest.makeSuite(TestI18NSearch))
......
...@@ -453,11 +453,16 @@ class ColumnMap(object): ...@@ -453,11 +453,16 @@ class ColumnMap(object):
'.' in raw_column or '*' in raw_column: '.' in raw_column or '*' in raw_column:
result = raw_column result = raw_column
else: else:
if raw_column.endswith('__score__'):
raw_column = raw_column[:-9]
column_suffix = '__score__'
else:
column_suffix = ''
function, column = self.raw_column_dict.get(raw_column, (None, raw_column)) function, column = self.raw_column_dict.get(raw_column, (None, raw_column))
if group is DEFAULT_GROUP_ID: if group is DEFAULT_GROUP_ID:
group, column = self.related_key_dict.get(column, (group, raw_column)) group, column = self.related_key_dict.get(column, (group, raw_column))
alias = self.table_alias_dict[(group, self.column_map[(group, column)])] alias = self.table_alias_dict[(group, self.column_map[(group, column)])]
result = '`%s`.`%s`' % (alias, column) result = '`%s`.`%s%s`' % (alias, column, column_suffix)
if function is not None: if function is not None:
result = '%s(%s)' % (function, result) result = '%s(%s)' % (function, result)
return result return result
......
...@@ -117,10 +117,9 @@ class MatchComparisonOperator(MonovaluedComparisonOperator): ...@@ -117,10 +117,9 @@ class MatchComparisonOperator(MonovaluedComparisonOperator):
select_dict = {} select_dict = {}
if not only_group_columns: if not only_group_columns:
select_dict['%s__score__' % column.replace('`', '').rsplit('.', 1)[-1]] = match_string select_dict['%s__score__' % column.replace('`', '').rsplit('.', 1)[-1]] = match_string
# Sort on this column uses relevance. # Support sort on the relevance by using (column)__score__ key.
# TODO: Add a way to allow sorting by raw column value.
order_by_dict = { order_by_dict = {
column: match_string, '`%s__score__`' % column.strip('`'): match_string,
} }
return SQLExpression( return SQLExpression(
self, self,
......
...@@ -505,12 +505,12 @@ class TestSQLCatalog(ERP5TypeTestCase): ...@@ -505,12 +505,12 @@ class TestSQLCatalog(ERP5TypeTestCase):
self.catalog(reference_query, {'fulltext': 'a NOT b'}) self.catalog(reference_query, {'fulltext': 'a NOT b'})
# The same, with an order by, must raise # The same, with an order by, must raise
self.assertRaises(MergeConflictError, self.catalog, reference_query, self.assertRaises(MergeConflictError, self.catalog, reference_query,
{'fulltext': 'a NOT b', 'order_by_list': [('fulltext', ), ]}, {'fulltext': 'a NOT b', 'order_by_list': [('fulltext__score__', ), ]},
check_search_text=False) check_search_text=False)
# If one want to sort on, he must use the equivalent FullText syntax: # If one want to sort on, he must use the equivalent FullText syntax:
self.catalog(ReferenceQuery(ReferenceQuery(operator='mroonga', self.catalog(ReferenceQuery(ReferenceQuery(operator='mroonga',
fulltext=MatchList(['a -b', '-b a'])), operator='and'), fulltext=MatchList(['a -b', '-b a'])), operator='and'),
{'fulltext': 'a -b', 'order_by_list': [('fulltext', ), ]}, {'fulltext': 'a -b', 'order_by_list': [('fulltext__score__', ), ]},
check_search_text=False) check_search_text=False)
self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'), self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'),
ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='b'), operator='not'), operator='or'), operator='and'), ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='b'), operator='not'), operator='or'), operator='and'),
...@@ -727,19 +727,26 @@ class TestSQLCatalog(ERP5TypeTestCase): ...@@ -727,19 +727,26 @@ class TestSQLCatalog(ERP5TypeTestCase):
'order_by_list': [('fulltext', ), ]}) 'order_by_list': [('fulltext', ), ]})
order_by_expression = sql_expression.getOrderByExpression() order_by_expression = sql_expression.getOrderByExpression()
self.assertNotEqual(order_by_expression, '') self.assertNotEqual(order_by_expression, '')
# ... and not sort by relevance
self.assertFalse('MATCH' in order_by_expression, order_by_expression)
# order_by_list on fulltext column + '__score__, resulting "ORDER BY" must be non-empty.
sql_expression = self.asSQLExpression({'fulltext': 'foo',
'order_by_list': [('fulltext__score__', ), ]})
order_by_expression = sql_expression.getOrderByExpression()
self.assertNotEqual(order_by_expression, '')
# ... and must sort by relevance # ... and must sort by relevance
self.assertTrue('MATCH' in order_by_expression, order_by_expression) self.assertTrue('MATCH' in order_by_expression, order_by_expression)
# ordering on fulltext column with sort order specified must preserve # ordering on fulltext column with sort order specified must preserve
# sorting by relevance. # sorting by relevance.
for direction in ('ASC', 'DESC'): for direction in ('ASC', 'DESC'):
sql_expression = self.asSQLExpression({'fulltext': 'foo', sql_expression = self.asSQLExpression({'fulltext': 'foo',
'order_by_list': [('fulltext', direction), ]}) 'order_by_list': [('fulltext__score__', direction), ]})
order_by_expression = sql_expression.getOrderByExpression() order_by_expression = sql_expression.getOrderByExpression()
self.assertTrue('MATCH' in order_by_expression, (order_by_expression, direction)) self.assertTrue('MATCH' in order_by_expression, (order_by_expression, direction))
# Providing a None cast should work too # Providing a None cast should work too
for direction in ('ASC', 'DESC'): for direction in ('ASC', 'DESC'):
sql_expression = self.asSQLExpression({'fulltext': 'foo', sql_expression = self.asSQLExpression({'fulltext': 'foo',
'order_by_list': [('fulltext', direction, None), ]}) 'order_by_list': [('fulltext__score__', direction, None), ]})
order_by_expression = sql_expression.getOrderByExpression() order_by_expression = sql_expression.getOrderByExpression()
self.assertTrue('MATCH' in order_by_expression, (order_by_expression, direction)) self.assertTrue('MATCH' in order_by_expression, (order_by_expression, direction))
......
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