Order the packages for parameter search

Previously, list packages with parameter 'search',
search content in any attributes, but packages are unordered
With this change, packages are  ordered.
Such as: list package with 'search=mysql'
the packages whose name like 'mysql' are ahead of others
whose afn like 'mysql',and so on

Change-Id: Iecc3038ac40e731788975cff0cdf7d42e7f97112
Closes-Bug:#1467487
This commit is contained in:
Huangsm 2016-01-28 01:00:19 -05:00 committed by huangsm
parent 3ef1d83c91
commit 82bd5858e3
4 changed files with 112 additions and 18 deletions

View File

@ -82,7 +82,7 @@ For an admin role all packages are available.
+----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+
| ``include_disabled`` | bool | Include disabled packages in a the result | | ``include_disabled`` | bool | Include disabled packages in a the result |
+----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+
| ``search`` | string | Gives opportunity to search specified data by all the package parameters | | ``search`` | string | Gives opportunity to search specified data by all the package parameters and order packages |
+----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+
| ``class_name`` | string | Search only for packages, that use specified class | | ``class_name`` | string | Search only for packages, that use specified class |
+----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+

View File

@ -16,6 +16,7 @@ from oslo_db import api as oslo_db_api
from oslo_db import exception as db_exceptions from oslo_db import exception as db_exceptions
from oslo_db.sqlalchemy import utils from oslo_db.sqlalchemy import utils
from oslo_log import log as logging from oslo_log import log as logging
import re
import six import six
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import or_ from sqlalchemy import or_
@ -340,26 +341,43 @@ def package_search(filters, context, manage_public=False,
fk_fields = {'categories': 'Category', fk_fields = {'categories': 'Category',
'tags': 'Tag', 'tags': 'Tag',
'class_definitions': 'Class'} 'class_definitions': 'Class'}
conditions = [] # the default search order
fields = ['name',
'fully_qualified_name',
'description',
'categories',
'tags',
'class_definitions',
'author']
# split to searching words
key_words = re.split(';|,', filters['search'])
for attr in dir(pkg): conditions = []
order_cases = []
sorted_fields = fields + list(set(dir(pkg)).difference(set(fields)))
for index in range(0, len(sorted_fields)):
attr = sorted_fields[index]
if attr.startswith('_'): if attr.startswith('_'):
continue continue
if isinstance(getattr(pkg, attr), if not isinstance(getattr(pkg, attr),
attributes.InstrumentedAttribute): attributes.InstrumentedAttribute):
search_str = filters['search'] continue
for delim in ',;': priority = min(index, len(fields))
search_str = search_str.replace(delim, ' ') for key_word in key_words:
for key_word in search_str.split(): _word = u'%{value}%'.format(value=key_word)
_word = u'%{value}%'.format(value=key_word) if attr in fk_fields.keys():
if attr in fk_fields.keys(): condition = getattr(pkg, attr).any(
condition = getattr(pkg, attr).any( getattr(models, fk_fields[attr]).name.like(_word))
getattr(models, fk_fields[attr]).name.like(_word)) conditions.append(condition)
conditions.append(condition) order_cases.append((condition, priority))
elif isinstance(getattr(pkg, attr) elif isinstance(getattr(pkg, attr)
.property.columns[0].type, sa.String): .property.columns[0].type, sa.String):
conditions.append(getattr(pkg, attr).like(_word)) condition = getattr(pkg, attr).like(_word)
query = query.filter(or_(*conditions)) conditions.append(condition)
order_cases.append((condition, priority))
order_expression = sa.case(order_cases).label("tmp_weight_uuid")
query = query.filter(or_(*conditions)).order_by(order_expression.asc())
sort_keys = [SEARCH_MAPPING[sort_key] for sort_key in sort_keys = [SEARCH_MAPPING[sort_key] for sort_key in
filters.get('order_by', ['name'])] filters.get('order_by', ['name'])]

View File

@ -562,6 +562,57 @@ class TestCatalogApi(test_base.ControllerTest, test_base.MuranoApiTestCase):
self.assertIn("Search by parameter 'tag' caused an error", self.assertIn("Search by parameter 'tag' caused an error",
warnings[0]) warnings[0])
def test_packages_filter_by_search(self):
self._set_policy_rules(
{'get_package': '',
'manage_public_package': ''}
)
excepted_pkg1 = self._add_pkg("test_tenant",
type='Application',
name='awcloud',
description='some context')
excepted_pkg2 = self._add_pkg("test_tenant",
type='Application',
name='mysql',
description='awcloud product')
excepted_pkg3 = self._add_pkg("test_tenant",
type='Application',
name='package3',
author='mysql author')
# filter by search=awcloud can see 2 pkgs
req_awc = self._get('/catalog/packages',
params={'search': 'awcloud'})
# filter by search=mysql only see 2 pkgs
req_mysql = self._get('/catalog/packages',
params={'search': 'mysql'})
for dummy in range(2):
self.expect_policy_check('get_package')
self.expect_policy_check('manage_public_package')
res_awc = req_awc.get_response(self.api)
self.assertEqual(200, res_awc.status_code)
self.assertEqual(2, len(res_awc.json['packages']))
self.assertEqual(excepted_pkg1.name,
res_awc.json['packages'][0]['name'])
self.assertEqual(excepted_pkg2.name,
res_awc.json['packages'][1]['name'])
res_mysql = req_mysql.get_response(self.api)
self.assertEqual(200, res_mysql.status_code)
self.assertEqual(2, len(res_mysql.json['packages']))
self.assertEqual(excepted_pkg2.name,
res_mysql.json['packages'][0]['name'])
self.assertEqual(excepted_pkg3.name,
res_mysql.json['packages'][1]['name'])
def test_packages(self): def test_packages(self):
self._set_policy_rules( self._set_policy_rules(
{'get_package': '', {'get_package': '',

View File

@ -212,6 +212,31 @@ class CatalogDBTestCase(base.MuranoWithDBTestCase):
res = api.package_search({'marker': marker}, self.context, limit=4) res = api.package_search({'marker': marker}, self.context, limit=4)
self.assertEqual(0, len(res)) self.assertEqual(0, len(res))
def test_package_search_search_order(self):
pkg1 = api.package_upload(
self._stub_package(
fully_qualified_name=str(uuid.uuid4()),
name='mysql',
description='awcloud'),
self.tenant_id)
pkg2 = api.package_upload(
self._stub_package(
fully_qualified_name=str(uuid.uuid4()),
name='awcloud',
description='mysql'),
self.tenant_id)
api.package_upload(
self._stub_package(
tags=[],
fully_qualified_name=str(uuid.uuid4())),
self.tenant_id)
res = api.package_search(
{'search': 'mysql'}, self.context)
self.assertEqual(2, len(res))
self.assertEqual(pkg1.name, res[0].name)
self.assertEqual(pkg2.description, res[1].description)
def test_package_search_search(self): def test_package_search_search(self):
pkg1 = api.package_upload( pkg1 = api.package_upload(
self._stub_package( self._stub_package(