kiarina-lib-redisearch 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. kiarina/lib/redisearch/__init__.py +35 -0
  2. kiarina/lib/redisearch/_async/__init__.py +0 -0
  3. kiarina/lib/redisearch/_async/client.py +181 -0
  4. kiarina/lib/redisearch/_async/registry.py +16 -0
  5. kiarina/lib/redisearch/_core/__init__.py +0 -0
  6. kiarina/lib/redisearch/_core/context.py +69 -0
  7. kiarina/lib/redisearch/_core/operations/__init__.py +0 -0
  8. kiarina/lib/redisearch/_core/operations/count.py +55 -0
  9. kiarina/lib/redisearch/_core/operations/create_index.py +52 -0
  10. kiarina/lib/redisearch/_core/operations/delete.py +43 -0
  11. kiarina/lib/redisearch/_core/operations/drop_index.py +59 -0
  12. kiarina/lib/redisearch/_core/operations/exists_index.py +56 -0
  13. kiarina/lib/redisearch/_core/operations/find.py +105 -0
  14. kiarina/lib/redisearch/_core/operations/get.py +61 -0
  15. kiarina/lib/redisearch/_core/operations/get_info.py +155 -0
  16. kiarina/lib/redisearch/_core/operations/get_key.py +8 -0
  17. kiarina/lib/redisearch/_core/operations/migrate_index.py +160 -0
  18. kiarina/lib/redisearch/_core/operations/reset_index.py +60 -0
  19. kiarina/lib/redisearch/_core/operations/search.py +111 -0
  20. kiarina/lib/redisearch/_core/operations/set.py +65 -0
  21. kiarina/lib/redisearch/_core/utils/__init__.py +0 -0
  22. kiarina/lib/redisearch/_core/utils/calc_score.py +35 -0
  23. kiarina/lib/redisearch/_core/utils/marshal_mappings.py +57 -0
  24. kiarina/lib/redisearch/_core/utils/parse_search_result.py +57 -0
  25. kiarina/lib/redisearch/_core/utils/unmarshal_mappings.py +57 -0
  26. kiarina/lib/redisearch/_core/views/__init__.py +0 -0
  27. kiarina/lib/redisearch/_core/views/document.py +25 -0
  28. kiarina/lib/redisearch/_core/views/info_result.py +24 -0
  29. kiarina/lib/redisearch/_core/views/search_result.py +31 -0
  30. kiarina/lib/redisearch/_sync/__init__.py +0 -0
  31. kiarina/lib/redisearch/_sync/client.py +179 -0
  32. kiarina/lib/redisearch/_sync/registry.py +16 -0
  33. kiarina/lib/redisearch/asyncio.py +33 -0
  34. kiarina/lib/redisearch/filter/__init__.py +61 -0
  35. kiarina/lib/redisearch/filter/_decorators.py +28 -0
  36. kiarina/lib/redisearch/filter/_enums.py +28 -0
  37. kiarina/lib/redisearch/filter/_field/__init__.py +5 -0
  38. kiarina/lib/redisearch/filter/_field/base.py +67 -0
  39. kiarina/lib/redisearch/filter/_field/numeric.py +178 -0
  40. kiarina/lib/redisearch/filter/_field/tag.py +142 -0
  41. kiarina/lib/redisearch/filter/_field/text.py +111 -0
  42. kiarina/lib/redisearch/filter/_model.py +93 -0
  43. kiarina/lib/redisearch/filter/_registry.py +153 -0
  44. kiarina/lib/redisearch/filter/_types.py +32 -0
  45. kiarina/lib/redisearch/filter/_utils.py +18 -0
  46. kiarina/lib/redisearch/py.typed +0 -0
  47. kiarina/lib/redisearch/schema/__init__.py +25 -0
  48. kiarina/lib/redisearch/schema/_field/__init__.py +0 -0
  49. kiarina/lib/redisearch/schema/_field/base.py +20 -0
  50. kiarina/lib/redisearch/schema/_field/numeric.py +33 -0
  51. kiarina/lib/redisearch/schema/_field/tag.py +46 -0
  52. kiarina/lib/redisearch/schema/_field/text.py +44 -0
  53. kiarina/lib/redisearch/schema/_field/vector/__init__.py +0 -0
  54. kiarina/lib/redisearch/schema/_field/vector/base.py +61 -0
  55. kiarina/lib/redisearch/schema/_field/vector/flat.py +40 -0
  56. kiarina/lib/redisearch/schema/_field/vector/hnsw.py +53 -0
  57. kiarina/lib/redisearch/schema/_model.py +98 -0
  58. kiarina/lib/redisearch/schema/_types.py +16 -0
  59. kiarina/lib/redisearch/settings.py +47 -0
  60. kiarina_lib_redisearch-1.0.0.dist-info/METADATA +886 -0
  61. kiarina_lib_redisearch-1.0.0.dist-info/RECORD +62 -0
  62. kiarina_lib_redisearch-1.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,35 @@
