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.
- kiarina/lib/redisearch/__init__.py +35 -0
- kiarina/lib/redisearch/_async/__init__.py +0 -0
- kiarina/lib/redisearch/_async/client.py +181 -0
- kiarina/lib/redisearch/_async/registry.py +16 -0
- kiarina/lib/redisearch/_core/__init__.py +0 -0
- kiarina/lib/redisearch/_core/context.py +69 -0
- kiarina/lib/redisearch/_core/operations/__init__.py +0 -0
- kiarina/lib/redisearch/_core/operations/count.py +55 -0
- kiarina/lib/redisearch/_core/operations/create_index.py +52 -0
- kiarina/lib/redisearch/_core/operations/delete.py +43 -0
- kiarina/lib/redisearch/_core/operations/drop_index.py +59 -0
- kiarina/lib/redisearch/_core/operations/exists_index.py +56 -0
- kiarina/lib/redisearch/_core/operations/find.py +105 -0
- kiarina/lib/redisearch/_core/operations/get.py +61 -0
- kiarina/lib/redisearch/_core/operations/get_info.py +155 -0
- kiarina/lib/redisearch/_core/operations/get_key.py +8 -0
- kiarina/lib/redisearch/_core/operations/migrate_index.py +160 -0
- kiarina/lib/redisearch/_core/operations/reset_index.py +60 -0
- kiarina/lib/redisearch/_core/operations/search.py +111 -0
- kiarina/lib/redisearch/_core/operations/set.py +65 -0
- kiarina/lib/redisearch/_core/utils/__init__.py +0 -0
- kiarina/lib/redisearch/_core/utils/calc_score.py +35 -0
- kiarina/lib/redisearch/_core/utils/marshal_mappings.py +57 -0
- kiarina/lib/redisearch/_core/utils/parse_search_result.py +57 -0
- kiarina/lib/redisearch/_core/utils/unmarshal_mappings.py +57 -0
- kiarina/lib/redisearch/_core/views/__init__.py +0 -0
- kiarina/lib/redisearch/_core/views/document.py +25 -0
- kiarina/lib/redisearch/_core/views/info_result.py +24 -0
- kiarina/lib/redisearch/_core/views/search_result.py +31 -0
- kiarina/lib/redisearch/_sync/__init__.py +0 -0
- kiarina/lib/redisearch/_sync/client.py +179 -0
- kiarina/lib/redisearch/_sync/registry.py +16 -0
- kiarina/lib/redisearch/asyncio.py +33 -0
- kiarina/lib/redisearch/filter/__init__.py +61 -0
- kiarina/lib/redisearch/filter/_decorators.py +28 -0
- kiarina/lib/redisearch/filter/_enums.py +28 -0
- kiarina/lib/redisearch/filter/_field/__init__.py +5 -0
- kiarina/lib/redisearch/filter/_field/base.py +67 -0
- kiarina/lib/redisearch/filter/_field/numeric.py +178 -0
- kiarina/lib/redisearch/filter/_field/tag.py +142 -0
- kiarina/lib/redisearch/filter/_field/text.py +111 -0
- kiarina/lib/redisearch/filter/_model.py +93 -0
- kiarina/lib/redisearch/filter/_registry.py +153 -0
- kiarina/lib/redisearch/filter/_types.py +32 -0
- kiarina/lib/redisearch/filter/_utils.py +18 -0
- kiarina/lib/redisearch/py.typed +0 -0
- kiarina/lib/redisearch/schema/__init__.py +25 -0
- kiarina/lib/redisearch/schema/_field/__init__.py +0 -0
- kiarina/lib/redisearch/schema/_field/base.py +20 -0
- kiarina/lib/redisearch/schema/_field/numeric.py +33 -0
- kiarina/lib/redisearch/schema/_field/tag.py +46 -0
- kiarina/lib/redisearch/schema/_field/text.py +44 -0
- kiarina/lib/redisearch/schema/_field/vector/__init__.py +0 -0
- kiarina/lib/redisearch/schema/_field/vector/base.py +61 -0
- kiarina/lib/redisearch/schema/_field/vector/flat.py +40 -0
- kiarina/lib/redisearch/schema/_field/vector/hnsw.py +53 -0
- kiarina/lib/redisearch/schema/_model.py +98 -0
- kiarina/lib/redisearch/schema/_types.py +16 -0
- kiarina/lib/redisearch/settings.py +47 -0
- kiarina_lib_redisearch-1.0.0.dist-info/METADATA +886 -0
- kiarina_lib_redisearch-1.0.0.dist-info/RECORD +62 -0
- 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()
|