elasticsearch 8.18.0__py3-none-any.whl → 8.19.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 (101) hide show
  1. elasticsearch/_async/client/__init__.py +56 -76
  2. elasticsearch/_async/client/async_search.py +5 -9
  3. elasticsearch/_async/client/autoscaling.py +4 -4
  4. elasticsearch/_async/client/cat.py +620 -65
  5. elasticsearch/_async/client/ccr.py +13 -13
  6. elasticsearch/_async/client/cluster.py +33 -24
  7. elasticsearch/_async/client/connector.py +30 -30
  8. elasticsearch/_async/client/dangling_indices.py +3 -3
  9. elasticsearch/_async/client/enrich.py +5 -5
  10. elasticsearch/_async/client/eql.py +13 -5
  11. elasticsearch/_async/client/esql.py +38 -9
  12. elasticsearch/_async/client/features.py +2 -2
  13. elasticsearch/_async/client/fleet.py +13 -13
  14. elasticsearch/_async/client/graph.py +1 -1
  15. elasticsearch/_async/client/ilm.py +11 -11
  16. elasticsearch/_async/client/indices.py +131 -82
  17. elasticsearch/_async/client/inference.py +516 -110
  18. elasticsearch/_async/client/ingest.py +9 -16
  19. elasticsearch/_async/client/license.py +11 -11
  20. elasticsearch/_async/client/logstash.py +3 -3
  21. elasticsearch/_async/client/migration.py +3 -3
  22. elasticsearch/_async/client/ml.py +81 -93
  23. elasticsearch/_async/client/nodes.py +9 -8
  24. elasticsearch/_async/client/query_rules.py +8 -8
  25. elasticsearch/_async/client/rollup.py +8 -8
  26. elasticsearch/_async/client/search_application.py +10 -10
  27. elasticsearch/_async/client/searchable_snapshots.py +4 -4
  28. elasticsearch/_async/client/security.py +72 -80
  29. elasticsearch/_async/client/shutdown.py +3 -3
  30. elasticsearch/_async/client/simulate.py +1 -1
  31. elasticsearch/_async/client/slm.py +9 -9
  32. elasticsearch/_async/client/snapshot.py +19 -13
  33. elasticsearch/_async/client/sql.py +6 -6
  34. elasticsearch/_async/client/ssl.py +1 -1
  35. elasticsearch/_async/client/synonyms.py +7 -7
  36. elasticsearch/_async/client/tasks.py +3 -3
  37. elasticsearch/_async/client/text_structure.py +4 -4
  38. elasticsearch/_async/client/transform.py +11 -11
  39. elasticsearch/_async/client/watcher.py +13 -13
  40. elasticsearch/_async/client/xpack.py +2 -2
  41. elasticsearch/_sync/client/__init__.py +56 -76
  42. elasticsearch/_sync/client/async_search.py +5 -9
  43. elasticsearch/_sync/client/autoscaling.py +4 -4
  44. elasticsearch/_sync/client/cat.py +620 -65
  45. elasticsearch/_sync/client/ccr.py +13 -13
  46. elasticsearch/_sync/client/cluster.py +33 -24
  47. elasticsearch/_sync/client/connector.py +30 -30
  48. elasticsearch/_sync/client/dangling_indices.py +3 -3
  49. elasticsearch/_sync/client/enrich.py +5 -5
  50. elasticsearch/_sync/client/eql.py +13 -5
  51. elasticsearch/_sync/client/esql.py +38 -9
  52. elasticsearch/_sync/client/features.py +2 -2
  53. elasticsearch/_sync/client/fleet.py +13 -13
  54. elasticsearch/_sync/client/graph.py +1 -1
  55. elasticsearch/_sync/client/ilm.py +11 -11
  56. elasticsearch/_sync/client/indices.py +131 -82
  57. elasticsearch/_sync/client/inference.py +516 -110
  58. elasticsearch/_sync/client/ingest.py +9 -16
  59. elasticsearch/_sync/client/license.py +11 -11
  60. elasticsearch/_sync/client/logstash.py +3 -3
  61. elasticsearch/_sync/client/migration.py +3 -3
  62. elasticsearch/_sync/client/ml.py +81 -93
  63. elasticsearch/_sync/client/nodes.py +9 -8
  64. elasticsearch/_sync/client/query_rules.py +8 -8
  65. elasticsearch/_sync/client/rollup.py +8 -8
  66. elasticsearch/_sync/client/search_application.py +10 -10
  67. elasticsearch/_sync/client/searchable_snapshots.py +4 -4
  68. elasticsearch/_sync/client/security.py +72 -80
  69. elasticsearch/_sync/client/shutdown.py +3 -3
  70. elasticsearch/_sync/client/simulate.py +1 -1
  71. elasticsearch/_sync/client/slm.py +9 -9
  72. elasticsearch/_sync/client/snapshot.py +19 -13
  73. elasticsearch/_sync/client/sql.py +6 -6
  74. elasticsearch/_sync/client/ssl.py +1 -1
  75. elasticsearch/_sync/client/synonyms.py +7 -7
  76. elasticsearch/_sync/client/tasks.py +3 -3
  77. elasticsearch/_sync/client/text_structure.py +4 -4
  78. elasticsearch/_sync/client/transform.py +11 -11
  79. elasticsearch/_sync/client/watcher.py +13 -13
  80. elasticsearch/_sync/client/xpack.py +2 -2
  81. elasticsearch/_version.py +1 -1
  82. elasticsearch/compat.py +5 -0
  83. elasticsearch/dsl/__init__.py +2 -1
  84. elasticsearch/dsl/_async/document.py +1 -1
  85. elasticsearch/dsl/_sync/document.py +1 -1
  86. elasticsearch/dsl/aggs.py +2 -3
  87. elasticsearch/dsl/document_base.py +176 -16
  88. elasticsearch/dsl/field.py +361 -38
  89. elasticsearch/dsl/query.py +55 -4
  90. elasticsearch/dsl/types.py +151 -22
  91. elasticsearch/dsl/utils.py +1 -1
  92. elasticsearch/esql/__init__.py +18 -0
  93. elasticsearch/esql/esql.py +1105 -0
  94. elasticsearch/esql/functions.py +1738 -0
  95. elasticsearch/exceptions.py +2 -0
  96. {elasticsearch-8.18.0.dist-info → elasticsearch-8.19.0.dist-info}/METADATA +1 -1
  97. elasticsearch-8.19.0.dist-info/RECORD +164 -0
  98. elasticsearch-8.18.0.dist-info/RECORD +0 -161
  99. {elasticsearch-8.18.0.dist-info → elasticsearch-8.19.0.dist-info}/WHEEL +0 -0
  100. {elasticsearch-8.18.0.dist-info → elasticsearch-8.19.0.dist-info}/licenses/LICENSE +0 -0
  101. {elasticsearch-8.18.0.dist-info → elasticsearch-8.19.0.dist-info}/licenses/NOTICE +0 -0
