Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id A5721200C4E for ; Fri, 7 Apr 2017 02:55:01 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id A45C2160B91; Fri, 7 Apr 2017 00:55:01 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id DA4F4160BA6 for ; Fri, 7 Apr 2017 02:55:00 +0200 (CEST) Received: (qmail 12681 invoked by uid 500); 7 Apr 2017 00:55:00 -0000 Mailing-List: contact dev-help@ariatosca.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ariatosca.incubator.apache.org Delivered-To: mailing list dev@ariatosca.incubator.apache.org Received: (qmail 12623 invoked by uid 99); 7 Apr 2017 00:55:00 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 07 Apr 2017 00:55:00 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id A7B5AC2457 for ; Fri, 7 Apr 2017 00:54:59 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.221 X-Spam-Level: X-Spam-Status: No, score=-4.221 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id 9w6Ewvh6BhnR for ; Fri, 7 Apr 2017 00:54:57 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 227C860CDB for ; Fri, 7 Apr 2017 00:54:55 +0000 (UTC) Received: (qmail 12404 invoked by uid 99); 7 Apr 2017 00:54:55 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 07 Apr 2017 00:54:55 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 0EDF3F218F; Fri, 7 Apr 2017 00:54:55 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: emblemparade@apache.org To: dev@ariatosca.incubator.apache.org Date: Fri, 07 Apr 2017 00:55:04 -0000 Message-Id: <318586471548493aa3db68a4fed03a32@git.apache.org> In-Reply-To: <4fc57db3c9604d689d97be9e0ba15008@git.apache.org> References: <4fc57db3c9604d689d97be9e0ba15008@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [11/14] incubator-ariatosca git commit: ARIA-137-Support-for-predicate-based-queries-in-the-SQL-mapi archived-at: Fri, 07 Apr 2017 00:55:01 -0000 ARIA-137-Support-for-predicate-based-queries-in-the-SQL-mapi Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/422574e0 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/422574e0 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/422574e0 Branch: refs/heads/ARIA-92-plugin-in-implementation-string Commit: 422574e03174e5dff57888af2519ed62048ce3fa Parents: e7ffc73 Author: max-orlov Authored: Tue Apr 4 20:41:59 2017 +0300 Committer: max-orlov Committed: Wed Apr 5 16:02:04 2017 +0300 ---------------------------------------------------------------------- aria/storage/sql_mapi.py | 28 +++++++++++++++- tests/storage/test_model_storage.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/422574e0/aria/storage/sql_mapi.py ---------------------------------------------------------------------- diff --git a/aria/storage/sql_mapi.py b/aria/storage/sql_mapi.py index 59e1896..8d34bb4 100644 --- a/aria/storage/sql_mapi.py +++ b/aria/storage/sql_mapi.py @@ -31,6 +31,13 @@ from . import ( exceptions, ) +_predicates = {'ge': '__ge__', + 'gt': '__gt__', + 'lt': '__lt__', + 'le': '__le__', + 'eq': '__eq__', + 'ne': '__ne__'} + class SQLAlchemyModelAPI(api.ModelAPI): """ @@ -243,7 +250,10 @@ class SQLAlchemyModelAPI(api.ModelAPI): @staticmethod def _add_value_filter(query, filters): for column, value in filters.items(): - if isinstance(value, (list, tuple)): + if isinstance(value, dict): + for predicate, operand in value.items(): + query = query.filter(getattr(column, predicate)(operand)) + elif isinstance(value, (list, tuple)): query = query.filter(column.in_(value)) else: query = query.filter(column == value) @@ -269,12 +279,28 @@ class SQLAlchemyModelAPI(api.ModelAPI): include, filters, sort, joins = self._get_joins_and_converted_columns( include, filters, sort ) + filters = self._convert_operands(filters) query = self._get_base_query(include, joins) query = self._filter_query(query, filters) query = self._sort_query(query, sort) return query + @staticmethod + def _convert_operands(filters): + for column, conditions in filters.items(): + if isinstance(conditions, dict): + for predicate, operand in conditions.items(): + if predicate not in _predicates: + raise exceptions.StorageError( + "{0} is not a valid predicate for filtering. Valid predicates are {1}" + .format(predicate, ', '.join(_predicates.keys()))) + del filters[column][predicate] + filters[column][_predicates[predicate]] = operand + + + return filters + def _get_joins_and_converted_columns(self, include, filters, http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/422574e0/tests/storage/test_model_storage.py ---------------------------------------------------------------------- diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py index e4f3eba..4dabfaf 100644 --- a/tests/storage/test_model_storage.py +++ b/tests/storage/test_model_storage.py @@ -15,6 +15,12 @@ import pytest +from sqlalchemy import ( + Column, + Integer, + Text +) + from aria import ( application_model_storage, modeling @@ -150,3 +156,54 @@ def test_mapi_include(context): assert_include(service1) assert_include(service2) + + +class MockModel(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin): #pylint: disable=abstract-method + __tablename__ = 'op_mock_model' + + name = Column(Text) + value = Column(Integer) + + +class TestFilterOperands(object): + + @pytest.fixture() + def storage(self): + model_storage = application_model_storage( + sql_mapi.SQLAlchemyModelAPI, initiator=tests_storage.init_inmemory_model_storage) + model_storage.register(MockModel) + for value in (1, 2, 3, 4): + model_storage.op_mock_model.put(MockModel(value=value)) + yield model_storage + tests_storage.release_sqlite_storage(model_storage) + + def test_gt(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(gt=3)))) == 1 + assert len(storage.op_mock_model.list(filters=dict(value=dict(gt=4)))) == 0 + + def test_ge(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(ge=3)))) == 2 + assert len(storage.op_mock_model.list(filters=dict(value=dict(ge=5)))) == 0 + + def test_lt(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(lt=2)))) == 1 + assert len(storage.op_mock_model.list(filters=dict(value=dict(lt=1)))) == 0 + + def test_le(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(le=2)))) == 2 + assert len(storage.op_mock_model.list(filters=dict(value=dict(le=0)))) == 0 + + def test_eq(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(eq=2)))) == 1 + assert len(storage.op_mock_model.list(filters=dict(value=dict(eq=0)))) == 0 + + def test_neq(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(ne=2)))) == 3 + + def test_gt_and_lt(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(gt=1, lt=3)))) == 1 + assert len(storage.op_mock_model.list(filters=dict(value=dict(gt=2, lt=2)))) == 0 + + def test_eq_and_ne(self, storage): + assert len(storage.op_mock_model.list(filters=dict(value=dict(eq=1, ne=3)))) == 1 + assert len(storage.op_mock_model.list(filters=dict(value=dict(eq=1, ne=1)))) == 0