elasticsearch9 9.1.3__py3-none-any.whl → 9.2.1__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 (60) hide show
  1. elasticsearch9/_async/client/__init__.py +91 -24
  2. elasticsearch9/_async/client/async_search.py +7 -0
  3. elasticsearch9/_async/client/autoscaling.py +7 -0
  4. elasticsearch9/_async/client/cat.py +8 -1
  5. elasticsearch9/_async/client/cluster.py +7 -7
  6. elasticsearch9/_async/client/eql.py +7 -0
  7. elasticsearch9/_async/client/esql.py +26 -3
  8. elasticsearch9/_async/client/indices.py +153 -7
  9. elasticsearch9/_async/client/inference.py +315 -42
  10. elasticsearch9/_async/client/ingest.py +8 -0
  11. elasticsearch9/_async/client/license.py +4 -2
  12. elasticsearch9/_async/client/ml.py +2 -2
  13. elasticsearch9/_async/client/nodes.py +2 -4
  14. elasticsearch9/_async/client/project.py +68 -0
  15. elasticsearch9/_async/client/security.py +39 -0
  16. elasticsearch9/_async/client/shutdown.py +6 -0
  17. elasticsearch9/_async/client/simulate.py +8 -0
  18. elasticsearch9/_async/client/snapshot.py +20 -10
  19. elasticsearch9/_async/client/sql.py +7 -0
  20. elasticsearch9/_async/client/streams.py +2 -3
  21. elasticsearch9/_async/helpers.py +28 -15
  22. elasticsearch9/_sync/client/__init__.py +91 -24
  23. elasticsearch9/_sync/client/async_search.py +7 -0
  24. elasticsearch9/_sync/client/autoscaling.py +7 -0
  25. elasticsearch9/_sync/client/cat.py +8 -1
  26. elasticsearch9/_sync/client/cluster.py +7 -7
  27. elasticsearch9/_sync/client/eql.py +7 -0
  28. elasticsearch9/_sync/client/esql.py +26 -3
  29. elasticsearch9/_sync/client/indices.py +153 -7
  30. elasticsearch9/_sync/client/inference.py +315 -42
  31. elasticsearch9/_sync/client/ingest.py +8 -0
  32. elasticsearch9/_sync/client/license.py +4 -2
  33. elasticsearch9/_sync/client/ml.py +2 -2
  34. elasticsearch9/_sync/client/nodes.py +2 -4
  35. elasticsearch9/_sync/client/project.py +68 -0
  36. elasticsearch9/_sync/client/security.py +39 -0
  37. elasticsearch9/_sync/client/shutdown.py +6 -0
  38. elasticsearch9/_sync/client/simulate.py +8 -0
  39. elasticsearch9/_sync/client/snapshot.py +20 -10
  40. elasticsearch9/_sync/client/sql.py +7 -0
  41. elasticsearch9/_sync/client/streams.py +2 -3
  42. elasticsearch9/_version.py +2 -2
  43. elasticsearch9/client.py +2 -0
  44. elasticsearch9/compat.py +2 -15
  45. elasticsearch9/dsl/_async/document.py +2 -1
  46. elasticsearch9/dsl/_sync/document.py +2 -1
  47. elasticsearch9/dsl/document_base.py +38 -13
  48. elasticsearch9/dsl/field.py +8 -0
  49. elasticsearch9/dsl/pydantic.py +152 -0
  50. elasticsearch9/dsl/query.py +5 -1
  51. elasticsearch9/dsl/search_base.py +5 -1
  52. elasticsearch9/dsl/types.py +37 -9
  53. elasticsearch9/esql/esql.py +331 -41
  54. elasticsearch9/esql/functions.py +88 -0
  55. elasticsearch9/helpers/actions.py +1 -1
  56. {elasticsearch9-9.1.3.dist-info → elasticsearch9-9.2.1.dist-info}/METADATA +26 -4
  57. {elasticsearch9-9.1.3.dist-info → elasticsearch9-9.2.1.dist-info}/RECORD +60 -57
  58. {elasticsearch9-9.1.3.dist-info → elasticsearch9-9.2.1.dist-info}/WHEEL +0 -0
  59. {elasticsearch9-9.1.3.dist-info → elasticsearch9-9.2.1.dist-info}/licenses/LICENSE +0 -0
  60. {elasticsearch9-9.1.3.dist-info → elasticsearch9-9.2.1.dist-info}/licenses/NOTICE +0 -0
