Skip to content

Commit a7ad1ce

Browse files
committed
allow array elements to be retrieved
1 parent 1dadf0b commit a7ad1ce

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-2
lines changed

piccolo/columns/column_types.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1268,8 +1268,9 @@ def arrow(self, key: str) -> JSONB:
12681268
Allows part of the JSON structure to be returned - for example,
12691269
for {"a": 1}, and a key value of "a", then 1 will be returned.
12701270
"""
1271-
self.json_operator = f"-> '{key}'"
1272-
return self
1271+
instance = t.cast(JSONB, self.copy())
1272+
instance.json_operator = f"-> '{key}'"
1273+
return instance
12731274

12741275
def get_select_string(self, engine_type: str, just_alias=False) -> str:
12751276
select_string = self._meta.get_full_name(just_alias=just_alias)
@@ -1383,6 +1384,7 @@ def __init__(
13831384

13841385
self.base_column = base_column
13851386
self.default = default
1387+
self.index: t.Optional[int] = None
13861388
kwargs.update({"base_column": base_column, "default": default})
13871389
super().__init__(**kwargs)
13881390

@@ -1394,3 +1396,41 @@ def column_type(self):
13941396
elif engine_type == "sqlite":
13951397
return "ARRAY"
13961398
raise Exception("Unrecognized engine type")
1399+
1400+
def __getitem__(self, value: int) -> Array:
1401+
"""
1402+
Allows queries which retrieve an item from the array. The index starts
1403+
with 0 for the first value. If you were to write the SQL by hand, the
1404+
first index would be 1 instead:
1405+
1406+
https://www.postgresql.org/docs/current/arrays.html
1407+
1408+
However, we keep the first index as 0 to fit better with Python.
1409+
1410+
For example:
1411+
1412+
.. code-block:: python
1413+
1414+
Ticket.select(Ticket.seat_numbers[1]).run_sync
1415+
1416+
"""
1417+
if isinstance(value, int):
1418+
if value < 0:
1419+
raise ValueError("Only positive integers are allowed.")
1420+
1421+
instance = t.cast(Array, self.copy())
1422+
1423+
# We deliberately add 1, as Postgres treats the first array element
1424+
# as index 1.
1425+
instance.index = value + 1
1426+
return instance
1427+
else:
1428+
raise ValueError("Only integers can be used for indexing.")
1429+
1430+
def get_select_string(self, engine_type: str, just_alias=False) -> str:
1431+
select_string = self._meta.get_full_name(just_alias=just_alias)
1432+
1433+
if isinstance(self.index, int):
1434+
return f"{select_string}[{self.index}]"
1435+
else:
1436+
return select_string

tests/columns/test_array.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,13 @@ def test_storage(self):
2727

2828
row = MyTable.objects().first().run_sync()
2929
self.assertEqual(row.value, [1, 2, 3])
30+
31+
def test_index(self):
32+
"""
33+
Indexes should allow individual array elements to be queried.
34+
"""
35+
MyTable(value=[1, 2, 3]).save().run_sync()
36+
37+
self.assertEqual(
38+
MyTable.select(MyTable.value[0]).first().run_sync(), {"value": 1}
39+
)

0 commit comments

Comments
 (0)