Repository: redis/redis-py

Issue:
RediSearch: search command doesn't support asyncio Pipeline
The RediSearch search command returns an instance of the `Result` class except when the Redis client is a `Pipeline` because `Pipeline` returns itself instead of a result when you execute a command. There's code that checks for this in both the `SearchCommands` and `AsyncSearchCommands` classes:
https://github.com/redis/redis-py/blob/4b0543d567aef36ac467ce495d831a24575d8d5b/redis/commands/search/commands.py#L414
https://github.com/redis/redis-py/blob/4b0543d567aef36ac467ce495d831a24575d8d5b/redis/commands/search/commands.py#L883
However, this check doesn't work if the `Pipeline` is from the `redis.asyncio.client` module. The following modification should fix the issue:
```python
from redis.client import Pipeline
from redis.asyncio.client import Pipeline as AsyncPipeline
...
        if isinstance(res, Pipeline) or isinstance(res, AsyncPipeline):
            return res
...
```
I'm not sure if it makes sense to check for both `Pipeline` types or if the `SearchCommands` class should check for just `Pipeline` and the `AsyncSearchCommands` class should check for just `AsyncPipeline`. Let me know and I can make a PR. Or feel free to make the changes yourself if that's easier. Thanks!


Discussion:

Resolving patch:
diff --git a/redis/commands/search/__init__.py b/redis/commands/search/__init__.py
--- a/redis/commands/search/__init__.py
+++ b/redis/commands/search/__init__.py
@@ -167,5 +167,5 @@ class Pipeline(SearchCommands, redis.client.Pipeline):
     """Pipeline for the module."""
 
 
-class AsyncPipeline(AsyncSearchCommands, AsyncioPipeline):
+class AsyncPipeline(AsyncSearchCommands, AsyncioPipeline, Pipeline):
     """AsyncPipeline for the module."""


Test patch:
diff --git a/tests/test_asyncio/test_search.py b/tests/test_asyncio/test_search.py
--- a/tests/test_asyncio/test_search.py
+++ b/tests/test_asyncio/test_search.py
@@ -16,7 +16,7 @@
 from redis.commands.search.query import GeoFilter, NumericFilter, Query
 from redis.commands.search.result import Result
 from redis.commands.search.suggestion import Suggestion
-from tests.conftest import skip_ifmodversion_lt
+from tests.conftest import skip_if_redis_enterprise, skip_ifmodversion_lt
 
 WILL_PLAY_TEXT = os.path.abspath(
     os.path.join(os.path.dirname(__file__), "testdata", "will_play_text.csv.bz2")
@@ -1043,3 +1043,22 @@ async def test_aggregations_sort_by_and_limit(modclient: redis.Redis):
     res = await modclient.ft().aggregate(req)
     assert len(res.rows) == 1
     assert res.rows[0] == ["t1", "b"]
+
+
+@pytest.mark.redismod
+@skip_if_redis_enterprise()
+async def test_search_commands_in_pipeline(modclient: redis.Redis):
+    p = await modclient.ft().pipeline()
+    p.create_index((TextField("txt"),))
+    p.add_document("doc1", payload="foo baz", txt="foo bar")
+    p.add_document("doc2", txt="foo bar")
+    q = Query("foo bar").with_payloads()
+    await p.search(q)
+    res = await p.execute()
+    assert res[:3] == ["OK", "OK", "OK"]
+    assert 2 == res[3][0]
+    assert "doc1" == res[3][1]
+    assert "doc2" == res[3][4]
+    assert "foo baz" == res[3][2]
+    assert res[3][5] is None
+    assert res[3][3] == res[3][6] == ["txt", "foo bar"]