@@ -18,7 +18,7 @@
18
18
  import json
19
19
  import re
20
20
  from abc import ABC, abstractmethod
21
- from typing import Any, Dict, Optional, Tuple, Type, Union
21
+ from typing import Any, Dict, List, Optional, Tuple, Type, Union
22
22
 
23
23
  from ..dsl.document_base import DocumentBase, InstrumentedExpression, InstrumentedField
24
24
 
@@ -78,6 +78,22 @@ class ESQL(ABC):
78
78
  """
79
79
  return Show(item)
80
80
 
81
+ @staticmethod
82
+ def ts(*indices: IndexType) -> "TS":
83
+ """The ``TS`` source command is similar to ``FROM``, but for time series indices.
84
+
85
+ :param indices: A list of indices, data streams or aliases. Supports wildcards and date math.
86
+
87
+ Examples::
88
+
89
+ query = (
90
+ ESQL.ts("metrics")
91
+ .where("@timestamp >= now() - 1 day")
92
+ .stats("SUM(AVG_OVER_TIME(memory_usage)").by("host", "TBUCKET(1 hour)")
93
+ )
94
+ """
95
+ return TS(*indices)
96
+
81
97
  @staticmethod
82
98
  def branch() -> "Branch":
83
99
  """This method can only be used inside a ``FORK`` command to create each branch.
@@ -143,7 +159,7 @@ class ESQLBase(ABC):
143
159
  return False
144
160
 
145
161
  def change_point(self, value: FieldType) -> "ChangePoint":
146
- """`CHANGE_POINT` detects spikes, dips, and change points in a metric.
162
+ """``CHANGE_POINT`` detects spikes, dips, and change points in a metric.
147
163
 
148
164
  :param value: The column with the metric in which you want to detect a change point.
149
165
 
@@ -163,17 +179,18 @@ class ESQLBase(ABC):
163
179
  def completion(
164
180
  self, *prompt: ExpressionType, **named_prompt: ExpressionType
165
181
  ) -> "Completion":
166
- """The `COMPLETION` command allows you to send prompts and context to a Large
182
+ """The ``COMPLETION`` command allows you to send prompts and context to a Large
167
183
  Language Model (LLM) directly within your ES|QL queries, to perform text
168
184
  generation tasks.
169
185
 
170
186
  :param prompt: The input text or expression used to prompt the LLM. This can
171
187
  be a string literal or a reference to a column containing text.
172
188
  :param named_prompt: The input text or expresion, given as a keyword argument.
173
- The argument name is used for the column name. If not
174
- specified, the results will be stored in a column named
175
- `completion`. If the specified column already exists, it
176
- will be overwritten with the new results.
189
+ The argument name is used for the column name. If the
190
+ prompt is given as a positional argument, the results will
191
+ be stored in a column named ``completion``. If the
192
+ specified column already exists, it will be overwritten
193
+ with the new results.
177
194
 
178
195
  Examples::
179
196
 
@@ -283,14 +300,14 @@ class ESQLBase(ABC):
283
300
 
284
301
  def fork(
285
302
  self,
286
- fork1: "ESQLBase",
287
- fork2: Optional["ESQLBase"] = None,
288
- fork3: Optional["ESQLBase"] = None,
289
- fork4: Optional["ESQLBase"] = None,
290
- fork5: Optional["ESQLBase"] = None,
291
- fork6: Optional["ESQLBase"] = None,
292
- fork7: Optional["ESQLBase"] = None,
293
- fork8: Optional["ESQLBase"] = None,
303
+ fork1: "Branch",
304
+ fork2: Optional["Branch"] = None,
305
+ fork3: Optional["Branch"] = None,
306
+ fork4: Optional["Branch"] = None,
307
+ fork5: Optional["Branch"] = None,
308
+ fork6: Optional["Branch"] = None,
309
+ fork7: Optional["Branch"] = None,
310
+ fork8: Optional["Branch"] = None,
294
311
  ) -> "Fork":