@@ -48,7 +48,7 @@ class WatcherClient(NamespacedClient):
48
48
  This happens when the condition of the watch is not met (the condition evaluates to false).</p>
49
49
 
50
50
 
51
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-ack-watch.html>`_
51
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-ack-watch>`_
52
52
 
53
53
  :param watch_id: The watch identifier.
54
54
  :param action_id: A comma-separated list of the action identifiers to acknowledge.
@@ -104,7 +104,7 @@ class WatcherClient(NamespacedClient):
104
104
  A watch can be either active or inactive.</p>
105
105
 
106
106
 
107
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-activate-watch.html>`_
107
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-activate-watch>`_
108
108
 
109
109
  :param watch_id: The watch identifier.
110
110
  """
@@ -148,7 +148,7 @@ class WatcherClient(NamespacedClient):
148
148
  A watch can be either active or inactive.</p>
149
149
 
150
150
 
151
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-deactivate-watch.html>`_
151
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-deactivate-watch>`_
152
152
 
153
153
  :param watch_id: The watch identifier.
154
154
  """
@@ -196,7 +196,7 @@ class WatcherClient(NamespacedClient):
196
196
  When Elasticsearch security features are enabled, make sure no write privileges are granted to anyone for the <code>.watches</code> index.</p>
197
197
 
198
198
 
199
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-delete-watch.html>`_
199
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-delete-watch>`_
200
200
 
