From 2e2d779e29ebc917bef2eaaf4421c8a60b374874 Mon Sep 17 00:00:00 2001 From: Minh Vu Date: Sun, 4 Oct 2026 16:21:56 +0200 Subject: [PATCH] Fix query indexing at the cache boundary --- aredis_om/model/model.py | 4 +-- tests/test_find_query_cache.py | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/test_find_query_cache.py diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index 35636c26..1f10e979 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -2059,7 +2059,7 @@ def __getitem__(self, item: int): "Cannot use [] notation with async code. " "Use FindQuery.get_item() instead." ) - if self._model_cache and len(self._model_cache) >= item: + if self._model_cache and len(self._model_cache) > item: return self._model_cache[item] query = self.copy(offset=item, limit=1) @@ -2083,7 +2083,7 @@ async def get_item(self, item: int): NOTE: This method is included specifically for async users, who cannot use the notation Model.find()[1000]. """ - if self._model_cache and len(self._model_cache) >= item: + if self._model_cache and len(self._model_cache) > item: return self._model_cache[item] query = self.copy(offset=item, limit=1) diff --git a/tests/test_find_query_cache.py b/tests/test_find_query_cache.py new file mode 100644 index 00000000..d2b9b090 --- /dev/null +++ b/tests/test_find_query_cache.py @@ -0,0 +1,65 @@ +from unittest import mock + +import pytest + +from aredis_om import FindQuery, HashModel +from aredis_om.model import model as model_module +from aredis_om.util import ASYNC_MODE + +from .conftest import py_test_mark_asyncio + + +@pytest.fixture +def query(monkeypatch): + fake_db = mock.AsyncMock() + + async def search(*args): + offset = args[4] + if offset >= 2: + return [2] + return [2, f"doc:{offset}", ["pk", str(offset), "name", str(offset)]] + + fake_db.execute_command.side_effect = search + monkeypatch.setattr(model_module, "has_redisearch", lambda conn: True) + + class Item(HashModel, index=True): + name: str + + class Meta: + database = fake_db + + return FindQuery(expressions=[], model=Item, limit=1) + + +@pytest.mark.parametrize("item", [-1, 0, 1, 2]) +@py_test_mark_asyncio +async def test_get_item_with_partial_cache(query, item): + cached = await query.execute(exhaust_results=False) + assert [model.name for model in cached] == ["0"] + + if item == 2: + with pytest.raises(IndexError): + await query.get_item(item) + else: + result = await query.get_item(item) + assert result.name == ("0" if item <= 0 else "1") + if item <= 0: + assert result is cached[0] + assert query.model.db().execute_command.call_count == 1 + + +@pytest.mark.skipif(ASYNC_MODE, reason="[] notation requires sync queries") +@pytest.mark.parametrize("item", [-1, 0, 1, 2]) +def test_getitem_with_partial_cache(query, item): + cached = query.execute(exhaust_results=False) + assert [model.name for model in cached] == ["0"] + + if item == 2: + with pytest.raises(IndexError): + query[item] + else: + result = query[item] + assert result.name == ("0" if item <= 0 else "1") + if item <= 0: + assert result is cached[0] + assert query.model.db().execute_command.call_count == 1