295
312
  """The ``FORK`` processing command creates multiple execution branches to operate on the
296
313
  same input data and combines the results in a single output table.
@@ -313,6 +330,51 @@ class ESQLBase(ABC):
313
330
  raise ValueError("a query can only have one fork")
314
331
  return Fork(self, fork1, fork2, fork3, fork4, fork5, fork6, fork7, fork8)
315
332
 
333
+ def fuse(self, method: Optional[str] = None) -> "Fuse":
334
+ """The ``FUSE`` processing command merges rows from multiple result sets and assigns
335
+ new relevance scores.
336
+
337
+ :param method: Defaults to ``RRF``. Can be one of ``RRF`` (for Reciprocal Rank Fusion)
338
+ or ``LINEAR`` (for linear combination of scores). Designates which
339
+ method to use to assign new relevance scores.
340
+
341
+ Examples::
342
+
343
+ query1 = (
344
+ ESQL.from_("books").metadata("_id", "_index", "_score")
345
+ .fork(
346
+ ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"),
347
+ ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"),
348
+ )
349
+ .fuse()
350
+ )
351
+ query2 = (
352
+ ESQL.from_("books").metadata("_id", "_index", "_score")
353
+ .fork(
354
+ ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"),
355
+ ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"),
356
+ )
357
+ .fuse("linear")
358
+ )
359
+ query3 = (
360
+ ESQL.from_("books").metadata("_id", "_index", "_score")
361
+ .fork(
362
+ ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"),
363
+ ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"),
364
+ )
365
+ .fuse("linear").by("title", "description")
366
+ )
367
+ query4 = (
368
+ ESQL.from_("books").metadata("_id", "_index", "_score")
369
+ .fork(
370
+ ESQL.branch().where('title:"Shakespeare"').sort("_score DESC"),
371
+ ESQL.branch().where('semantic_title:"Shakespeare"').sort("_score DESC"),
372
+ )
373
+ .fuse("linear").with_(normalizer="minmax")
374
+ )
375
+ """
376
+ return Fuse(self, method)
377
+
316
378
  def grok(self, input: FieldType, pattern: str) -> "Grok":
317
379
  """``GROK`` enables you to extract structured data out of a string.
318
380
 
@@ -347,6 +409,58 @@ class ESQLBase(ABC):
347
409
  """
348
410
  return Grok(self, input, pattern)
349
411
 
412
+ def inline_stats(
413
+ self, *expressions: ExpressionType, **named_expressions: ExpressionType
414
+ ) -> "Stats":
415
+ """The ``INLINE STATS`` processing command groups rows according to a common value
416
+ and calculates one or more aggregated values over the grouped rows.
417
+
418
+ The command is identical to ``STATS`` except that it preserves all the columns from
419
+ the input table.
420
+
421
+ :param expressions: A list of expressions, given as positional arguments.
422
+ :param named_expressions: A list of expressions, given as keyword arguments. The
423
+ argument names are used for the returned aggregated values.
424
+
425
+ Note that only one of ``expressions`` and ``named_expressions`` must be provided.
426
+
427
+ Examples::
428
+
429
+ query1 = (
430
+ ESQL.from_("employees")
431
+ .keep("emp_no", "languages", "salary")
432
+ .inline_stats(max_salary=functions.max(E("salary"))).by("languages")
433
+ )
434
+ query2 = (
435
+ ESQL.from_("employees")
436
+ .keep("emp_no", "languages", "salary")
437
+ .inline_stats(max_salary=functions.max(E("salary")))
438
+ )
439
+ query3 = (
440
+ ESQL.from_("employees")
441
+ .where("still_hired")
442
+ .keep("emp_no", "languages", "salary", "hire_date")
443
+ .eval(tenure=functions.date_diff("year", E("hire_date"), "2025-09-18T00:00:00"))
444
+ .drop("hire_date")
445
+ .inline_stats(
446
+ avg_salary=functions.avg(E("salary")),
447
+ count=functions.count(E("*")),
448
+ )
449
+ .by("languages", "tenure")
450
+ )
451
+ query4 = (
452
+ ESQL.from_("employees")
453
+ .keep("emp_no", "salary")
454
+ .inline_stats(
455
+ avg_lt_50=functions.round(functions.avg(E("salary"))).where(E("salary") < 50000),
456
+ avg_lt_60=functions.round(functions.avg(E("salary"))).where(E("salary") >= 50000, E("salary") < 60000),
457
+ avg_gt_60=functions.round(functions.avg(E("salary"))).where(E("salary") >= 60000),
458
+ )
459
+ )
460
+
461
+ """
462
+ return InlineStats(self, *expressions, **named_expressions)
463
+
350
464
  def keep(self, *columns: FieldType) -> "Keep":
351
465
  """The ``KEEP`` processing command enables you to specify what columns are returned