201
201
  :param id: The watch identifier.
202
202
  """
@@ -277,7 +277,7 @@ class WatcherClient(NamespacedClient):
277
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.</p>
278
278
 
279
279
 
280
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-execute-watch.html>`_
280
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-execute-watch>`_
281
281
 
282
282
  :param id: The watch identifier.
283
283
  :param action_modes: Determines how to handle the watch actions as part of the
@@ -365,7 +365,7 @@ class WatcherClient(NamespacedClient):
365
365
  Only a subset of settings are shown, for example <code>index.auto_expand_replicas</code> and <code>index.number_of_replicas</code>.</p>
366
366
 
367
367
 
368
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-get-settings.html>`_
368
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-get-settings>`_
369
369
 
370
370
  :param master_timeout: The period to wait for a connection to the master node.
371
371
  If no response is received before the timeout expires, the request fails
@@ -410,7 +410,7 @@ class WatcherClient(NamespacedClient):
410
410
  <p>Get a watch.</p>
411
411
 
412
412
 
413
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-get-watch.html>`_
413
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-get-watch>`_
414
414
 
415
415
  :param id: The watch identifier.
416
416
  """
@@ -485,7 +485,7 @@ class WatcherClient(NamespacedClient):
485
485
  If the user is able to read index <code>a</code>, but not index <code>b</code>, the same will apply when the watch runs.</p>
486
486
 
487
487
 
488
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-put-watch.html>`_
488
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-put-watch>`_
489
489
 
490
490
  :param id: The identifier for the watch.
491
491
  :param actions: The list of actions that will be run if the condition matches.
@@ -598,7 +598,7 @@ class WatcherClient(NamespacedClient):
598
598
  <p>Note that only the <code>_id</code> and <code>metadata.*</code> fields are queryable or sortable.</p>
599
599
 
600
600
 
601
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-query-watches.html>`_
601
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-query-watches>`_
602
602
 
603
603
  :param from_: The offset from the first result to fetch. It must be non-negative.
604
604
  :param query: A query that filters the watches to be returned.
@@ -673,7 +673,7 @@ class WatcherClient(NamespacedClient):
673
673
  Start the Watcher service if it is not already running.</p>
674
674
 
675
675
 
676
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-start.html>`_
676
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-start>`_
677
677
 
678
678
  :param master_timeout: Period to wait for a connection to the master node.