1
+ import logging
2
+ from importlib import import_module
3
+ from importlib.metadata import version
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from ._sync.client import RedisearchClient
8
+ from ._sync.registry import create_redisearch_client
9
+ from .settings import RedisearchSettings, settings_manager
10
+
11
+ __version__ = version("kiarina-lib-redisearch")
12
+
13
+ __all__ = [
14
+ "create_redisearch_client",
15
+ "RedisearchClient",
16
+ "RedisearchSettings",
17
+ "settings_manager",
18
+ ]
19
+
20
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
21
+
22
+
23
+ def __getattr__(name: str) -> object:
24
+ if name not in __all__:
25
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
26
+
27
+ module_map = {
28
+ "create_redisearch_client": "._sync.registry",
29
+ "RedisearchClient": "._sync.client",
30
+ "RedisearchSettings": ".settings",
31
+ "settings_manager": ".settings",
32
+ }
33
+
34
+ globals()[name] = getattr(import_module(module_map[name], __name__), name)
35
+ return globals()[name]
File without changes
@@ -0,0 +1,181 @@
1
+ from typing import Any
2
+
3
+ from redis.asyncio import Redis
4
+
5
+ from .._core.context import RedisearchContext
6
+ from .._core.operations.count import count
7
+ from .._core.operations.create_index import create_index
8
+ from .._core.operations.delete import delete
9
+ from .._core.operations.drop_index import drop_index
10
+ from .._core.operations.exists_index import exists_index
11
+ from .._core.operations.find import find
12
+ from .._core.operations.get import get
13
+ from .._core.operations.get_info import get_info
14
+ from .._core.operations.get_key import get_key
15
+ from .._core.operations.migrate_index import migrate_index
16
+ from .._core.operations.reset_index import reset_index
17
+ from .._core.operations.search import search
18
+ from .._core.operations.set import set
19
+ from .._core.views.document import Document
20
+ from .._core.views.info_result import InfoResult
21
+ from .._core.views.search_result import SearchResult
22
+ from ..filter import RedisearchFilter, RedisearchFilterConditions
23
+ from ..settings import RedisearchSettings
24
+
25
+
26
+ class RedisearchClient:
27
+ """
28
+ Redisearch client
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ settings: RedisearchSettings,
34
+ *,
35
+ redis: Redis,
36
+ ) -> None:
37
+ """
38
+ Initialize the Redisearch client.
39
+ """
40
+ if redis.get_encoder().decode_responses: # type: ignore[no-untyped-call]
41
+ # As the vector field in Redisearch is expected to be handled as bytes in redis-py,
42
+ raise ValueError("Redis client must have decode_responses=False")
43
+
44
+ self.ctx: RedisearchContext = RedisearchContext(
45
+ settings=settings, _redis_async=redis
46
+ )
47
+ """Redisearch context"""
48
+
49
+ # --------------------------------------------------
50
+ # Index operations
51
+ # --------------------------------------------------
52
+
53
+ async def exists_index(self) -> bool:
54
+ """
55
+ Check if the index exists.
56
+ """
57
+ return await exists_index("async", self.ctx)
58
+
59
+ async def create_index(self) -> None:
60
+ """
61
+ Create the search index.
62
+ """
63
+ await create_index("async", self.ctx)
64
+
65
+ async def drop_index(self, *, delete_documents: bool = False) -> bool:
66
+ """
67
+ Delete the index.
68
+ """
69
+ return await drop_index("async", self.ctx, delete_documents=delete_documents)
70
+
71
+ async def reset_index(self) -> None:
72
+ """
73
+ Reset the search index.
74
+ """
75
+ await reset_index("async", self.ctx)
76
+
77
+ async def migrate_index(self) -> None:
78
+ """
79
+ Migrate the search index.
80
+ """
81
+ await migrate_index("async", self.ctx)
82
+
83
+ async def get_info(self) -> InfoResult:
84
+ """
85
+ Get index information using FT.INFO command.
86
+ """
87
+ return await get_info("async", self.ctx)
88
+
89
+ # --------------------------------------------------
90
+ # Data operations
91
+ # --------------------------------------------------
92
+
93
+ async def set(self, mapping: dict[str, Any], *, id: str | None = None) -> None:
94
+ """
95
+ Set a hash value.
96
+
97
+ If no id is specified, the mapping must contain an "id" field.
98
+ """
99
+ await set("async", self.ctx, mapping, id=id)
100
+
101
+ async def delete(self, id: str) -> None:
102
+ """
103
+ Delete a document from the index.
104
+ """
105
+ await delete("async", self.ctx, id)
106
+
107
+ async def get(self, id: str) -> Document | None:
108
+ """
109
+ Get a document from the index.
110
+ """
111
+ return await get("async", self.ctx, id)
112
+
113
+ # --------------------------------------------------
114
+ # Search operations
115
+ # --------------------------------------------------
116
+
117
+ async def count(
118
+ self,
119
+ *,
120
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
121
+ ) -> SearchResult:
122
+ """
123
+ Count documents matching the filter.
124
+ """
125
+ return await count("async", self.ctx, filter=filter)
126
+
127
+ async def find(
128
+ self,
129
+ *,
130
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
131
+ sort_by: str | None = None,
132
+ sort_desc: bool = False,
133
+ offset: int | None = None,
134
+ limit: int | None = None,
135
+ return_fields: list[str] | None = None,
136
+ ) -> SearchResult:
137
+ """
138
+ Find documents matching the filter criteria.
139
+ """
140
+ return await find(
141
+ "async",
142
+ self.ctx,
143
+ filter=filter,
144
+ sort_by=sort_by,
145
+ sort_desc=sort_desc,
146
+ offset=offset,
147
+ limit=limit,
148
+ return_fields=return_fields,
149
+ )
150
+
151
+ async def search(
152
+ self,
153
+ *,
154
+ vector: list[float],
155
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
156
+ offset: int | None = None,
157
+ limit: int | None = None,
158
+ return_fields: list[str] | None = None,
159
+ ) -> SearchResult:
160
+ """
161
+ Search documents using vector similarity search.
162
+ """
163
+ return await search(
164
+ "async",
165
+ self.ctx,
166
+ vector=vector,
167
+ filter=filter,
168
+ offset=offset,
169
+ limit=limit,
170
+ return_fields=return_fields,
171
+ )
172
+
173
+ # --------------------------------------------------
174
+ # Utilities
175
+ # --------------------------------------------------
176
+
177
+ def get_key(self, id: str) -> str:
178
+ """
179
+ Get the Redis key for a given Redisearch ID.
180
+ """
181
+ return get_key(self.ctx, id)
@@ -0,0 +1,16 @@
1
+ import redis.asyncio
2
+
3
+ from ..settings import settings_manager
4
+ from .client import RedisearchClient
5
+
6
+
7
+ def create_redisearch_client(
8
+ config_key: str | None = None,
9
+ *,
10
+ redis: redis.asyncio.Redis,
11
+ ) -> RedisearchClient:
12
+ """
13
+ Create a Redisearch client.
14
+ """
15
+ settings = settings_manager.get_settings_by_key(config_key)
16
+ return RedisearchClient(settings, redis=redis)
File without changes
@@ -0,0 +1,69 @@
1
+ from dataclasses import dataclass
2
+
3
+ from redis import Redis
4
+ from redis.asyncio import Redis as AsyncRedis
5
+
6
+ from ..schema import RedisearchSchema
7
+ from ..settings import RedisearchSettings
8
+
9
+
10
+ @dataclass
11
+ class RedisearchContext:
12
+ """
13
+ Redisearch context
14
+ """
15
+
16
+ settings: RedisearchSettings
17
+
18
+ _schema: RedisearchSchema | None = None
19
+
20
+ _redis: Redis | None = None
21
+
22
+ _redis_async: AsyncRedis | None = None
23
+
24
+ # --------------------------------------------------
25
+ # Properties
26
+ # --------------------------------------------------
27
+
28
+ @property
29
+ def schema(self) -> RedisearchSchema:
30
+ """
31
+ Redisearch index schema
32
+ """
33
+ if self._schema is None:
34
+ if not self.settings.index_schema:
35
+ raise ValueError("Index schema is not set in RedisearchSettings")
36
+
37
+ self._schema = RedisearchSchema.from_field_dicts(self.settings.index_schema)
38
+
39
+ return self._schema
40
+
41
+ @schema.setter
42
+ def schema(self, value: RedisearchSchema) -> None:
43
+ self._schema = value
44
+
45
+ # --------------------------------------------------
46
+
47
+ @property
48
+ def redis(self) -> Redis:
49
+ if self._redis is None:
50
+ raise ValueError("Redis client is not set in RedisearchContext")
51
+
52
+ return self._redis
53
+
54
+ @redis.setter
55
+ def redis(self, value: Redis) -> None:
56
+ self._redis = value
57
+
58
+ # --------------------------------------------------
59
+
60
+ @property
61
+ def redis_async(self) -> AsyncRedis:
62
+ if self._redis_async is None:
63
+ raise ValueError("Async Redis client is not set in RedisearchContext")
64
+
65
+ return self._redis_async
66
+
67
+ @redis_async.setter
68
+ def redis_async(self, value: AsyncRedis) -> None:
69
+ self._redis_async = value
File without changes
@@ -0,0 +1,55 @@
1
+ from typing import Awaitable, Literal, overload
2
+
3
+ from redis.commands.search.query import Query
4
+
5
+ from ...filter import (
6
+ RedisearchFilter,
7
+ RedisearchFilterConditions,
8
+ create_redisearch_filter,
9
+ )
10
+ from ..context import RedisearchContext
11
+ from ..views.search_result import SearchResult
12
+
13
+
14
+ @overload
15
+ def count(
16
+ mode: Literal["sync"],
17
+ ctx: RedisearchContext,
18
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
19
+ ) -> SearchResult: ...
20
+
21
+
22
+ @overload
23
+ def count(
24
+ mode: Literal["async"],
25
+ ctx: RedisearchContext,
26
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
27
+ ) -> Awaitable[SearchResult]: ...
28
+
29
+
30
+ def count(
31
+ mode: Literal["sync", "async"],
32
+ ctx: RedisearchContext,
33
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
34
+ ) -> SearchResult | Awaitable[SearchResult]:
35
+ """
36
+ Count documents matching the filter.
37
+ """
38
+ if filter is not None:
39
+ filter = create_redisearch_filter(filter=filter, schema=ctx.schema)
40
+
41
+ filter_query = "*" if filter is None else str(filter)
42
+ query = Query(filter_query).no_content().paging(0, 0)
43
+
44
+ def _sync() -> SearchResult:
45
+ result = ctx.redis.ft(ctx.settings.index_name).search(query)
46
+ return SearchResult(total=result.total, duration=result.duration) # pyright: ignore[reportAttributeAccessIssue]
47
+
48
+ async def _async() -> SearchResult:
49
+ result = await ctx.redis_async.ft(ctx.settings.index_name).search(query)
50
+ return SearchResult(total=result.total, duration=result.duration) # pyright: ignore[reportAttributeAccessIssue]
51
+
52
+ if mode == "sync":
53
+ return _sync()
54
+ else:
55
+ return _async()
@@ -0,0 +1,52 @@
1
+ from typing import Awaitable, Literal, overload
2
+
3
+ from redis.commands.search.index_definition import IndexDefinition, IndexType
4
+
5
+ from ..context import RedisearchContext
6
+
7
+
8
+ @overload
9
+ def create_index(
10
+ mode: Literal["sync"],
11
+ ctx: RedisearchContext,
12
+ ) -> None: ...
13
+
14
+
15
+ @overload
16
+ def create_index(
17
+ mode: Literal["async"],
18
+ ctx: RedisearchContext,
19
+ ) -> Awaitable[None]: ...
20
+
21
+
22
+ def create_index(
23
+ mode: Literal["sync", "async"],
24
+ ctx: RedisearchContext,
25
+ ) -> None | Awaitable[None]:
26
+ """
27
+ Create the index.
28
+ """
29
+ fields = ctx.schema.to_fields()
30
+
31
+ definition = IndexDefinition( # type: ignore[no-untyped-call]
32
+ prefix=[ctx.settings.key_prefix],
33
+ index_type=IndexType.HASH,
34
+ )
35
+
36
+ def _sync() -> None:
37
+ ctx.redis.ft(ctx.settings.index_name).create_index(
38
+ fields=fields,
39
+ definition=definition,
40
+ )
41
+
42
+ async def _async() -> None:
43
+ await ctx.redis_async.ft(ctx.settings.index_name).create_index(
44
+ fields=fields,
45
+ definition=definition,
46
+ )
47
+
48
+ if mode == "sync":
49
+ _sync()
50
+ return None
51
+ else:
52
+ return _async()
@@ -0,0 +1,43 @@
1
+ from typing import Awaitable, Literal, overload
2
+
3
+ from ..context import RedisearchContext
4
+ from .get_key import get_key
5
+
6
+
7
+ @overload
8
+ def delete(
9
+ mode: Literal["sync"],
10
+ ctx: RedisearchContext,
11
+ id: str,
12
+ ) -> None: ...
13
+
14
+
15
+ @overload
16
+ def delete(
17
+ mode: Literal["async"],
18
+ ctx: RedisearchContext,
19
+ id: str,
20
+ ) -> Awaitable[None]: ...
21
+
22
+
23
+ def delete(
24
+ mode: Literal["sync", "async"],
25
+ ctx: RedisearchContext,
26
+ id: str,
27
+ ) -> None | Awaitable[None]:
28
+ """
29
+ Delete a document from the index.
30
+ """
31
+ key = get_key(ctx, id)
32
+
33
+ def _sync() -> None:
34
+ ctx.redis.delete(key)
35
+
36
+ async def _async() -> None:
37
+ await ctx.redis_async.delete(key)
38
+
39
+ if mode == "sync":
40
+ _sync()
41
+ return None
42
+ else:
43
+ return _async()
@@ -0,0 +1,59 @@
1
+ from typing import Awaitable, Literal, overload
2
+
3
+ from ..context import RedisearchContext
4
+
5
+
6
+ @overload
7
+ def drop_index(
8
+ mode: Literal["sync"],
9
+ ctx: RedisearchContext,
10
+ *,
11
+ delete_documents: bool = False,
12
+ ) -> bool: ...
13
+
14
+
15
+ @overload
16
+ def drop_index(
17
+ mode: Literal["async"],
18
+ ctx: RedisearchContext,
19
+ *,
20
+ delete_documents: bool = False,
21
+ ) -> Awaitable[bool]: ...
22
+
23
+
24
+ def drop_index(
25
+ mode: Literal["sync", "async"],
26
+ ctx: RedisearchContext,
27
+ *,
28
+ delete_documents: bool = False,
29
+ ) -> bool | Awaitable[bool]:
30
+ """
31
+ Delete the index.
32
+ """
33
+
34
+ def _sync() -> bool:
35
+ if ctx.settings.protect_index_deletion:
36
+ return False
37
+
38
+ try:
39
+ ctx.redis.ft(ctx.settings.index_name).dropindex(delete_documents)
40
+ return True
41
+ except Exception:
42
+ return False
43
+
44
+ async def _async() -> bool:
45
+ if ctx.settings.protect_index_deletion:
46
+ return False
47
+
48
+ try:
49
+ await ctx.redis_async.ft(ctx.settings.index_name).dropindex(
50
+ delete_documents
51
+ )
52
+ return True
53
+ except Exception:
54
+ return False
55
+
56
+ if mode == "sync":
57
+ return _sync()
58
+ else:
59
+ return _async()
@@ -0,0 +1,56 @@
1
+ from typing import Awaitable, Literal, overload
2
+
3
+ from ..context import RedisearchContext
4
+ from .get_info import get_info
5
+
6
+
7
+ @overload
8
+ def exists_index(
9
+ mode: Literal["sync"],
10
+ ctx: RedisearchContext,
11
+ ) -> bool: ...
12
+
13
+
14
+ @overload
15
+ def exists_index(
16
+ mode: Literal["async"],
17
+ ctx: RedisearchContext,
18
+ ) -> Awaitable[bool]: ...
19
+
20
+
21
+ def exists_index(
22
+ mode: Literal["sync", "async"],
23
+ ctx: RedisearchContext,
24
+ ) -> bool | Awaitable[bool]:
25
+ """
26
+ Check if the index exists.
27
+ """
28
+
29
+ def _handle_exception(e: Exception) -> bool:
30
+ # <= Redis 7
31
+ if str(e) == "Unknown index name":
32
+ return False
33
+ # Redis 8
34
+ elif "no such index" in str(e):
35
+ return False
36
+
37
+ raise
38
+
39
+ def _sync() -> bool:
40
+ try:
41
+ get_info(mode="sync", ctx=ctx)
42
+ return True
43
+ except Exception as e:
44
+ return _handle_exception(e)
45
+
46
+ async def _async() -> bool:
47
+ try:
48
+ await get_info(mode="async", ctx=ctx)
49
+ return True
50
+ except Exception as e:
51
+ return _handle_exception(e)
52
+
53
+ if mode == "sync":
54
+ return _sync()
55
+ else:
56
+ return _async()
@@ -0,0 +1,105 @@
1
+ from typing import Any, Awaitable, Literal, overload
2
+
3
+ from redis.commands.search.query import Query
4
+ from redis.commands.search.result import Result
5
+
6
+ from ...filter import (
7
+ RedisearchFilter,
8
+ RedisearchFilterConditions,
9
+ create_redisearch_filter,
10
+ )
11
+ from ..context import RedisearchContext
12
+ from ..utils.parse_search_result import parse_search_result
13
+ from ..views.search_result import SearchResult
14
+ from .count import count
15
+
16
+
17
+ @overload
18
+ def find(
19
+ mode: Literal["sync"],
20
+ ctx: RedisearchContext,
21
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
22
+ sort_by: str | None = None,
23
+ sort_desc: bool = False,
24
+ offset: int | None = None,
25
+ limit: int | None = None,
26
+ return_fields: list[str] | None = None,
27
+ ) -> SearchResult: ...
28
+
29
+
30
+ @overload
31
+ def find(
32
+ mode: Literal["async"],
33
+ ctx: RedisearchContext,
34
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
35
+ sort_by: str | None = None,
36
+ sort_desc: bool = False,
37
+ offset: int | None = None,
38
+ limit: int | None = None,
39
+ return_fields: list[str] | None = None,
40
+ ) -> Awaitable[SearchResult]: ...
41
+
42
+
43
+ def find(
44
+ mode: Literal["sync", "async"],
45
+ ctx: RedisearchContext,
46
+ filter: RedisearchFilter | RedisearchFilterConditions | None = None,
47
+ sort_by: str | None = None,
48
+ sort_desc: bool = False,
49
+ offset: int | None = None,
50
+ limit: int | None = None,
51
+ return_fields: list[str] | None = None,
52
+ ) -> SearchResult | Awaitable[SearchResult]:
53
+ """
54
+ Find documents matching the filter criteria.
55
+
56
+ Args:
57
+ limit (int | None):
58
+ Number of documents to retrieve. If None, retrieves all documents
59
+ return_fields (list[str] | None):
60
+ Fields to return. If None, returns no content, only IDs.
61
+ """
62
+ if filter is not None:
63
+ filter = create_redisearch_filter(filter=filter, schema=ctx.schema)
64
+
65
+ filter_query = "*" if filter is None else str(filter)
66
+
67
+ def _build_query(limit: int) -> Query:
68
+ query = Query(filter_query)
69
+
70
+ if return_fields:
71
+ query = query.return_fields(*return_fields)
72
+ else:
73
+ query = query.no_content()
74
+
75
+ if sort_by:
76
+ query = query.sort_by(sort_by, asc=not sort_desc)
77
+
78
+ query = query.paging(offset or 0, limit)
79
+ return query
80
+
81
+ def _parse_search_result(result: Any) -> SearchResult:
82
+ assert isinstance(result, Result)
83
+ return parse_search_result(
84
+ key_prefix=ctx.settings.key_prefix,
85
+ schema=ctx.schema,
86
+ return_fields=return_fields,
87
+ result=result,
88
+ )
89
+
90
+ def _sync() -> SearchResult:
91
+ query = _build_query(limit or count(mode="sync", ctx=ctx, filter=filter).total)
92
+ result = ctx.redis.ft(ctx.settings.index_name).search(query)
93
+ return _parse_search_result(result)
94
+
95
+ async def _async() -> SearchResult:
96
+ query = _build_query(
97
+ limit or (await count(mode="async", ctx=ctx, filter=filter)).total
98
+ )
99
+ result = await ctx.redis_async.ft(ctx.settings.index_name).search(query)
100
+ return _parse_search_result(result)
101
+
102
+ if mode == "sync":
103
+ return _sync()
104
+ else:
105
+ return _async()