352
466
  and the order in which they are returned.
@@ -376,7 +490,7 @@ class ESQLBase(ABC):
376
490
  return Limit(self, max_number_of_rows)
377
491
 
378
492
  def lookup_join(self, lookup_index: IndexType) -> "LookupJoin":
379
- """`LOOKUP JOIN` enables you to add data from another index, AKA a 'lookup' index,
493
+ """``LOOKUP JOIN`` enables you to add data from another index, AKA a 'lookup' index,
380
494
  to your ES|QL query results, simplifying data enrichment and analysis workflows.
381
495
 
382
496
  :param lookup_index: The name of the lookup index. This must be a specific index
@@ -410,7 +524,7 @@ class ESQLBase(ABC):
410
524
  return LookupJoin(self, lookup_index)
411
525
 
412
526
  def mv_expand(self, column: FieldType) -> "MvExpand":
413
- """The `MV_EXPAND` processing command expands multivalued columns into one row per
527
+ """The ``MV_EXPAND`` processing command expands multivalued columns into one row per
414
528
  value, duplicating other columns.
415
529
 
416
530
  :param column: The multivalued column to expand.
@@ -439,6 +553,54 @@ class ESQLBase(ABC):
439
553
  """
440
554
  return Rename(self, **columns)
441
555
 
556
+ def rerank(self, *query: ExpressionType, **named_query: ExpressionType) -> "Rerank":
557
+ """The ``RERANK`` command uses an inference model to compute a new relevance score
558
+ for an initial set of documents, directly within your ES|QL queries.
559
+
560
+ :param query: The query text used to rerank the documents. This is typically the
561
+ same query used in the initial search.
562
+ :param named_query: The query text used to rerank the documents, given as a
563
+ keyword argument. The argument name is used for the column
564
+ name. If the query is given as a positional argument, the
565
+ results will be stored in a column named ``_score``. If the
566
+ specified column already exists, it will be overwritten with
567
+ the new results.
568
+
569
+ Examples::
570
+
571
+ query1 = (
572
+ ESQL.from_("books").metadata("_score")
573
+ .where('MATCH(description, "hobbit")')
574
+ .sort("_score DESC")
575
+ .limit(100)
576
+ .rerank("hobbit").on("description").with_(inference_id="test_reranker")
577
+ .limit(3)
578
+ .keep("title", "_score")
579
+ )
580
+ query2 = (
581
+ ESQL.from_("books").metadata("_score")
582
+ .where('MATCH(description, "hobbit") OR MATCH(author, "Tolkien")')
583
+ .sort("_score DESC")
584
+ .limit(100)
585
+ .rerank(rerank_score="hobbit").on("description", "author").with_(inference_id="test_reranker")
586
+ .sort("rerank_score")
587
+ .limit(3)
588
+ .keep("title", "_score", "rerank_score")
589
+ )
590
+ query3 = (
591
+ ESQL.from_("books").metadata("_score")
592
+ .where('MATCH(description, "hobbit") OR MATCH(author, "Tolkien")')
593
+ .sort("_score DESC")
594
+ .limit(100)
595
+ .rerank(rerank_score="hobbit").on("description", "author").with_(inference_id="test_reranker")
596
+ .eval(original_score="_score", _score="rerank_score + original_score")
597
+ .sort("_score")
598
+ .limit(3)
599
+ .keep("title", "original_score", "rerank_score", "_score")
600
+ )
601
+ """
602
+ return Rerank(self, *query, **named_query)
603
+
442
604
  def sample(self, probability: float) -> "Sample":
443
605
  """The ``SAMPLE`` command samples a fraction of the table rows.
444
606
 
@@ -491,7 +653,7 @@ class ESQLBase(ABC):
491
653
  :param named_expressions: A list of expressions, given as keyword arguments. The
492
654
  argument names are used for the returned aggregated values.
493
655
 
494
- Note that only one of `expressions` and `named_expressions` must be provided.
656
+ Note that only one of ``expressions`` and ``named_expressions`` must be provided.
495
657
 
496
658
  Examples::
497
659
 
@@ -547,7 +709,7 @@ class ESQLBase(ABC):
547
709
 
548
710
  def where(self, *expressions: ExpressionType) -> "Where":
