From 2f48df4baa602e0d441ec95c8aae242e564fe38a Mon Sep 17 00:00:00 2001 From: "Chart.js" <> Date: Mon, 3 Oct 2022 21:54:50 -0400 Subject: [PATCH] feat: JSONB has_all --- README.md | 11 ++++++++++ src/ormar_postgres_extensions/fields/jsonb.py | 13 +++++++++++ tests/fields/test_jsonb.py | 22 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/README.md b/README.md index 443db31..1c8d329 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,17 @@ The maps to the [`contains`](https://docs.sqlalchemy.org/en/14/dialects/postgres await JSONBTestModel.objects.filter(data__jsonb_contains=dict(key="value")).all() ``` +##### jsonb_has_all + +The maps to the [`has_all`](https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_all) operator in Postgres. + +```python +from sqlalchemy.dialects.postgres import array + +await JSONBTestModel.objects.filter(data__jsonb_has_all=array(["key1", "key2"])).all() +``` + +##### jsonb_has_any ##### jsonb_has_key The maps to the [`has_key`](https://docs.sqlalchemy.org/en/14/dialects/postgresql.html#sqlalchemy.dialects.postgresql.JSONB.Comparator.has_key) operator in Postgres. diff --git a/src/ormar_postgres_extensions/fields/jsonb.py b/src/ormar_postgres_extensions/fields/jsonb.py index 255c1d2..481c421 100644 --- a/src/ormar_postgres_extensions/fields/jsonb.py +++ b/src/ormar_postgres_extensions/fields/jsonb.py @@ -26,6 +26,17 @@ def jsonb_contains(self, other: Any) -> ormar.queryset.clause.FilterGroup: return self._select_operator(op="jsonb_contains", other=other) +def jsonb_has_all(self, other: Any) -> ormar.queryset.clause.FilterGroup: + """ + works as postgresql `column ?& VALUE::jsonb` + :param other: value to check against operator + :type other: Any + :return: FilterGroup for operator + :rtype: ormar.queryset.clause.FilterGroup + """ + return self._select_operator(op="jsonb_has_all", other=other) + + def jsonb_has_key(self, other: Any) -> ormar.queryset.clause.FilterGroup: """ works as postgresql `column ? VALUE::jsonb` @@ -41,6 +52,7 @@ def jsonb_has_key(self, other: Any) -> ormar.queryset.clause.FilterGroup: FIELD_ACCESSOR_MAP = [ ("jsonb_contained_by", jsonb_contained_by), ("jsonb_contains", jsonb_contains), + ("jsonb_has_all", jsonb_has_all), ("jsonb_has_key", jsonb_has_key), ] @@ -54,6 +66,7 @@ def jsonb_has_key(self, other: Any) -> ormar.queryset.clause.FilterGroup: ACCESSOR_MAP = [ ("jsonb_contained_by", "contained_by"), ("jsonb_contains", "contains"), + ("jsonb_has_all", "has_all"), ("jsonb_has_key", "has_key"), ] diff --git a/tests/fields/test_jsonb.py b/tests/fields/test_jsonb.py index 859284a..bba5f5d 100644 --- a/tests/fields/test_jsonb.py +++ b/tests/fields/test_jsonb.py @@ -3,6 +3,7 @@ import ormar import pytest +from sqlalchemy.dialects.postgresql import array import ormar_postgres_extensions as ormar_pg_ext from tests.database import ( @@ -123,6 +124,27 @@ async def test_contained_by(db): assert len(found) == 0 +@pytest.mark.asyncio +async def test_has_all(db): + await JSONBTestModel(data=json.dumps(dict(key1="foo", key3=2))).save() + await JSONBTestModel(data=json.dumps(dict(key2="bar"))).save() + + found = await JSONBTestModel.objects.filter( + data__jsonb_has_all=array(["key1"]) + ).all() + assert len(found) == 1 + + found = await JSONBTestModel.objects.filter( + data__jsonb_has_all=array(["key1", "key2"]) + ).all() + assert len(found) == 0 + + found = await JSONBTestModel.objects.filter( + data__jsonb_has_all=array(["key1", "key3"]) + ).all() + assert len(found) == 1 + + @pytest.mark.asyncio async def test_has_key_object(db): await JSONBTestModel(data=json.dumps(dict(key1="foo"))).save()