]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
sqlalchemy/dialects/postgresql/pgjson:
authornathan <nathan.alexander.rice@gmail.com>
Wed, 11 Dec 2013 15:21:08 +0000 (10:21 -0500)
committernathan <nathan.alexander.rice@gmail.com>
Wed, 11 Dec 2013 15:21:08 +0000 (10:21 -0500)
 - Added support for additional operators
 - Made return as json default (rather than text)

lib/sqlalchemy/dialects/postgresql/pgjson.py
test/dialect/postgresql/test_types.py

index 161fe83fa895a3552069ecd3c8e271437b4435be..b41446dae50950bbb2eed833a25deab4344029b0 100644 (file)
@@ -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):
index 114b36f8f4c34de09119f83b2a8ff9dd72525219..c7a973e4e0e91656f766ddda4cebf9de7c784548 100644 (file)
@@ -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