elasticsearch 9.0.2__py3-none-any.whl → 9.0.4__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.
- elasticsearch/_async/client/__init__.py +59 -202
- elasticsearch/_async/client/cat.py +1011 -59
- elasticsearch/_async/client/cluster.py +14 -4
- elasticsearch/_async/client/eql.py +10 -2
- elasticsearch/_async/client/esql.py +33 -10
- elasticsearch/_async/client/indices.py +88 -44
- elasticsearch/_async/client/inference.py +108 -3
- elasticsearch/_async/client/ingest.py +0 -7
- elasticsearch/_async/client/license.py +4 -4
- elasticsearch/_async/client/ml.py +6 -17
- elasticsearch/_async/client/monitoring.py +1 -1
- elasticsearch/_async/client/rollup.py +1 -22
- elasticsearch/_async/client/security.py +11 -17
- elasticsearch/_async/client/snapshot.py +6 -0
- elasticsearch/_async/client/sql.py +1 -1
- elasticsearch/_async/client/synonyms.py +1 -0
- elasticsearch/_async/client/transform.py +60 -0
- elasticsearch/_async/client/watcher.py +4 -2
- elasticsearch/_sync/client/__init__.py +59 -202
- elasticsearch/_sync/client/cat.py +1011 -59
- elasticsearch/_sync/client/cluster.py +14 -4
- elasticsearch/_sync/client/eql.py +10 -2
- elasticsearch/_sync/client/esql.py +33 -10
- elasticsearch/_sync/client/indices.py +88 -44
- elasticsearch/_sync/client/inference.py +108 -3
- elasticsearch/_sync/client/ingest.py +0 -7
- elasticsearch/_sync/client/license.py +4 -4
- elasticsearch/_sync/client/ml.py +6 -17
- elasticsearch/_sync/client/monitoring.py +1 -1
- elasticsearch/_sync/client/rollup.py +1 -22
- elasticsearch/_sync/client/security.py +11 -17
- elasticsearch/_sync/client/snapshot.py +6 -0
- elasticsearch/_sync/client/sql.py +1 -1
- elasticsearch/_sync/client/synonyms.py +1 -0
- elasticsearch/_sync/client/transform.py +60 -0
- elasticsearch/_sync/client/watcher.py +4 -2
- elasticsearch/_version.py +1 -1
- elasticsearch/compat.py +5 -0
- elasticsearch/dsl/__init__.py +2 -1
- elasticsearch/dsl/_async/document.py +84 -0
- elasticsearch/dsl/_sync/document.py +84 -0
- elasticsearch/dsl/document_base.py +219 -16
- elasticsearch/dsl/field.py +245 -57
- elasticsearch/dsl/query.py +7 -4
- elasticsearch/dsl/response/aggs.py +1 -1
- elasticsearch/dsl/types.py +125 -88
- elasticsearch/dsl/utils.py +2 -2
- elasticsearch/{dsl/_sync/_sync_check → esql}/__init__.py +3 -0
- elasticsearch/esql/esql.py +1156 -0
- elasticsearch/esql/functions.py +1750 -0
- {elasticsearch-9.0.2.dist-info → elasticsearch-9.0.4.dist-info}/METADATA +1 -3
- {elasticsearch-9.0.2.dist-info → elasticsearch-9.0.4.dist-info}/RECORD +55 -59
- elasticsearch/dsl/_sync/_sync_check/document.py +0 -514
- elasticsearch/dsl/_sync/_sync_check/faceted_search.py +0 -50
- elasticsearch/dsl/_sync/_sync_check/index.py +0 -597
- elasticsearch/dsl/_sync/_sync_check/mapping.py +0 -49
- elasticsearch/dsl/_sync/_sync_check/search.py +0 -230
- elasticsearch/dsl/_sync/_sync_check/update_by_query.py +0 -45
- {elasticsearch-9.0.2.dist-info → elasticsearch-9.0.4.dist-info}/WHEEL +0 -0
- {elasticsearch-9.0.2.dist-info → elasticsearch-9.0.4.dist-info}/licenses/LICENSE +0 -0
- {elasticsearch-9.0.2.dist-info → elasticsearch-9.0.4.dist-info}/licenses/NOTICE +0 -0
|
@@ -2213,13 +2213,10 @@ class SecurityClient(NamespacedClient):
|
|
|
2213
2213
|
def get_user_privileges(
|
|
2214
2214
|
self,
|
|
2215
2215
|
*,
|
|
2216
|
-
application: t.Optional[str] = None,
|
|
2217
2216
|
error_trace: t.Optional[bool] = None,
|
|
2218
2217
|
filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None,
|
|
2219
2218
|
human: t.Optional[bool] = None,
|
|
2220
2219
|
pretty: t.Optional[bool] = None,
|
|
2221
|
-
priviledge: t.Optional[str] = None,
|
|
2222
|
-
username: t.Optional[t.Union[None, str]] = None,
|
|
2223
2220
|
) -> ObjectApiResponse[t.Any]:
|
|
2224
2221
|
"""
|
|
2225
2222
|
.. raw:: html
|
|
@@ -2232,19 +2229,10 @@ class SecurityClient(NamespacedClient):
|
|
|
2232
2229
|
|
|
2233
2230
|
|
|
2234
2231
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-security-get-user-privileges>`_
|
|
2235
|
-
|
|
2236
|
-
:param application: The name of the application. Application privileges are always
|
|
2237
|
-
associated with exactly one application. If you do not specify this parameter,
|
|
2238
|
-
the API returns information about all privileges for all applications.
|
|
2239
|
-
:param priviledge: The name of the privilege. If you do not specify this parameter,
|
|
2240
|
-
the API returns information about all privileges for the requested application.
|
|
2241
|
-
:param username:
|
|
2242
2232
|
"""
|
|
2243
2233
|
__path_parts: t.Dict[str, str] = {}
|
|
2244
2234
|
__path = "/_security/user/_privileges"
|
|
2245
2235
|
__query: t.Dict[str, t.Any] = {}
|
|
2246
|
-
if application is not None:
|
|
2247
|
-
__query["application"] = application
|
|
2248
2236
|
if error_trace is not None:
|
|
2249
2237
|
__query["error_trace"] = error_trace
|
|
2250
2238
|
if filter_path is not None:
|
|
@@ -2253,10 +2241,6 @@ class SecurityClient(NamespacedClient):
|
|
|
2253
2241
|
__query["human"] = human
|
|
2254
2242
|
if pretty is not None:
|
|
2255
2243
|
__query["pretty"] = pretty
|
|
2256
|
-
if priviledge is not None:
|
|
2257
|
-
__query["priviledge"] = priviledge
|
|
2258
|
-
if username is not None:
|
|
2259
|
-
__query["username"] = username
|
|
2260
2244
|
__headers = {"accept": "application/json"}
|
|
2261
2245
|
return self.perform_request( # type: ignore[return-value]
|
|
2262
2246
|
"GET",
|
|
@@ -2345,6 +2329,9 @@ class SecurityClient(NamespacedClient):
|
|
|
2345
2329
|
human: t.Optional[bool] = None,
|
|
2346
2330
|
password: t.Optional[str] = None,
|
|
2347
2331
|
pretty: t.Optional[bool] = None,
|
|
2332
|
+
refresh: t.Optional[
|
|
2333
|
+
t.Union[bool, str, t.Literal["false", "true", "wait_for"]]
|
|
2334
|
+
] = None,
|
|
2348
2335
|
run_as: t.Optional[str] = None,
|
|
2349
2336
|
username: t.Optional[str] = None,
|
|
2350
2337
|
body: t.Optional[t.Dict[str, t.Any]] = None,
|
|
@@ -2382,6 +2369,9 @@ class SecurityClient(NamespacedClient):
|
|
|
2382
2369
|
types.
|
|
2383
2370
|
:param password: The user's password. If you specify the `password` grant type,
|
|
2384
2371
|
this parameter is required. It is not valid with other grant types.
|
|
2372
|
+
:param refresh: If 'true', Elasticsearch refreshes the affected shards to make
|
|
2373
|
+
this operation visible to search. If 'wait_for', it waits for a refresh to
|
|
2374
|
+
make this operation visible to search. If 'false', nothing is done with refreshes.
|
|
2385
2375
|
:param run_as: The name of the user to be impersonated.
|
|
2386
2376
|
:param username: The user name that identifies the user. If you specify the `password`
|
|
2387
2377
|
grant type, this parameter is required. It is not valid with other grant
|
|
@@ -2403,6 +2393,8 @@ class SecurityClient(NamespacedClient):
|
|
|
2403
2393
|
__query["human"] = human
|
|
2404
2394
|
if pretty is not None:
|
|
2405
2395
|
__query["pretty"] = pretty
|
|
2396
|
+
if refresh is not None:
|
|
2397
|
+
__query["refresh"] = refresh
|
|
2406
2398
|
if not __body:
|
|
2407
2399
|
if api_key is not None:
|
|
2408
2400
|
__body["api_key"] = api_key
|
|
@@ -3553,7 +3545,8 @@ class SecurityClient(NamespacedClient):
|
|
|
3553
3545
|
You can optionally filter the results with a query.</p>
|
|
3554
3546
|
<p>To use this API, you must have at least the <code>manage_own_api_key</code> or the <code>read_security</code> cluster privileges.
|
|
3555
3547
|
If you have only the <code>manage_own_api_key</code> privilege, this API returns only the API keys that you own.
|
|
3556
|
-
If you have the <code>read_security</code>, <code>manage_api_key</code>, or greater privileges (including <code>manage_security</code>), this API returns all API keys regardless of ownership
|
|
3548
|
+
If you have the <code>read_security</code>, <code>manage_api_key</code>, or greater privileges (including <code>manage_security</code>), this API returns all API keys regardless of ownership.
|
|
3549
|
+
Refer to the linked documentation for examples of how to find API keys:</p>
|
|
3557
3550
|
|
|
3558
3551
|
|
|
3559
3552
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-security-query-api-keys>`_
|
|
@@ -4466,6 +4459,7 @@ class SecurityClient(NamespacedClient):
|
|
|
4466
4459
|
<p>This API supports updates to an API key's access scope, metadata, and expiration.
|
|
4467
4460
|
The owner user's information, such as the <code>username</code> and <code>realm</code>, is also updated automatically on every call.</p>
|
|
4468
4461
|
<p>NOTE: This API cannot update REST API keys, which should be updated by either the update API key or bulk update API keys API.</p>
|
|
4462
|
+
<p>To learn more about how to use this API, refer to the <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/update-cc-api-key-examples">Update cross cluter API key API examples page</a>.</p>
|
|
4469
4463
|
|
|
4470
4464
|
|
|
4471
4465
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-security-update-cross-cluster-api-key>`_
|
|
@@ -403,6 +403,7 @@ class SnapshotClient(NamespacedClient):
|
|
|
403
403
|
human: t.Optional[bool] = None,
|
|
404
404
|
master_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None,
|
|
405
405
|
pretty: t.Optional[bool] = None,
|
|
406
|
+
wait_for_completion: t.Optional[bool] = None,
|
|
406
407
|
) -> ObjectApiResponse[t.Any]:
|
|
407
408
|
"""
|
|
408
409
|
.. raw:: html
|
|
@@ -418,6 +419,9 @@ class SnapshotClient(NamespacedClient):
|
|
|
418
419
|
:param master_timeout: The period to wait for the master node. If the master
|
|
419
420
|
node is not available before the timeout expires, the request fails and returns
|
|
420
421
|
an error. To indicate that the request should never timeout, set it to `-1`.
|
|
422
|
+
:param wait_for_completion: If `true`, the request returns a response when the
|
|
423
|
+
matching snapshots are all deleted. If `false`, the request returns a response
|
|
424
|
+
as soon as the deletes are scheduled.
|
|
421
425
|
"""
|
|
422
426
|
if repository in SKIP_IN_PATH:
|
|
423
427
|
raise ValueError("Empty value passed for parameter 'repository'")
|
|
@@ -439,6 +443,8 @@ class SnapshotClient(NamespacedClient):
|
|
|
439
443
|
__query["master_timeout"] = master_timeout
|
|
440
444
|
if pretty is not None:
|
|
441
445
|
__query["pretty"] = pretty
|
|
446
|
+
if wait_for_completion is not None:
|
|
447
|
+
__query["wait_for_completion"] = wait_for_completion
|
|
442
448
|
__headers = {"accept": "application/json"}
|
|
443
449
|
return self.perform_request( # type: ignore[return-value]
|
|
444
450
|
"DELETE",
|
|
@@ -283,7 +283,7 @@ class SqlClient(NamespacedClient):
|
|
|
283
283
|
keep_alive: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None,
|
|
284
284
|
keep_on_completion: t.Optional[bool] = None,
|
|
285
285
|
page_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None,
|
|
286
|
-
params: t.Optional[t.
|
|
286
|
+
params: t.Optional[t.Sequence[t.Any]] = None,
|
|
287
287
|
pretty: t.Optional[bool] = None,
|
|
288
288
|
query: t.Optional[str] = None,
|
|
289
289
|
request_timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None,
|
|
@@ -309,6 +309,7 @@ class SynonymsClient(NamespacedClient):
|
|
|
309
309
|
If you need to manage more synonym rules, you can create multiple synonym sets.</p>
|
|
310
310
|
<p>When an existing synonyms set is updated, the search analyzers that use the synonyms set are reloaded automatically for all indices.
|
|
311
311
|
This is equivalent to invoking the reload search analyzers API for all indices that use the synonyms set.</p>
|
|
312
|
+
<p>For practical examples of how to create or update a synonyms set, refer to the External documentation.</p>
|
|
312
313
|
|
|
313
314
|
|
|
314
315
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-synonyms-put-synonym>`_
|
|
@@ -602,6 +602,66 @@ class TransformClient(NamespacedClient):
|
|
|
602
602
|
path_parts=__path_parts,
|
|
603
603
|
)
|
|
604
604
|
|
|
605
|
+
@_rewrite_parameters()
|
|
606
|
+
def set_upgrade_mode(
|
|
607
|
+
self,
|
|
608
|
+
*,
|
|
609
|
+
enabled: t.Optional[bool] = None,
|
|
610
|
+
error_trace: t.Optional[bool] = None,
|
|
611
|
+
filter_path: t.Optional[t.Union[str, t.Sequence[str]]] = None,
|
|
612
|
+
human: t.Optional[bool] = None,
|
|
613
|
+
pretty: t.Optional[bool] = None,
|
|
614
|
+
timeout: t.Optional[t.Union[str, t.Literal[-1], t.Literal[0]]] = None,
|
|
615
|
+
) -> ObjectApiResponse[t.Any]:
|
|
616
|
+
"""
|
|
617
|
+
.. raw:: html
|
|
618
|
+
|
|
619
|
+
<p>Set upgrade_mode for transform indices.
|
|
620
|
+
Sets a cluster wide upgrade_mode setting that prepares transform
|
|
621
|
+
indices for an upgrade.
|
|
622
|
+
When upgrading your cluster, in some circumstances you must restart your
|
|
623
|
+
nodes and reindex your transform indices. In those circumstances,
|
|
624
|
+
there must be no transforms running. You can close the transforms,
|
|
625
|
+
do the upgrade, then open all the transforms again. Alternatively,
|
|
626
|
+
you can use this API to temporarily halt tasks associated with the transforms
|
|
627
|
+
and prevent new transforms from opening. You can also use this API
|
|
628
|
+
during upgrades that do not require you to reindex your transform
|
|
629
|
+
indices, though stopping transforms is not a requirement in that case.
|
|
630
|
+
You can see the current value for the upgrade_mode setting by using the get
|
|
631
|
+
transform info API.</p>
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
`<https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-transform-set-upgrade-mode>`_
|
|
635
|
+
|
|
636
|
+
:param enabled: When `true`, it enables `upgrade_mode` which temporarily halts
|
|
637
|
+
all transform tasks and prohibits new transform tasks from starting.
|
|
638
|
+
:param timeout: The time to wait for the request to be completed.
|
|
639
|
+
"""
|
|
640
|
+
__path_parts: t.Dict[str, str] = {}
|
|
641
|
+
__path = "/_transform/set_upgrade_mode"
|
|
642
|
+
__query: t.Dict[str, t.Any] = {}
|
|
643
|
+
if enabled is not None:
|
|
644
|
+
__query["enabled"] = enabled
|
|
645
|
+
if error_trace is not None:
|
|
646
|
+
__query["error_trace"] = error_trace
|
|
647
|
+
if filter_path is not None:
|
|
648
|
+
__query["filter_path"] = filter_path
|
|
649
|
+
if human is not None:
|
|
650
|
+
__query["human"] = human
|
|
651
|
+
if pretty is not None:
|
|
652
|
+
__query["pretty"] = pretty
|
|
653
|
+
if timeout is not None:
|
|
654
|
+
__query["timeout"] = timeout
|
|
655
|
+
__headers = {"accept": "application/json"}
|
|
656
|
+
return self.perform_request( # type: ignore[return-value]
|
|
657
|
+
"POST",
|
|
658
|
+
__path,
|
|
659
|
+
params=__query,
|
|
660
|
+
headers=__headers,
|
|
661
|
+
endpoint_id="transform.set_upgrade_mode",
|
|
662
|
+
path_parts=__path_parts,
|
|
663
|
+
)
|
|
664
|
+
|
|
605
665
|
@_rewrite_parameters(
|
|
606
666
|
parameter_aliases={"from": "from_"},
|
|
607
667
|
)
|
|
@@ -45,7 +45,8 @@ class WatcherClient(NamespacedClient):
|
|
|
45
45
|
<p>IMPORTANT: If the specified watch is currently being executed, this API will return an error
|
|
46
46
|
The reason for this behavior is to prevent overwriting the watch status from a watch execution.</p>
|
|
47
47
|
<p>Acknowledging an action throttles further executions of that action until its <code>ack.state</code> is reset to <code>awaits_successful_execution</code>.
|
|
48
|
-
This happens when the condition of the watch is not met (the condition evaluates to false)
|
|
48
|
+
This happens when the condition of the watch is not met (the condition evaluates to false).
|
|
49
|
+
To demonstrate how throttling works in practice and how it can be configured for individual actions within a watch, refer to External documentation.</p>
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-watcher-ack-watch>`_
|
|
@@ -274,7 +275,8 @@ class WatcherClient(NamespacedClient):
|
|
|
274
275
|
This serves as great tool for testing and debugging your watches prior to adding them to Watcher.</p>
|
|
275
276
|
<p>When Elasticsearch security features are enabled on your cluster, watches are run with the privileges of the user that stored the watches.
|
|
276
277
|
If your user is allowed to read index <code>a</code>, but not index <code>b</code>, then the exact same set of rules will apply during execution of a watch.</p>
|
|
277
|
-
<p>When using the run watch API, the authorization data of the user that called the API will be used as a base, instead of the information who stored the watch
|
|
278
|
+
<p>When using the run watch API, the authorization data of the user that called the API will be used as a base, instead of the information who stored the watch.
|
|
279
|
+
Refer to the external documentation for examples of watch execution requests, including existing, customized, and inline watches.</p>
|
|
278
280
|
|
|
279
281
|
|
|
280
282
|
`<https://www.elastic.co/docs/api/doc/elasticsearch/v9/operation/operation-watcher-execute-watch>`_
|
elasticsearch/_version.py
CHANGED
elasticsearch/compat.py
CHANGED
|
@@ -16,12 +16,15 @@
|
|
|
16
16
|
# under the License.
|
|
17
17
|
|
|
18
18
|
import inspect
|
|
19
|
+
import os
|
|
19
20
|
import sys
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import Tuple, Type, Union
|
|
22
23
|
|
|
23
24
|
string_types: Tuple[Type[str], Type[bytes]] = (str, bytes)
|
|
24
25
|
|
|
26
|
+
DISABLE_WARN_STACKLEVEL_ENV_VAR = "DISABLE_WARN_STACKLEVEL"
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
def to_str(x: Union[str, bytes], encoding: str = "ascii") -> str:
|
|
27
30
|
if not isinstance(x, str):
|
|
@@ -37,6 +40,8 @@ def to_bytes(x: Union[str, bytes], encoding: str = "ascii") -> bytes:
|
|
|
37
40
|
|
|
38
41
|
def warn_stacklevel() -> int:
|
|
39
42
|
"""Dynamically determine warning stacklevel for warnings based on the call stack"""
|
|
43
|
+
if os.environ.get(DISABLE_WARN_STACKLEVEL_ENV_VAR) in ["1", "true", "True"]:
|
|
44
|
+
return 0
|
|
40
45
|
try:
|
|
41
46
|
# Grab the root module from the current module '__name__'
|
|
42
47
|
module_name = __name__.partition(".")[0]
|
elasticsearch/dsl/__init__.py
CHANGED
|
@@ -19,7 +19,7 @@ from . import async_connections, connections
|
|
|
19
19
|
from .aggs import A, Agg
|
|
20
20
|
from .analysis import analyzer, char_filter, normalizer, token_filter, tokenizer
|
|
21
21
|
from .document import AsyncDocument, Document
|
|
22
|
-
from .document_base import InnerDoc, M, MetaField, mapped_field
|
|
22
|
+
from .document_base import E, InnerDoc, M, MetaField, mapped_field
|
|
23
23
|
from .exceptions import (
|
|
24
24
|
ElasticsearchDslException,
|
|
25
25
|
IllegalOperation,
|
|
@@ -135,6 +135,7 @@ __all__ = [
|
|
|
135
135
|
"Double",
|
|
136
136
|
"DoubleRange",
|
|
137
137
|
"DslBase",
|
|
138
|
+
"E",
|
|
138
139
|
"ElasticsearchDslException",
|
|
139
140
|
"EmptySearch",
|
|
140
141
|
"Facet",
|
|
@@ -20,6 +20,7 @@ from typing import (
|
|
|
20
20
|
TYPE_CHECKING,
|
|
21
21
|
Any,
|
|
22
22
|
AsyncIterable,
|
|
23
|
+
AsyncIterator,
|
|
23
24
|
Dict,
|
|
24
25
|
List,
|
|
25
26
|
Optional,
|
|
@@ -42,6 +43,7 @@ from .search import AsyncSearch
|
|
|
42
43
|
|
|
43
44
|
if TYPE_CHECKING:
|
|
44
45
|
from elasticsearch import AsyncElasticsearch
|
|
46
|
+
from elasticsearch.esql.esql import ESQLBase
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class AsyncIndexMeta(DocumentMeta):
|
|
@@ -520,3 +522,85 @@ class AsyncDocument(DocumentBase, metaclass=AsyncIndexMeta):
|
|
|
520
522
|
return action
|
|
521
523
|
|
|
522
524
|
return await async_bulk(es, Generate(actions), **kwargs)
|
|
525
|
+
|
|
526
|
+
@classmethod
|
|
527
|
+
async def esql_execute(
|
|
528
|
+
cls,
|
|
529
|
+
query: "ESQLBase",
|
|
530
|
+
return_additional: bool = False,
|
|
531
|
+
ignore_missing_fields: bool = False,
|
|
532
|
+
using: Optional[AsyncUsingType] = None,
|
|
533
|
+
**kwargs: Any,
|
|
534
|
+
) -> AsyncIterator[Union[Self, Tuple[Self, Dict[str, Any]]]]:
|
|
535
|
+
"""
|
|
536
|
+
Execute the given ES|QL query and return an iterator of 2-element tuples,
|
|
537
|
+
where the first element is an instance of this ``Document`` and the
|
|
538
|
+
second a dictionary with any remaining columns requested in the query.
|
|
539
|
+
|
|
540
|
+
:arg query: an ES|QL query object created with the ``esql_from()`` method.
|
|
541
|
+
:arg return_additional: if ``False`` (the default), this method returns
|
|
542
|
+
document objects. If set to ``True``, the method returns tuples with
|
|
543
|
+
a document in the first element and a dictionary with any additional
|
|
544
|
+
columns returned by the query in the second element.
|
|
545
|
+
:arg ignore_missing_fields: if ``False`` (the default), all the fields of
|
|
546
|
+
the document must be present in the query, or else an exception is
|
|
547
|
+
raised. Set to ``True`` to allow missing fields, which will result in
|
|
548
|
+
partially initialized document objects.
|
|
549
|
+
:arg using: connection alias to use, defaults to ``'default'``
|
|
550
|
+
:arg kwargs: additional options for the ``client.esql.query()`` function.
|
|
551
|
+
"""
|
|
552
|
+
es = cls._get_connection(using)
|
|
553
|
+
response = await es.esql.query(query=str(query), **kwargs)
|
|
554
|
+
query_columns = [col["name"] for col in response.body.get("columns", [])]
|
|
555
|
+
|
|
556
|
+
# Here we get the list of columns defined in the document, which are the
|
|
557
|
+
# columns that we will take from each result to assemble the document
|
|
558
|
+
# object.
|
|
559
|
+
# When `for_esql=False` is passed below by default, the list will include
|
|
560
|
+
# nested fields, which ES|QL does not return, causing an error. When passing
|
|
561
|
+
# `ignore_missing_fields=True` the list will be generated with
|
|
562
|
+
# `for_esql=True`, so the error will not occur, but the documents will
|
|
563
|
+
# not have any Nested objects in them.
|
|
564
|
+
doc_fields = set(cls._get_field_names(for_esql=ignore_missing_fields))
|
|
565
|
+
if not ignore_missing_fields and not doc_fields.issubset(set(query_columns)):
|
|
566
|
+
raise ValueError(
|
|
567
|
+
f"Not all fields of {cls.__name__} were returned by the query. "
|
|
568
|
+
"Make sure your document does not use Nested fields, which are "
|
|
569
|
+
"currently not supported in ES|QL. To force the query to be "
|
|
570
|
+
"evaluated in spite of the missing fields, pass set the "
|
|
571
|
+
"ignore_missing_fields=True option in the esql_execute() call."
|
|
572
|
+
)
|
|
573
|
+
non_doc_fields: set[str] = set(query_columns) - doc_fields - {"_id"}
|
|
574
|
+
index_id = query_columns.index("_id")
|
|
575
|
+
|
|
576
|
+
results = response.body.get("values", [])
|
|
577
|
+
for column_values in results:
|
|
578
|
+
# create a dictionary with all the document fields, expanding the
|
|
579
|
+
# dot notation returned by ES|QL into the recursive dictionaries
|
|
580
|
+
# used by Document.from_dict()
|
|
581
|
+
doc_dict: Dict[str, Any] = {}
|
|
582
|
+
for col, val in zip(query_columns, column_values):
|
|
583
|
+
if col in doc_fields:
|
|
584
|
+
cols = col.split(".")
|
|
585
|
+
d = doc_dict
|
|
586
|
+
for c in cols[:-1]:
|
|
587
|
+
if c not in d:
|
|
588
|
+
d[c] = {}
|
|
589
|
+
d = d[c]
|
|
590
|
+
d[cols[-1]] = val
|
|
591
|
+
|
|
592
|
+
# create the document instance
|
|
593
|
+
obj = cls(meta={"_id": column_values[index_id]})
|
|
594
|
+
obj._from_dict(doc_dict)
|
|
595
|
+
|
|
596
|
+
if return_additional:
|
|
597
|
+
# build a dict with any other values included in the response
|
|
598
|
+
other = {
|
|
599
|
+
col: val
|
|
600
|
+
for col, val in zip(query_columns, column_values)
|
|
601
|
+
if col in non_doc_fields
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
yield obj, other
|
|
605
|
+
else:
|
|
606
|
+
yield obj
|
|
@@ -21,6 +21,7 @@ from typing import (
|
|
|
21
21
|
Any,
|
|
22
22
|
Dict,
|
|
23
23
|
Iterable,
|
|
24
|
+
Iterator,
|
|
24
25
|
List,
|
|
25
26
|
Optional,
|
|
26
27
|
Tuple,
|
|
@@ -42,6 +43,7 @@ from .search import Search
|
|
|
42
43
|
|
|
43
44
|
if TYPE_CHECKING:
|
|
44
45
|
from elasticsearch import Elasticsearch
|
|
46
|
+
from elasticsearch.esql.esql import ESQLBase
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
class IndexMeta(DocumentMeta):
|
|
@@ -512,3 +514,85 @@ class Document(DocumentBase, metaclass=IndexMeta):
|
|
|
512
514
|
return action
|
|
513
515
|
|
|
514
516
|
return bulk(es, Generate(actions), **kwargs)
|
|
517
|
+
|
|
518
|
+
@classmethod
|
|
519
|
+
def esql_execute(
|
|
520
|
+
cls,
|
|
521
|
+
query: "ESQLBase",
|
|
522
|
+
return_additional: bool = False,
|
|
523
|
+
ignore_missing_fields: bool = False,
|
|
524
|
+
using: Optional[UsingType] = None,
|
|
525
|
+
**kwargs: Any,
|
|
526
|
+
) -> Iterator[Union[Self, Tuple[Self, Dict[str, Any]]]]:
|
|
527
|
+
"""
|
|
528
|
+
Execute the given ES|QL query and return an iterator of 2-element tuples,
|
|
529
|
+
where the first element is an instance of this ``Document`` and the
|
|
530
|
+
second a dictionary with any remaining columns requested in the query.
|
|
531
|
+
|
|
532
|
+
:arg query: an ES|QL query object created with the ``esql_from()`` method.
|
|
533
|
+
:arg return_additional: if ``False`` (the default), this method returns
|
|
534
|
+
document objects. If set to ``True``, the method returns tuples with
|
|
535
|
+
a document in the first element and a dictionary with any additional
|
|
536
|
+
columns returned by the query in the second element.
|
|
537
|
+
:arg ignore_missing_fields: if ``False`` (the default), all the fields of
|
|
538
|
+
the document must be present in the query, or else an exception is
|
|
539
|
+
raised. Set to ``True`` to allow missing fields, which will result in
|
|
540
|
+
partially initialized document objects.
|
|
541
|
+
:arg using: connection alias to use, defaults to ``'default'``
|
|
542
|
+
:arg kwargs: additional options for the ``client.esql.query()`` function.
|
|
543
|
+
"""
|
|
544
|
+
es = cls._get_connection(using)
|
|
545
|
+
response = es.esql.query(query=str(query), **kwargs)
|
|
546
|
+
query_columns = [col["name"] for col in response.body.get("columns", [])]
|
|
547
|
+
|
|
548
|
+
# Here we get the list of columns defined in the document, which are the
|
|
549
|
+
# columns that we will take from each result to assemble the document
|
|
550
|
+
# object.
|
|
551
|
+
# When `for_esql=False` is passed below by default, the list will include
|
|
552
|
+
# nested fields, which ES|QL does not return, causing an error. When passing
|
|
553
|
+
# `ignore_missing_fields=True` the list will be generated with
|
|
554
|
+
# `for_esql=True`, so the error will not occur, but the documents will
|
|
555
|
+
# not have any Nested objects in them.
|
|
556
|
+
doc_fields = set(cls._get_field_names(for_esql=ignore_missing_fields))
|
|
557
|
+
if not ignore_missing_fields and not doc_fields.issubset(set(query_columns)):
|
|
558
|
+
raise ValueError(
|
|
559
|
+
f"Not all fields of {cls.__name__} were returned by the query. "
|
|
560
|
+
"Make sure your document does not use Nested fields, which are "
|
|
561
|
+
"currently not supported in ES|QL. To force the query to be "
|
|
562
|
+
"evaluated in spite of the missing fields, pass set the "
|
|
563
|
+
"ignore_missing_fields=True option in the esql_execute() call."
|
|
564
|
+
)
|
|
565
|
+
non_doc_fields: set[str] = set(query_columns) - doc_fields - {"_id"}
|
|
566
|
+
index_id = query_columns.index("_id")
|
|
567
|
+
|
|
568
|
+
results = response.body.get("values", [])
|
|
569
|
+
for column_values in results:
|
|
570
|
+
# create a dictionary with all the document fields, expanding the
|
|
571
|
+
# dot notation returned by ES|QL into the recursive dictionaries
|
|
572
|
+
# used by Document.from_dict()
|
|
573
|
+
doc_dict: Dict[str, Any] = {}
|
|
574
|
+
for col, val in zip(query_columns, column_values):
|
|
575
|
+
if col in doc_fields:
|
|
576
|
+
cols = col.split(".")
|
|
577
|
+
d = doc_dict
|
|
578
|
+
for c in cols[:-1]:
|
|
579
|
+
if c not in d:
|
|
580
|
+
d[c] = {}
|
|
581
|
+
d = d[c]
|
|
582
|
+
d[cols[-1]] = val
|
|
583
|
+
|
|
584
|
+
# create the document instance
|
|
585
|
+
obj = cls(meta={"_id": column_values[index_id]})
|
|
586
|
+
obj._from_dict(doc_dict)
|
|
587
|
+
|
|
588
|
+
if return_additional:
|
|
589
|
+
# build a dict with any other values included in the response
|
|
590
|
+
other = {
|
|
591
|
+
col: val
|
|
592
|
+
for col, val in zip(query_columns, column_values)
|
|
593
|
+
if col in non_doc_fields
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
yield obj, other
|
|
597
|
+
else:
|
|
598
|
+
yield obj
|