679
679
  """
@@ -739,7 +739,7 @@ class WatcherClient(NamespacedClient):
739
739
  You retrieve more metrics by using the metric parameter.</p>
740
740
 
741
741
 
742
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-stats.html>`_
742
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-stats>`_
743
743
 
744
744
  :param metric: Defines which additional metrics are included in the response.
745
745
  :param emit_stacktraces: Defines whether stack traces are generated for each
@@ -790,7 +790,7 @@ class WatcherClient(NamespacedClient):
790
790
  Stop the Watcher service if it is running.</p>
791
791
 
792
792
 
793
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-stop.html>`_
793
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-stop>`_
794
794
 
795
795
  :param master_timeout: The period to wait for the master node. If the master
796
796
  node is not available before the timeout expires, the request fails and returns
@@ -851,7 +851,7 @@ class WatcherClient(NamespacedClient):
851
851
  Watcher shards must always be in the <code>data_content</code> tier.</p>
852
852
 
853
853
 
854
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/watcher-api-update-settings.html>`_
854
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-watcher-update-settings>`_
855
855
 
856
856
  :param index_auto_expand_replicas:
857
857
  :param index_number_of_replicas:
@@ -54,7 +54,7 @@ class XPackClient(NamespacedClient):
54
54
  </ul>
55
55
 
56
56
 
57
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/info-api.html>`_
57
+ `<https://www.elastic.co/guide/en/elasticsearch/reference/8.19/info-api.html>`_
58
58
 
59
59
  :param accept_enterprise: If this param is used it must be set to true
60
60
  :param categories: A comma-separated list of the information categories to include
@@ -103,7 +103,7 @@ class XPackClient(NamespacedClient):
103
103
  The API also provides some usage statistics.</p>
104
104
 
105
105
 
106
- `<https://www.elastic.co/guide/en/elasticsearch/reference/8.18/usage-api.html>`_
106
+ `<https://www.elastic.co/docs/api/doc/elasticsearch/v8/group/endpoint-xpack>`_
107
107
 
108
108
  :param master_timeout: The period to wait for a connection to the master node.
109
109
  If no response is received before the timeout expires, the request fails
elasticsearch/_version.py CHANGED
@@ -15,4 +15,4 @@
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
17
 
18
- __versionstr__ = "8.18.0"
18
+ __versionstr__ = "8.19.0"
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]
@@ -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",
@@ -96,7 +96,7 @@ class AsyncDocument(DocumentBase, metaclass=AsyncIndexMeta):
96
96
 
97
97
  @classmethod
98
98
  def _get_using(cls, using: Optional[AsyncUsingType] = None) -> AsyncUsingType:
99
- return cast(AsyncUsingType, using or cls._index._using)
99
+ return using or cls._index._using
100
100
 
101
101
  @classmethod
102
102
  def _get_connection(
@@ -92,7 +92,7 @@ class Document(DocumentBase, metaclass=IndexMeta):
92
92
 
93
93
  @classmethod
94
94
  def _get_using(cls, using: Optional[UsingType] = None) -> UsingType:
95
- return cast(UsingType, using or cls._index._using)
95
+ return using or cls._index._using
96
96
 
97
97
  @classmethod
98
98
  def _get_connection(cls, using: Optional[UsingType] = None) -> "Elasticsearch":
elasticsearch/dsl/aggs.py CHANGED
@@ -678,9 +678,8 @@ class CategorizeText(Bucket[_R]):
678
678
  :arg categorization_analyzer: The categorization analyzer specifies
679
679
  how the text is analyzed and tokenized before being categorized.
680
680
  The syntax is very similar to that used to define the analyzer in
681
- the [Analyze endpoint](https://www.elastic.co/guide/en/elasticsear
682
- ch/reference/8.0/indices-analyze.html). This property cannot be
683
- used at the same time as categorization_filters.
681
+ the `_analyze` endpoint. This property cannot be used at the same
682
+ time as categorization_filters.
684
683
  :arg shard_size: The number of categorization buckets to return from
685
684
  each shard before merging all the results.
686
685
  :arg size: The number of buckets to return. Defaults to `10` if
@@ -15,6 +15,7 @@
15
15
  # specific language governing permissions and limitations
16
16
  # under the License.
17
17
 
18
+ import json
18
19
  from datetime import date, datetime
19
20
  from fnmatch import fnmatch
20
21
  from typing import (
@@ -56,7 +57,163 @@ class MetaField:
56
57
  self.args, self.kwargs = args, kwargs
57
58
 
58
59
 
59
- class InstrumentedField:
60
+ class InstrumentedExpression:
61
+ """Proxy object for a ES|QL expression."""
62
+
63
+ def __init__(self, expr: str):
64
+ self._expr = expr
65
+
66
+ def _render_value(self, value: Any) -> str:
67
+ if isinstance(value, InstrumentedExpression):
68
+ return str(value)
69
+ return json.dumps(value)
70
+
71
+ def __str__(self) -> str:
72
+ return self._expr
73
+
74
+ def __repr__(self) -> str:
75
+ return f"InstrumentedExpression[{self._expr}]"
76
+
77
+ def __pos__(self) -> "InstrumentedExpression":
78
+ return self
79
+
80
+ def __neg__(self) -> "InstrumentedExpression":
81
+ return InstrumentedExpression(f"-({self._expr})")
82
+
83
+ def __eq__(self, value: Any) -> "InstrumentedExpression": # type: ignore[override]
84
+ return InstrumentedExpression(f"{self._expr} == {self._render_value(value)}")
85
+
86
+ def __ne__(self, value: Any) -> "InstrumentedExpression": # type: ignore[override]
87
+ return InstrumentedExpression(f"{self._expr} != {self._render_value(value)}")
88
+
89
+ def __lt__(self, value: Any) -> "InstrumentedExpression":
90
+ return InstrumentedExpression(f"{self._expr} < {self._render_value(value)}")
91
+
92
+ def __gt__(self, value: Any) -> "InstrumentedExpression":
93
+ return InstrumentedExpression(f"{self._expr} > {self._render_value(value)}")
94
+
95
+ def __le__(self, value: Any) -> "InstrumentedExpression":
96
+ return InstrumentedExpression(f"{self._expr} <= {self._render_value(value)}")
97
+
98
+ def __ge__(self, value: Any) -> "InstrumentedExpression":
99
+ return InstrumentedExpression(f"{self._expr} >= {self._render_value(value)}")
100
+
101
+ def __add__(self, value: Any) -> "InstrumentedExpression":
102
+ return InstrumentedExpression(f"{self._expr} + {self._render_value(value)}")
103
+
104
+ def __radd__(self, value: Any) -> "InstrumentedExpression":
105
+ return InstrumentedExpression(f"{self._render_value(value)} + {self._expr}")
106
+
107
+ def __sub__(self, value: Any) -> "InstrumentedExpression":
108
+ return InstrumentedExpression(f"{self._expr} - {self._render_value(value)}")
109
+
110
+ def __rsub__(self, value: Any) -> "InstrumentedExpression":
111
+ return InstrumentedExpression(f"{self._render_value(value)} - {self._expr}")
112
+
113
+ def __mul__(self, value: Any) -> "InstrumentedExpression":
114
+ return InstrumentedExpression(f"{self._expr} * {self._render_value(value)}")
115
+
116
+ def __rmul__(self, value: Any) -> "InstrumentedExpression":
117
+ return InstrumentedExpression(f"{self._render_value(value)} * {self._expr}")
118
+
119
+ def __truediv__(self, value: Any) -> "InstrumentedExpression":
120
+ return InstrumentedExpression(f"{self._expr} / {self._render_value(value)}")
121
+
122
+ def __rtruediv__(self, value: Any) -> "InstrumentedExpression":
123
+ return InstrumentedExpression(f"{self._render_value(value)} / {self._expr}")
124
+
125
+ def __mod__(self, value: Any) -> "InstrumentedExpression":
126
+ return InstrumentedExpression(f"{self._expr} % {self._render_value(value)}")
127
+
128
+ def __rmod__(self, value: Any) -> "InstrumentedExpression":
129
+ return InstrumentedExpression(f"{self._render_value(value)} % {self._expr}")
130
+
131
+ def is_null(self) -> "InstrumentedExpression":
132
+ """Compare the expression against NULL."""
133
+ return InstrumentedExpression(f"{self._expr} IS NULL")
134
+
135
+ def is_not_null(self) -> "InstrumentedExpression":
136
+ """Compare the expression against NOT NULL."""
137
+ return InstrumentedExpression(f"{self._expr} IS NOT NULL")
138
+
139
+ def in_(self, *values: Any) -> "InstrumentedExpression":
140
+ """Test if the expression equals one of the given values."""
141
+ rendered_values = ", ".join([f"{value}" for value in values])
142
+ return InstrumentedExpression(f"{self._expr} IN ({rendered_values})")
143
+
144
+ def like(self, *patterns: str) -> "InstrumentedExpression":
145
+ """Filter the expression using a string pattern."""
146
+ if len(patterns) == 1:
147
+ return InstrumentedExpression(
148
+ f"{self._expr} LIKE {self._render_value(patterns[0])}"
149
+ )
150
+ else:
151
+ return InstrumentedExpression(
152
+ f'{self._expr} LIKE ({", ".join([self._render_value(p) for p in patterns])})'
153
+ )
154
+
155
+ def rlike(self, *patterns: str) -> "InstrumentedExpression":
156
+ """Filter the expression using a regular expression."""
157
+ if len(patterns) == 1:
158
+ return InstrumentedExpression(
159
+ f"{self._expr} RLIKE {self._render_value(patterns[0])}"
160
+ )
161
+ else:
162
+ return InstrumentedExpression(
163
+ f'{self._expr} RLIKE ({", ".join([self._render_value(p) for p in patterns])})'
164
+ )
165
+
166
+ def match(self, query: str) -> "InstrumentedExpression":
167
+ """Perform a match query on the field."""
168
+ return InstrumentedExpression(f"{self._expr}:{self._render_value(query)}")
169
+
170
+ def asc(self) -> "InstrumentedExpression":
171
+ """Return the field name representation for ascending sort order.
172
+
173
+ For use in ES|QL queries only.
174
+ """
175
+ return InstrumentedExpression(f"{self._expr} ASC")
176
+
177
+ def desc(self) -> "InstrumentedExpression":
178
+ """Return the field name representation for descending sort order.
179
+
180
+ For use in ES|QL queries only.
181
+ """
182
+ return InstrumentedExpression(f"{self._expr} DESC")
183
+
184
+ def nulls_first(self) -> "InstrumentedExpression":
185
+ """Return the field name representation for nulls first sort order.
186
+
187
+ For use in ES|QL queries only.
188
+ """
189
+ return InstrumentedExpression(f"{self._expr} NULLS FIRST")
190
+
191
+ def nulls_last(self) -> "InstrumentedExpression":
192
+ """Return the field name representation for nulls last sort order.
193
+
194
+ For use in ES|QL queries only.
195
+ """
196
+ return InstrumentedExpression(f"{self._expr} NULLS LAST")
197
+
198
+ def where(
199
+ self, *expressions: Union[str, "InstrumentedExpression"]
200
+ ) -> "InstrumentedExpression":
201
+ """Add a condition to be met for the row to be included.
202
+
203
+ Use only in expressions given in the ``STATS`` command.
204
+ """
205
+ if len(expressions) == 1:
206
+ return InstrumentedExpression(f"{self._expr} WHERE {expressions[0]}")
207
+ else:
208
+ return InstrumentedExpression(
209
+ f'{self._expr} WHERE {" AND ".join([f"({expr})" for expr in expressions])}'
210
+ )
211
+
212
+
213
+ E = InstrumentedExpression
214
+
215
+
216
+ class InstrumentedField(InstrumentedExpression):
60
217
  """Proxy object for a mapped document field.