549
711
  """The ``WHERE`` processing command produces a table that contains all the rows
550
- from the input table for which the provided condition evaluates to `true`.
712
+ from the input table for which the provided condition evaluates to ``true``.
551
713
 
552
714
  :param expressions: A list of boolean expressions, given as positional arguments.
553
715
  These expressions are combined with an ``AND`` logical operator.
@@ -580,13 +742,15 @@ class From(ESQLBase):
580
742
  in a single expression.
581
743
  """
582
744
 
745
+ command_name = "FROM"
746
+
583
747
  def __init__(self, *indices: IndexType):
584
748
  super().__init__()
585
749
  self._indices = indices
586
750
  self._metadata_fields: Tuple[FieldType, ...] = tuple()
587
751
 
588
752
  def metadata(self, *fields: FieldType) -> "From":
589
- """Continuation of the ``FROM`` source command.
753
+ """Continuation of the ``FROM`` and ``TS`` source commands.
590
754
 
591
755
  :param fields: metadata fields to retrieve, given as positional arguments.
592
756
  """
@@ -595,7 +759,7 @@ class From(ESQLBase):
595
759
 
596
760
  def _render_internal(self) -> str:
597
761
  indices = [self._format_index(index) for index in self._indices]
598
- s = f'{self.__class__.__name__.upper()} {", ".join(indices)}'
762
+ s = f'{self.command_name} {", ".join(indices)}'
599
763
  if self._metadata_fields:
600
764
  s = (
601
765
  s
@@ -643,6 +807,17 @@ class Show(ESQLBase):
643
807
  return f"SHOW {self._format_id(self._item)}"
644
808
 
645
809
 
810
+ class TS(From):
811
+ """Implementation of the ``TS`` source command.
812
+
813
+ This class inherits from :class:`ESQLBase <elasticsearch.esql.esql.ESQLBase>`,
814
+ to make it possible to chain all the commands that belong to an ES|QL query
815
+ in a single expression.
816
+ """
817
+
818
+ command_name = "TS"
819
+
820
+
646
821
  class Branch(ESQLBase):
647
822
  """Implementation of a branch inside a ``FORK`` processing command.
648
823
 
@@ -671,21 +846,22 @@ class ChangePoint(ESQLBase):
671
846
  self._pvalue_name: Optional[str] = None
672
847
 
673
848
  def on(self, key: FieldType) -> "ChangePoint":
674
- """Continuation of the `CHANGE_POINT` command.
849
+ """Continuation of the ``CHANGE_POINT`` command.
675
850
 
676
851
  :param key: The column with the key to order the values by. If not specified,
677
- `@timestamp` is used.
852
+ ``@timestamp`` is used.
678
853
  """
679
854
  self._key = key
680
855
  return self
681
856
 
682
857
  def as_(self, type_name: str, pvalue_name: str) -> "ChangePoint":
683
- """Continuation of the `CHANGE_POINT` command.
858
+ """Continuation of the ``CHANGE_POINT`` command.
684
859
 
685
860
  :param type_name: The name of the output column with the change point type.
686
- If not specified, `type` is used.
861
+ If not specified, ``type`` is used.
687
862
  :param pvalue_name: The name of the output column with the p-value that indicates
688
- how extreme the change point is. If not specified, `pvalue` is used.
863
+ how extreme the change point is. If not specified, ``pvalue``
864
+ is used.
689
865
  """
690
866
  self._type_name = type_name
691
867
  self._pvalue_name = pvalue_name
@@ -722,10 +898,10 @@ class Completion(ESQLBase):
722
898
  self._inference_id: Optional[str] = None
723
899
 
724
900
  def with_(self, inference_id: str) -> "Completion":
725
- """Continuation of the `COMPLETION` command.
901
+ """Continuation of the ``COMPLETION`` command.
726
902
 
727
903
  :param inference_id: The ID of the inference endpoint to use for the task. The
728
- inference endpoint must be configured with the completion
904
+ inference endpoint must be configured with the ``completion``
729
905
  task type.
730
906
  """
731
907
  self._inference_id = inference_id
@@ -814,7 +990,7 @@ class Enrich(ESQLBase):
814
990
  :param match_field: The match field. ``ENRICH`` uses its value to look for records
815
991
  in the enrich index. If not specified, the match will be
816
992
  performed on the column with the same name as the
817
- `match_field` defined in the enrich policy.
993
+ ``match_field`` defined in the enrich policy.
818
994
  """
819
995
  self._match_field = match_field
820
996
  return self
