From: nathan Date: Wed, 11 Dec 2013 15:21:08 +0000 (-0500) Subject: sqlalchemy/dialects/postgresql/pgjson: X-Git-Tag: rel_0_9_0~26^2~6^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=015c73c83a1bd915cd34cc61370a6d89228c40d6;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git sqlalchemy/dialects/postgresql/pgjson: - Added support for additional operators - Made return as json default (rather than text) --- diff --git a/lib/sqlalchemy/dialects/postgresql/pgjson.py b/lib/sqlalchemy/dialects/postgresql/pgjson.py index 161fe83fa8..b41446dae5 100644 --- a/lib/sqlalchemy/dialects/postgresql/pgjson.py +++ b/lib/sqlalchemy/dialects/postgresql/pgjson.py @@ -39,7 +39,7 @@ class JSON(sqltypes.TypeEngine): * Path Index operations:: - data_table.c.data.get_path('{key_1, key_2, ..., key_n}'] + data_table.c.data.get_path("'{key_1, key_2, ..., key_n}'"] Please be aware that when used with the SQLAlchemy ORM, you will need to replace the JSON object present on an attribute with a new object in order @@ -71,11 +71,23 @@ class JSON(sqltypes.TypeEngine): # The only downside to this is that you cannot dereference more # than one level deep in json structures, though comparator # support for multi-level dereference is lacking anyhow. + return self.expr.op('->', precedence=5)(other) + + def get_item_as_text(self, other): + """Text expression. Get the value at the given key as text. Use + this when you need to cast the type of the returned value.""" return self.expr.op('->>', precedence=5)(other) def get_path(self, other): - """Text expression. Get the value at a given path. Paths are of + """Text expression. Get the value at a given path. Paths are of the form {key_1, key_2, ..., key_n}.""" + return self.expr.op('#>', precedence=5)(other) + + def get_path_as_text(self, other): + """Text expression. Get the value at a given path, as text. + Paths are of the form '{key_1, key_2, ..., key_n}' (quotes are + required). Use this when you need to cast the type of the + returned value.""" return self.expr.op('#>>', precedence=5)(other) def _adapt_expression(self, op, other_comparator): diff --git a/test/dialect/postgresql/test_types.py b/test/dialect/postgresql/test_types.py index 114b36f8f4..c7a973e4e0 100644 --- a/test/dialect/postgresql/test_types.py +++ b/test/dialect/postgresql/test_types.py @@ -1713,19 +1713,31 @@ class JSONTest(fixtures.TestBase): def test_where_getitem(self): self._test_where( self.jsoncol['bar'] == None, - "(test_table.test_column ->> %(test_column_1)s) IS NULL" + "(test_table.test_column -> %(test_column_1)s) IS NULL" ) def test_where_path(self): self._test_where( self.jsoncol.get_path('{"foo", 1}') == None, + "(test_table.test_column #> %(test_column_1)s) IS NULL" + ) + + def test_where_getitem_as_text(self): + self._test_where( + self.jsoncol.get_item_as_text('bar') == None, + "(test_table.test_column ->> %(test_column_1)s) IS NULL" + ) + + def test_where_path_as_text(self): + self._test_where( + self.jsoncol.get_path_as_text('{"foo", 1}') == None, "(test_table.test_column #>> %(test_column_1)s) IS NULL" ) def test_cols_get(self): self._test_cols( self.jsoncol['foo'], - "test_table.test_column ->> %(test_column_1)s AS anon_1", + "test_table.test_column -> %(test_column_1)s AS anon_1", True ) @@ -1800,9 +1812,31 @@ class JSONRoundTripTest(fixtures.TablesTest): self._fixture_data(engine) self._test_criterion(engine) + def test_path_query(self): + engine = testing.db + self._fixture_data(engine) + data_table = self.tables.data_table + result = engine.execute( + select([data_table.c.data]).where( + data_table.c.data.get_path_as_text('{k1}') == 'r3v1' + ) + ).first() + eq_(result, ({'k1': 'r3v1', 'k2': 'r3v2'},)) + + def test_query_returned_as_text(self): + engine = testing.db + self._fixture_data(engine) + data_table = self.tables.data_table + result = engine.execute( + select([data_table.c.data.get_item_as_text('k1')]) + ).first() + assert isinstance(result[0], basestring) + def _test_criterion(self, engine): data_table = self.tables.data_table result = engine.execute( - select([data_table.c.data]).where(data_table.c.data['k1'] == 'r3v1') + select([data_table.c.data]).where( + data_table.c.data.get_item_as_text('k1') == 'r3v1' + ) ).first() eq_(result, ({'k1': 'r3v1', 'k2': 'r3v2'},)) \ No newline at end of file