61
218
 
62
219
  An object of this instance is returned when a field is accessed as a class
@@ -71,8 +228,8 @@ class InstrumentedField:
71
228
  s = s.sort(-MyDocument.name) # sort by name in descending order
72
229
  """
73
230
 
74
- def __init__(self, name: str, field: Field):
75
- self._name = name
231
+ def __init__(self, name: str, field: Optional[Field]):
232
+ super().__init__(name)
76
233
  self._field = field
77
234
 
78
235
  # note that the return value type here assumes classes will only be used to
@@ -83,26 +240,29 @@ class InstrumentedField:
83
240
  # first let's see if this is an attribute of this object
84
241
  return super().__getattribute__(attr) # type: ignore[no-any-return]
85
242
  except AttributeError:
86
- try:
87
- # next we see if we have a sub-field with this name
88
- return InstrumentedField(f"{self._name}.{attr}", self._field[attr])
89
- except KeyError:
90
- # lastly we let the wrapped field resolve this attribute
91
- return getattr(self._field, attr) # type: ignore[no-any-return]
92
-
93
- def __pos__(self) -> str:
243
+ if self._field:
244
+ try:
245
+ # next we see if we have a sub-field with this name
246
+ return InstrumentedField(f"{self._expr}.{attr}", self._field[attr])
247
+ except KeyError:
248
+ # lastly we let the wrapped field resolve this attribute
249
+ return getattr(self._field, attr) # type: ignore[no-any-return]
250
+ else:
251
+ raise
252
+
253
+ def __pos__(self) -> str: # type: ignore[override]
94
254
  """Return the field name representation for ascending sort order"""
95
- return f"{self._name}"
255
+ return f"{self._expr}"
96
256
 
97
- def __neg__(self) -> str:
257
+ def __neg__(self) -> str: # type: ignore[override]
98
258
  """Return the field name representation for descending sort order"""
99
- return f"-{self._name}"
259
+ return f"-{self._expr}"
100
260
 
101
261
  def __str__(self) -> str:
102
- return self._name
262
+ return self._expr
103
263
 
104
264
  def __repr__(self) -> str:
105
- return f"InstrumentedField[{self._name}]"
265
+ return f"InstrumentedField[{self._expr}]"
106
266
 
107
267
 
108
268
  class DocumentMeta(type):