@@ -904,14 +1080,14 @@ class Fork(ESQLBase):
904
1080
  def __init__(
905
1081
  self,
906
1082
  parent: ESQLBase,
907
- fork1: ESQLBase,
908
- fork2: Optional[ESQLBase] = None,
909
- fork3: Optional[ESQLBase] = None,
910
- fork4: Optional[ESQLBase] = None,
911
- fork5: Optional[ESQLBase] = None,
912
- fork6: Optional[ESQLBase] = None,
913
- fork7: Optional[ESQLBase] = None,
914
- fork8: Optional[ESQLBase] = None,
1083
+ fork1: "Branch",
1084
+ fork2: Optional["Branch"] = None,
1085
+ fork3: Optional["Branch"] = None,
1086
+ fork4: Optional["Branch"] = None,
1087
+ fork5: Optional["Branch"] = None,
1088
+ fork6: Optional["Branch"] = None,
1089
+ fork7: Optional["Branch"] = None,
1090
+ fork8: Optional["Branch"] = None,
915
1091
  ):
916
1092
  super().__init__(parent)
917
1093
  self._branches = [fork1, fork2, fork3, fork4, fork5, fork6, fork7, fork8]
@@ -928,6 +1104,39 @@ class Fork(ESQLBase):
928
1104
  return f"FORK {cmds}"
929
1105
 
930
1106
 
1107
+ class Fuse(ESQLBase):
1108
+ """Implementation of the ``FUSE`` processing command.
1109
+
1110
+ This class inherits from :class:`ESQLBase <elasticsearch.esql.esql.ESQLBase>`,
1111
+ to make it possible to chain all the commands that belong to an ES|QL query
1112
+ in a single expression.
1113
+ """
1114
+
1115
+ def __init__(self, parent: ESQLBase, method: Optional[str] = None):
1116
+ super().__init__(parent)
1117
+ self.method = method
1118
+ self.by_columns: List[FieldType] = []
1119
+ self.options: Dict[str, Any] = {}
1120
+
1121
+ def by(self, *columns: FieldType) -> "Fuse":
1122
+ self.by_columns += list(columns)
1123
+ return self
1124
+
1125
+ def with_(self, **options: Any) -> "Fuse":
1126
+ self.options = options
1127
+ return self
1128
+
1129
+ def _render_internal(self) -> str:
1130
+ method = f" {self.method.upper()}" if self.method else ""
1131
+ by = (
1132
+ " " + " ".join([f"BY {column}" for column in self.by_columns])
1133
+ if self.by_columns
1134
+ else ""
1135
+ )
1136
+ with_ = " WITH " + json.dumps(self.options) if self.options else ""
1137
+ return f"FUSE{method}{by}{with_}"
1138
+
1139
+
931
1140
  class Grok(ESQLBase):
