From 00bae420b7ba9cee2c5528caa2d4024f4a1f9e90 Mon Sep 17 00:00:00 2001 From: Kushal Agrawal Date: Mon, 2 Apr 2018 14:08:14 +0530 Subject: [PATCH] and/or query combiner for filter options With the new changes it is be possible to search tags based on or query. it can be used with both tags and tags-any filter parameters. Change-Id: Ib7a0054cb7e220e64d19839d79367b1ac180ea94 Implements: blueprint glare-search-artifact-query-update --- glare/db/sqlalchemy/api.py | 16 +++++++++++----- glare/objects/base.py | 11 ++++++++--- glare/tests/unit/api/test_list.py | 12 ++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/glare/db/sqlalchemy/api.py b/glare/db/sqlalchemy/api.py index 5d55e83..e357ea7 100644 --- a/glare/db/sqlalchemy/api.py +++ b/glare/db/sqlalchemy/api.py @@ -258,14 +258,15 @@ def _apply_user_filters(query, basic_conds, tag_conds, prop_conds): if basic_conds: for basic_condition in basic_conds['and']: query = query.filter(and_(*basic_condition)) - or_queries = [] for basic_condition in basic_conds['or']: or_queries.append(*basic_condition) if tag_conds: - for tag_condition in tag_conds: + for tag_condition in tag_conds['and']: query = query.join(models.ArtifactTag, aliased=True).filter( and_(*tag_condition)) + for tag_condition in tag_conds['or']: + or_queries.append(*tag_condition) if prop_conds: for prop_condition in prop_conds['and']: @@ -437,7 +438,10 @@ def _do_query_filters(filters): "and": [], "or": [] } - tag_conds = [] + tag_conds = { + "and": [], + "or": [] + } prop_conds = { "and": [], "or": [] @@ -446,10 +450,12 @@ def _do_query_filters(filters): if field_name == 'tags': tags = utils.split_filter_value_for_quotes(value) for tag in tags: - tag_conds.append([models.ArtifactTag.value == tag]) + tag_conds[query_combiner].append( + [models.ArtifactTag.value == tag]) elif field_name == 'tags-any': tags = utils.split_filter_value_for_quotes(value) - tag_conds.append([models.ArtifactTag.value.in_(tags)]) + tag_conds[query_combiner].append( + [models.ArtifactTag.value.in_(tags)]) elif field_name in BASE_ARTIFACT_PROPERTIES: if op == 'in': if field_name == 'version': diff --git a/glare/objects/base.py b/glare/objects/base.py index 3fc5fd2..3206958 100644 --- a/glare/objects/base.py +++ b/glare/objects/base.py @@ -354,12 +354,17 @@ Possible values: for filter_name, filter_value in filters: if filter_name in ('tags-any', 'tags'): - if ':' in filter_value: + tag_values = filter_value + combiner = cls.DEFAULT_QUERY_COMBINER + if filter_value.startswith(("and:", "or:")): + combiner = filter_value[:filter_value.index(":")] + tag_values = filter_value[filter_value.index(":") + 1:] + if ':' in tag_values: msg = _("Tags are filtered without operator") raise exception.BadRequest(msg) new_filters.append( - (filter_name, None, None, None, filter_value, - cls.DEFAULT_QUERY_COMBINER)) + (filter_name, None, None, None, tag_values, + combiner)) continue key_name = None diff --git a/glare/tests/unit/api/test_list.py b/glare/tests/unit/api/test_list.py index add5777..3b31d05 100644 --- a/glare/tests/unit/api/test_list.py +++ b/glare/tests/unit/api/test_list.py @@ -533,6 +533,18 @@ class TestArtifactList(base.BaseTestArtifactAPI): for i in (0, 3, 5): self.assertIn(arts[i], res['artifacts']) + filters = [('name', 'in:name4,name1'), ('tags-any', 'and:tag2,tag4')] + res = self.controller.list(self.req, 'sample_artifact', filters) + self.assertEqual(2, len(res['artifacts'])) + for i in (3, 0): + self.assertIn(arts[i], res['artifacts']) + + filters = [('name', 'or:in:name4,name1'), ('tags-any', 'or:tag4')] + res = self.controller.list(self.req, 'sample_artifact', filters) + self.assertEqual(6, len(res['artifacts'])) + for i in (5, 4, 3, 0): + self.assertIn(arts[i], res['artifacts']) + # Filtering by tags with operators leads to BadRequest for f in ('tags', 'tags-any'): filters = [(f, 'eq:tag1')]