Skip to content

Commit

Permalink
add not_any method to Array column (#1022)
Browse files Browse the repository at this point in the history
  • Loading branch information
dantownsend authored Jun 14, 2024
1 parent 5859917 commit 2b79717
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
6 changes: 6 additions & 0 deletions docs/src/piccolo/schema/column_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,12 @@ any

.. automethod:: Array.any

=======
not_any
=======

.. automethod:: Array.not_any

===
all
===
Expand Down
24 changes: 23 additions & 1 deletion piccolo/columns/column_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ class Band(Table):
TimestamptzNow,
)
from piccolo.columns.defaults.uuid import UUID4, UUIDArg
from piccolo.columns.operators.comparison import ArrayAll, ArrayAny
from piccolo.columns.operators.comparison import (
ArrayAll,
ArrayAny,
ArrayNotAny,
)
from piccolo.columns.operators.string import Concat
from piccolo.columns.reference import LazyTableReference
from piccolo.querystring import QueryString
Expand Down Expand Up @@ -2670,6 +2674,24 @@ def any(self, value: t.Any) -> Where:
else:
raise ValueError("Unrecognised engine type")

def not_any(self, value: t.Any) -> Where:
"""
Check if the given value isn't in the array.
.. code-block:: python
>>> await Ticket.select().where(Ticket.seat_numbers.not_any(510))
"""
engine_type = self._meta.engine_type

if engine_type in ("postgres", "cockroach"):
return Where(column=self, value=value, operator=ArrayNotAny)
elif engine_type == "sqlite":
return self.not_like(f"%{value}%")
else:
raise ValueError("Unrecognised engine type")

def all(self, value: t.Any) -> Where:
"""
Check if all of the items in the array match the given value.
Expand Down
4 changes: 4 additions & 0 deletions piccolo/columns/operators/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,9 @@ class ArrayAny(ComparisonOperator):
template = "{value} = ANY ({name})"


class ArrayNotAny(ComparisonOperator):
template = "NOT {value} = ANY ({name})"


class ArrayAll(ComparisonOperator):
template = "{value} = ALL ({name})"
28 changes: 28 additions & 0 deletions tests/columns/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,34 @@ def test_any(self):
None,
)

@engines_skip("sqlite")
@pytest.mark.cockroach_array_slow
def test_not_any(self):
"""
Make sure rows can be retrieved where the array doesn't contain a
certain value.
In CockroachDB <= v22.2.0 we had this error:
* https://github.com/cockroachdb/cockroach/issues/71908 "could not decorrelate subquery" error under asyncpg
In newer CockroachDB versions, it runs but is very slow:
* https://github.com/piccolo-orm/piccolo/issues/1005
""" # noqa: E501

MyTable(value=[1, 2, 3]).save().run_sync()
MyTable(value=[4, 5, 6]).save().run_sync()

# We have to explicitly specify the type, so CockroachDB works.
self.assertEqual(
MyTable.select(MyTable.value)
.where(MyTable.value.not_any(QueryString("{}::INTEGER", 4)))
.run_sync(),
[{"value": [1, 2, 3]}],
)

@engines_skip("sqlite")
@pytest.mark.cockroach_array_slow
def test_cat(self):
Expand Down

0 comments on commit 2b79717

Please sign in to comment.