932
1141
  """Implementation of the ``GROK`` processing command.
933
1142
 
@@ -991,7 +1200,7 @@ class LookupJoin(ESQLBase):
991
1200
  self._field: Optional[FieldType] = None
992
1201
 
993
1202
  def on(self, field: FieldType) -> "LookupJoin":
994
- """Continuation of the `LOOKUP_JOIN` command.
1203
+ """Continuation of the ``LOOKUP JOIN`` command.
995
1204
 
996
1205
  :param field: The field to join on. This field must exist in both your current query
997
1206
  results and in the lookup index. If the field contains multi-valued
@@ -1046,6 +1255,67 @@ class Rename(ESQLBase):
1046
1255
  return f'RENAME {", ".join([f"{self._format_id(old_name)} AS {self._format_id(new_name)}" for old_name, new_name in self._columns.items()])}'
1047
1256
 
1048
1257
 
1258
+ class Rerank(ESQLBase):
1259
+ """Implementation of the ``RERANK`` processing command.
1260
+
1261
+ This class inherits from :class:`ESQLBase <elasticsearch.esql.esql.ESQLBase>`,
1262
+ to make it possible to chain all the commands that belong to an ES|QL query
1263
+ in a single expression.
1264
+ """
1265
+
1266
+ def __init__(
1267
+ self, parent: ESQLBase, *query: ExpressionType, **named_query: ExpressionType
1268
+ ):
1269
+ if len(query) + len(named_query) > 1:
1270
+ raise ValueError(
1271
+ "this method requires either one positional or one keyword argument only"
1272
+ )
1273
+ super().__init__(parent)
1274
+ self._query = query
1275
+ self._named_query = named_query
1276
+ self._fields: Optional[Tuple[str, ...]] = None
1277
+ self._inference_id: Optional[str] = None
1278
+
1279
+ def on(self, *fields: str) -> "Rerank":
1280
+ """Continuation of the ``RERANK`` command.
1281
+
1282
+ :param fields: One or more fields to use for reranking. These fields should
1283
+ contain the text that the reranking model will evaluate.
1284
+ """
1285
+ self._fields = fields
1286
+ return self
1287
+
1288
+ def with_(self, inference_id: str) -> "Rerank":
1289
+ """Continuation of the ``RERANK`` command.
1290
+
1291
+ :param inference_id: The ID of the inference endpoint to use for the task. The
1292
+ inference endpoint must be configured with the ``rerank``
1293
+ task type.
1294
+ """
1295
+ self._inference_id = inference_id
1296
+ return self
1297
+
1298
+ def _render_internal(self) -> str:
1299
+ if self._fields is None:
1300
+ raise ValueError(
1301
+ "The rerank command requires one or more fields to rerank on"
1302
+ )
1303
+ if self._inference_id is None:
1304
+ raise ValueError("The completion command requires an inference ID")
1305
+ with_ = {"inference_id": self._inference_id}
1306
+ if self._named_query:
1307
+ column = list(self._named_query.keys())[0]
1308
+ query = list(self._named_query.values())[0]
1309
+ query = f"{self._format_id(column)} = {json.dumps(query)}"
1310
+ else:
1311
+ query = json.dumps(self._query[0])
1312
+ return (
1313
+ f"RERANK {query} "
1314
+ f"ON {', '.join([self._format_id(field) for field in self._fields])} "
1315
+ f"WITH {json.dumps(with_)}"
1316
+ )
1317
+
1318
+
1049
1319
  class Sample(ESQLBase):
1050
1320
  """Implementation of the ``SAMPLE`` processing command.
1051
1321
 
@@ -1090,6 +1360,8 @@ class Stats(ESQLBase):
1090
1360
  in a single expression.
1091
1361
  """
1092
1362
 
1363
+ command_name = "STATS"
1364
+
1093
1365
  def __init__(
1094
1366
  self,
1095
1367
  parent: ESQLBase,
@@ -1105,6 +1377,12 @@ class Stats(ESQLBase):
1105
1377
  self._grouping_expressions: Optional[Tuple[ExpressionType, ...]] = None
1106
1378
 
1107
1379
  def by(self, *grouping_expressions: ExpressionType) -> "Stats":
1380
+ """Continuation of the ``STATS`` and ``INLINE STATS`` commands.
1381
+
1382
+ :param grouping_expressions: Expressions that output the values to group by.
1383
+ If their names coincide with one of the computed
1384
+ columns, that column will be ignored.
1385
+ """
1108
1386
  self._grouping_expressions = grouping_expressions
1109
1387
  return self
1110
1388
 
@@ -1116,13 +1394,25 @@ class Stats(ESQLBase):
1116
1394
  ]
1117
1395
  else:
1118
1396
  exprs = [f"{self._format_expr(expr)}" for expr in self._expressions]
1119
- expression_separator = ",\n "
1397
+ indent = " " * (len(self.command_name) + 3)
1398
+ expression_separator = f",\n{indent}"
1120
1399
  by = (
1121
1400
  ""
1122
1401
  if self._grouping_expressions is None
1123
- else f'\n BY {", ".join([f"{self._format_expr(expr)}" for expr in self._grouping_expressions])}'
1402
+ else f'\n{indent}BY {", ".join([f"{self._format_expr(expr)}" for expr in self._grouping_expressions])}'
1124
1403
  )
1125
- return f'STATS {expression_separator.join([f"{expr}" for expr in exprs])}{by}'
1404
+ return f'{self.command_name} {expression_separator.join([f"{expr}" for expr in exprs])}{by}'
1405
+
1406
+
1407
+ class InlineStats(Stats):
1408
+ """Implementation of the ``INLINE STATS`` processing command.
1409
+
1410
+ This class inherits from :class:`ESQLBase <elasticsearch.esql.esql.ESQLBase>`,
1411
+ to make it possible to chain all the commands that belong to an ES|QL query
1412
+ in a single expression.
1413
+ """
1414
+
1415
+ command_name = "INLINE STATS"
1126
1416
 
1127
1417
 
1128
1418
  class Where(ESQLBase):