Skip to content

Commit

Permalink
feat(alchemy): implement json getitem for sqlalchemy backends
Browse files Browse the repository at this point in the history
  • Loading branch information
cpcloud authored and kszucs committed Sep 20, 2022
1 parent 3e2efb4 commit 7384087
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ibis/backends/base/sql/alchemy/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def sa_inet(_, satype, nullable=True):
return dt.INET(nullable=nullable)


@dt.dtype.register(PGDialect, postgresql.JSON)
@dt.dtype.register(Dialect, sa.types.JSON)
def sa_json(_, satype, nullable=True):
return dt.JSON(nullable=nullable)

Expand Down
3 changes: 3 additions & 0 deletions ibis/backends/base/sql/alchemy/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ def _cast(t, op):
# in pandas. CAST(expr AS BYTEA) is correct and returns byte strings.
return sa.cast(sa_arg, sa.LargeBinary())

if isinstance(typ, dt.JSON) and not t.native_json_type:
return sa_arg
return sa.cast(sa_arg, sa_type)


Expand Down Expand Up @@ -646,6 +648,7 @@ def _count_star(t, op):
ops.BitwiseLeftShift: _bitwise_op("<<"),
ops.BitwiseRightShift: _bitwise_op(">>"),
ops.BitwiseNot: _bitwise_not,
ops.JSONGetItem: fixed_arity(lambda x, y: x.op("->")(y), 2),
}


Expand Down
1 change: 1 addition & 0 deletions ibis/backends/base/sql/alchemy/translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class AlchemyExprTranslator(ExprTranslator):
_has_reduction_filter_syntax = False

integer_to_timestamp = sa.func.to_timestamp
native_json_type = True

def name(self, translated, name, force=True):
return translated.label(name)
Expand Down
1 change: 1 addition & 0 deletions ibis/backends/mysql/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MySQLExprTranslator(AlchemyExprTranslator):
)
_bool_aggs_need_cast_to_int32 = False
integer_to_timestamp = sa.func.from_unixtime
native_json_type = False


rewrites = MySQLExprTranslator.rewrites
Expand Down
11 changes: 11 additions & 0 deletions ibis/backends/mysql/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,16 @@ def _find_in_set(t, op):
)


def _json_get_item(t, op):
arg = t.translate(op.arg)
index = t.translate(op.index)
if isinstance(op.index.output_dtype, dt.Integer):
path = "$[" + sa.cast(index, sa.TEXT) + "]"
else:
path = "$." + index
return sa.func.json_extract(arg, path)


operation_registry.update(
{
ops.Literal: _literal,
Expand Down Expand Up @@ -211,5 +221,6 @@ def _find_in_set(t, op):
ops.GroupConcat: _group_concat,
ops.DayOfWeekIndex: _day_of_week_index,
ops.DayOfWeekName: _day_of_week_name,
ops.JSONGetItem: _json_get_item,
}
)
14 changes: 14 additions & 0 deletions ibis/backends/pandas/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""The pandas client implementation."""

import json
from collections.abc import Mapping, Sequence

import numpy as np
Expand Down Expand Up @@ -302,6 +303,19 @@ def convert_array_to_series(in_dtype, out_dtype, column):
return column.map(lambda x: x if x is None else list(x))


@sch.convert.register(np.dtype, dt.JSON, pd.Series)
def convert_json_to_series(in_, out, col: pd.Series):
def try_json(x):
if x is None:
return x
try:
return json.loads(x)
except (TypeError, json.JSONDecodeError):
return x

return pd.Series(list(map(try_json, col)), dtype="object")


dt.DataType.to_pandas = ibis_dtype_to_pandas # type: ignore
sch.Schema.to_pandas = ibis_schema_to_pandas # type: ignore

Expand Down

0 comments on commit 7384087

Please sign in to comment.