From 82bd5858e31b04d4b26f9ac9c2f8bdee8e60a1a8 Mon Sep 17 00:00:00 2001 From: Huangsm Date: Thu, 28 Jan 2016 01:00:19 -0500 Subject: [PATCH] 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 --- .../specification/murano-repository.rst | 2 +- murano/db/catalog/api.py | 52 +++++++++++++------ murano/tests/unit/api/v1/test_catalog.py | 51 ++++++++++++++++++ murano/tests/unit/db/test_catalog.py | 25 +++++++++ 4 files changed, 112 insertions(+), 18 deletions(-) diff --git a/doc/source/specification/murano-repository.rst b/doc/source/specification/murano-repository.rst index f0373e5c..b2fb3372 100644 --- a/doc/source/specification/murano-repository.rst +++ b/doc/source/specification/murano-repository.rst @@ -82,7 +82,7 @@ For an admin role all packages are available. +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ | ``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 | +----------------------+-------------+------------------------------------------------------------------------------------------------------------------------------+ diff --git a/murano/db/catalog/api.py b/murano/db/catalog/api.py index 057b1ad9..256ae24f 100644 --- a/murano/db/catalog/api.py +++ b/murano/db/catalog/api.py @@ -16,6 +16,7 @@ from oslo_db import api as oslo_db_api from oslo_db import exception as db_exceptions from oslo_db.sqlalchemy import utils from oslo_log import log as logging +import re import six import sqlalchemy as sa from sqlalchemy import or_ @@ -340,26 +341,43 @@ def package_search(filters, context, manage_public=False, fk_fields = {'categories': 'Category', 'tags': 'Tag', '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('_'): continue - if isinstance(getattr(pkg, attr), - attributes.InstrumentedAttribute): - search_str = filters['search'] - for delim in ',;': - search_str = search_str.replace(delim, ' ') - for key_word in search_str.split(): - _word = u'%{value}%'.format(value=key_word) - if attr in fk_fields.keys(): - condition = getattr(pkg, attr).any( - getattr(models, fk_fields[attr]).name.like(_word)) - conditions.append(condition) - elif isinstance(getattr(pkg, attr) - .property.columns[0].type, sa.String): - conditions.append(getattr(pkg, attr).like(_word)) - query = query.filter(or_(*conditions)) + if not isinstance(getattr(pkg, attr), + attributes.InstrumentedAttribute): + continue + priority = min(index, len(fields)) + for key_word in key_words: + _word = u'%{value}%'.format(value=key_word) + if attr in fk_fields.keys(): + condition = getattr(pkg, attr).any( + getattr(models, fk_fields[attr]).name.like(_word)) + conditions.append(condition) + order_cases.append((condition, priority)) + elif isinstance(getattr(pkg, attr) + .property.columns[0].type, sa.String): + condition = getattr(pkg, attr).like(_word) + 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 filters.get('order_by', ['name'])] diff --git a/murano/tests/unit/api/v1/test_catalog.py b/murano/tests/unit/api/v1/test_catalog.py index f963e273..33474f45 100644 --- a/murano/tests/unit/api/v1/test_catalog.py +++ b/murano/tests/unit/api/v1/test_catalog.py @@ -562,6 +562,57 @@ class TestCatalogApi(test_base.ControllerTest, test_base.MuranoApiTestCase): self.assertIn("Search by parameter 'tag' caused an error", 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): self._set_policy_rules( {'get_package': '', diff --git a/murano/tests/unit/db/test_catalog.py b/murano/tests/unit/db/test_catalog.py index 6138e085..64a6c879 100644 --- a/murano/tests/unit/db/test_catalog.py +++ b/murano/tests/unit/db/test_catalog.py @@ -212,6 +212,31 @@ class CatalogDBTestCase(base.MuranoWithDBTestCase): res = api.package_search({'marker': marker}, self.context, limit=4) 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): pkg1 = api.package_upload( self._stub_package(