elasticsearch 8.17.1__py3-none-any.whl → 9.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. elasticsearch/__init__.py +2 -2
  2. elasticsearch/_async/client/__init__.py +2125 -1053
  3. elasticsearch/_async/client/_base.py +1 -2
  4. elasticsearch/_async/client/async_search.py +46 -35
  5. elasticsearch/_async/client/autoscaling.py +32 -26
  6. elasticsearch/_async/client/cat.py +244 -176
  7. elasticsearch/_async/client/ccr.py +268 -128
  8. elasticsearch/_async/client/cluster.py +191 -164
  9. elasticsearch/_async/client/connector.py +226 -116
  10. elasticsearch/_async/client/dangling_indices.py +22 -16
  11. elasticsearch/_async/client/enrich.py +51 -11
  12. elasticsearch/_async/client/eql.py +54 -13
  13. elasticsearch/_async/client/esql.py +351 -7
  14. elasticsearch/_async/client/features.py +37 -27
  15. elasticsearch/_async/client/fleet.py +32 -22
  16. elasticsearch/_async/client/graph.py +10 -9
  17. elasticsearch/_async/client/ilm.py +115 -77
  18. elasticsearch/_async/client/indices.py +1119 -772
  19. elasticsearch/_async/client/inference.py +1933 -84
  20. elasticsearch/_async/client/ingest.py +83 -50
  21. elasticsearch/_async/client/license.py +90 -38
  22. elasticsearch/_async/client/logstash.py +20 -9
  23. elasticsearch/_async/client/migration.py +26 -17
  24. elasticsearch/_async/client/ml.py +646 -374
  25. elasticsearch/_async/client/monitoring.py +6 -3
  26. elasticsearch/_async/client/nodes.py +52 -54
  27. elasticsearch/_async/client/query_rules.py +59 -33
  28. elasticsearch/_async/client/rollup.py +124 -86
  29. elasticsearch/_async/client/search_application.py +60 -32
  30. elasticsearch/_async/client/searchable_snapshots.py +25 -12
  31. elasticsearch/_async/client/security.py +903 -562
  32. elasticsearch/_async/client/shutdown.py +34 -36
  33. elasticsearch/_async/client/simulate.py +22 -28
  34. elasticsearch/_async/client/slm.py +65 -40
  35. elasticsearch/_async/client/snapshot.py +454 -327
  36. elasticsearch/_async/client/sql.py +43 -22
  37. elasticsearch/_async/client/ssl.py +17 -18
  38. elasticsearch/_async/client/synonyms.py +58 -37
  39. elasticsearch/_async/client/tasks.py +77 -48
  40. elasticsearch/_async/client/text_structure.py +65 -56
  41. elasticsearch/_async/client/transform.py +124 -93
  42. elasticsearch/_async/client/watcher.py +117 -73
  43. elasticsearch/_async/client/xpack.py +18 -9
  44. elasticsearch/_async/helpers.py +1 -2
  45. elasticsearch/_sync/client/__init__.py +2125 -1053
  46. elasticsearch/_sync/client/_base.py +1 -2
  47. elasticsearch/_sync/client/async_search.py +46 -35
  48. elasticsearch/_sync/client/autoscaling.py +32 -26
  49. elasticsearch/_sync/client/cat.py +244 -176
  50. elasticsearch/_sync/client/ccr.py +268 -128
  51. elasticsearch/_sync/client/cluster.py +191 -164
  52. elasticsearch/_sync/client/connector.py +226 -116
  53. elasticsearch/_sync/client/dangling_indices.py +22 -16
  54. elasticsearch/_sync/client/enrich.py +51 -11
  55. elasticsearch/_sync/client/eql.py +54 -13
  56. elasticsearch/_sync/client/esql.py +351 -7
  57. elasticsearch/_sync/client/features.py +37 -27
  58. elasticsearch/_sync/client/fleet.py +32 -22
  59. elasticsearch/_sync/client/graph.py +10 -9
  60. elasticsearch/_sync/client/ilm.py +115 -77
  61. elasticsearch/_sync/client/indices.py +1119 -772
  62. elasticsearch/_sync/client/inference.py +1933 -84
  63. elasticsearch/_sync/client/ingest.py +83 -50
  64. elasticsearch/_sync/client/license.py +90 -38
  65. elasticsearch/_sync/client/logstash.py +20 -9
  66. elasticsearch/_sync/client/migration.py +26 -17
  67. elasticsearch/_sync/client/ml.py +646 -374
  68. elasticsearch/_sync/client/monitoring.py +6 -3
  69. elasticsearch/_sync/client/nodes.py +52 -54
  70. elasticsearch/_sync/client/query_rules.py +59 -33
  71. elasticsearch/_sync/client/rollup.py +124 -86
  72. elasticsearch/_sync/client/search_application.py +60 -32
  73. elasticsearch/_sync/client/searchable_snapshots.py +25 -12
  74. elasticsearch/_sync/client/security.py +903 -562
  75. elasticsearch/_sync/client/shutdown.py +34 -36
  76. elasticsearch/_sync/client/simulate.py +22 -28
  77. elasticsearch/_sync/client/slm.py +65 -40
  78. elasticsearch/_sync/client/snapshot.py +454 -327
  79. elasticsearch/_sync/client/sql.py +43 -22
  80. elasticsearch/_sync/client/ssl.py +17 -18
  81. elasticsearch/_sync/client/synonyms.py +58 -37
  82. elasticsearch/_sync/client/tasks.py +77 -48
  83. elasticsearch/_sync/client/text_structure.py +65 -56
  84. elasticsearch/_sync/client/transform.py +124 -93
  85. elasticsearch/_sync/client/utils.py +1 -41
  86. elasticsearch/_sync/client/watcher.py +117 -73
  87. elasticsearch/_sync/client/xpack.py +18 -9
  88. elasticsearch/_version.py +1 -1
  89. elasticsearch/client.py +2 -0
  90. elasticsearch/dsl/__init__.py +203 -0
  91. elasticsearch/dsl/_async/__init__.py +16 -0
  92. elasticsearch/dsl/_async/document.py +522 -0
  93. elasticsearch/dsl/_async/faceted_search.py +50 -0
  94. elasticsearch/dsl/_async/index.py +639 -0
  95. elasticsearch/dsl/_async/mapping.py +49 -0
  96. elasticsearch/dsl/_async/search.py +237 -0
  97. elasticsearch/dsl/_async/update_by_query.py +47 -0
  98. elasticsearch/dsl/_sync/__init__.py +16 -0
  99. elasticsearch/dsl/_sync/document.py +514 -0
  100. elasticsearch/dsl/_sync/faceted_search.py +50 -0
  101. elasticsearch/dsl/_sync/index.py +597 -0
  102. elasticsearch/dsl/_sync/mapping.py +49 -0
  103. elasticsearch/dsl/_sync/search.py +230 -0
  104. elasticsearch/dsl/_sync/update_by_query.py +45 -0
  105. elasticsearch/dsl/aggs.py +3734 -0
  106. elasticsearch/dsl/analysis.py +341 -0
  107. elasticsearch/dsl/async_connections.py +37 -0
  108. elasticsearch/dsl/connections.py +142 -0
  109. elasticsearch/dsl/document.py +20 -0
  110. elasticsearch/dsl/document_base.py +444 -0
  111. elasticsearch/dsl/exceptions.py +32 -0
  112. elasticsearch/dsl/faceted_search.py +28 -0
  113. elasticsearch/dsl/faceted_search_base.py +489 -0
  114. elasticsearch/dsl/field.py +4392 -0
  115. elasticsearch/dsl/function.py +180 -0
  116. elasticsearch/dsl/index.py +23 -0
  117. elasticsearch/dsl/index_base.py +178 -0
  118. elasticsearch/dsl/mapping.py +19 -0
  119. elasticsearch/dsl/mapping_base.py +219 -0
  120. elasticsearch/dsl/query.py +2822 -0
  121. elasticsearch/dsl/response/__init__.py +388 -0
  122. elasticsearch/dsl/response/aggs.py +100 -0
  123. elasticsearch/dsl/response/hit.py +53 -0
  124. elasticsearch/dsl/search.py +20 -0
  125. elasticsearch/dsl/search_base.py +1053 -0
  126. elasticsearch/dsl/serializer.py +34 -0
  127. elasticsearch/dsl/types.py +6453 -0
  128. elasticsearch/dsl/update_by_query.py +19 -0
  129. elasticsearch/dsl/update_by_query_base.py +149 -0
  130. elasticsearch/dsl/utils.py +687 -0
  131. elasticsearch/dsl/wrappers.py +144 -0
  132. elasticsearch/helpers/actions.py +1 -1
  133. elasticsearch/helpers/vectorstore/_async/strategies.py +12 -12
  134. elasticsearch/helpers/vectorstore/_sync/strategies.py +12 -12
  135. elasticsearch/helpers/vectorstore/_sync/vectorstore.py +4 -1
  136. {elasticsearch-8.17.1.dist-info → elasticsearch-9.0.0.dist-info}/METADATA +12 -15
  137. elasticsearch-9.0.0.dist-info/RECORD +160 -0
  138. elasticsearch/transport.py +0 -57
  139. elasticsearch-8.17.1.dist-info/RECORD +0 -119
  140. {elasticsearch-8.17.1.dist-info → elasticsearch-9.0.0.dist-info}/WHEEL +0 -0
  141. {elasticsearch-8.17.1.dist-info → elasticsearch-9.0.0.dist-info}/licenses/LICENSE +0 -0
  142. {elasticsearch-8.17.1.dist-info → elasticsearch-9.0.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,489 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ from datetime import datetime, timedelta
19
+ from typing import (
20
+ TYPE_CHECKING,
21
+ Any,
22
+ Dict,
23
+ Generic,
24
+ List,
25
+ Optional,
26
+ Sequence,
27
+ Tuple,
28
+ Type,
29
+ Union,
30
+ cast,
31
+ )
32
+
33
+ from typing_extensions import Self
34
+
35
+ from .aggs import A, Agg
36
+ from .query import MatchAll, Nested, Query, Range, Terms
37
+ from .response import Response
38
+ from .utils import _R, AttrDict
39
+
40
+ if TYPE_CHECKING:
41
+ from .document_base import DocumentBase
42
+ from .response.aggs import BucketData
43
+ from .search_base import SearchBase
44
+
45
+ FilterValueType = Union[str, int, float, bool]
46
+
47
+ __all__ = [
48
+ "FacetedSearchBase",
49
+ "HistogramFacet",
50
+ "TermsFacet",
51
+ "DateHistogramFacet",
52
+ "RangeFacet",
53
+ "NestedFacet",
54
+ ]
55
+
56
+
57
+ class Facet(Generic[_R]):
58
+ """
59
+ A facet on faceted search. Wraps and aggregation and provides functionality
60
+ to create a filter for selected values and return a list of facet values
61
+ from the result of the aggregation.
62
+ """
63
+
64
+ agg_type: str = ""
65
+
66
+ def __init__(
67
+ self, metric: Optional[Agg[_R]] = None, metric_sort: str = "desc", **kwargs: Any
68
+ ):
69
+ self.filter_values = ()
70
+ self._params = kwargs
71
+ self._metric = metric
72
+ if metric and metric_sort:
73
+ self._params["order"] = {"metric": metric_sort}
74
+
75
+ def get_aggregation(self) -> Agg[_R]:
76
+ """
77
+ Return the aggregation object.
78
+ """
79
+ agg: Agg[_R] = A(self.agg_type, **self._params)
80
+ if self._metric:
81
+ agg.metric("metric", self._metric)
82
+ return agg
83
+
84
+ def add_filter(self, filter_values: List[FilterValueType]) -> Optional[Query]:
85
+ """
86
+ Construct a filter.
87
+ """
88
+ if not filter_values:
89
+ return None
90
+
91
+ f = self.get_value_filter(filter_values[0])
92
+ for v in filter_values[1:]:
93
+ f |= self.get_value_filter(v)
94
+ return f
95
+
96
+ def get_value_filter(self, filter_value: FilterValueType) -> Query: # type: ignore[empty-body]
97
+ """
98
+ Construct a filter for an individual value
99
+ """
100
+ pass
101
+
102
+ def is_filtered(self, key: str, filter_values: List[FilterValueType]) -> bool:
103
+ """
104
+ Is a filter active on the given key.
105
+ """
106
+ return key in filter_values
107
+
108
+ def get_value(self, bucket: "BucketData[_R]") -> Any:
109
+ """
110
+ return a value representing a bucket. Its key as default.
111
+ """
112
+ return bucket["key"]
113
+
114
+ def get_metric(self, bucket: "BucketData[_R]") -> int:
115
+ """
116
+ Return a metric, by default doc_count for a bucket.
117
+ """
118
+ if self._metric:
119
+ return cast(int, bucket["metric"]["value"])
120
+ return cast(int, bucket["doc_count"])
121
+
122
+ def get_values(
123
+ self, data: "BucketData[_R]", filter_values: List[FilterValueType]
124
+ ) -> List[Tuple[Any, int, bool]]:
125
+ """
126
+ Turn the raw bucket data into a list of tuples containing the key,
127
+ number of documents and a flag indicating whether this value has been
128
+ selected or not.
129
+ """
130
+ out = []
131
+ for bucket in data.buckets:
132
+ b = cast("BucketData[_R]", bucket)
133
+ key = self.get_value(b)
134
+ out.append((key, self.get_metric(b), self.is_filtered(key, filter_values)))
135
+ return out
136
+
137
+
138
+ class TermsFacet(Facet[_R]):
139
+ agg_type = "terms"
140
+
141
+ def add_filter(self, filter_values: List[FilterValueType]) -> Optional[Query]:
142
+ """Create a terms filter instead of bool containing term filters."""
143
+ if filter_values:
144
+ return Terms(self._params["field"], filter_values, _expand__to_dot=False)
145
+ return None
146
+
147
+
148
+ class RangeFacet(Facet[_R]):
149
+ agg_type = "range"
150
+
151
+ def _range_to_dict(
152
+ self, range: Tuple[Any, Tuple[Optional[int], Optional[int]]]
153
+ ) -> Dict[str, Any]:
154
+ key, _range = range
155
+ out: Dict[str, Any] = {"key": key}
156
+ if _range[0] is not None:
157
+ out["from"] = _range[0]
158
+ if _range[1] is not None:
159
+ out["to"] = _range[1]
160
+ return out
161
+
162
+ def __init__(
163
+ self,
164
+ ranges: Sequence[Tuple[Any, Tuple[Optional[int], Optional[int]]]],
165
+ **kwargs: Any,
166
+ ):
167
+ super().__init__(**kwargs)
168
+ self._params["ranges"] = list(map(self._range_to_dict, ranges))
169
+ self._params["keyed"] = False
170
+ self._ranges = dict(ranges)
171
+
172
+ def get_value_filter(self, filter_value: FilterValueType) -> Query:
173
+ f, t = self._ranges[filter_value]
174
+ limits: Dict[str, Any] = {}
175
+ if f is not None:
176
+ limits["gte"] = f
177
+ if t is not None:
178
+ limits["lt"] = t
179
+
180
+ return Range(self._params["field"], limits, _expand__to_dot=False)
181
+
182
+
183
+ class HistogramFacet(Facet[_R]):
184
+ agg_type = "histogram"
185
+
186
+ def get_value_filter(self, filter_value: FilterValueType) -> Range:
187
+ return Range(
188
+ self._params["field"],
189
+ {
190
+ "gte": filter_value,
191
+ "lt": filter_value + self._params["interval"],
192
+ },
193
+ _expand__to_dot=False,
194
+ )
195
+
196
+
197
+ def _date_interval_year(d: datetime) -> datetime:
198
+ return d.replace(
199
+ year=d.year + 1, day=(28 if d.month == 2 and d.day == 29 else d.day)
200
+ )
201
+
202
+
203
+ def _date_interval_month(d: datetime) -> datetime:
204
+ return (d + timedelta(days=32)).replace(day=1)
205
+
206
+
207
+ def _date_interval_week(d: datetime) -> datetime:
208
+ return d + timedelta(days=7)
209
+
210
+
211
+ def _date_interval_day(d: datetime) -> datetime:
212
+ return d + timedelta(days=1)
213
+
214
+
215
+ def _date_interval_hour(d: datetime) -> datetime:
216
+ return d + timedelta(hours=1)
217
+
218
+
219
+ class DateHistogramFacet(Facet[_R]):
220
+ agg_type = "date_histogram"
221
+
222
+ DATE_INTERVALS = {
223
+ "year": _date_interval_year,
224
+ "1Y": _date_interval_year,
225
+ "month": _date_interval_month,
226
+ "1M": _date_interval_month,
227
+ "week": _date_interval_week,
228
+ "1w": _date_interval_week,
229
+ "day": _date_interval_day,
230
+ "1d": _date_interval_day,
231
+ "hour": _date_interval_hour,
232
+ "1h": _date_interval_hour,
233
+ }
234
+
235
+ def __init__(self, **kwargs: Any):
236
+ kwargs.setdefault("min_doc_count", 0)
237
+ super().__init__(**kwargs)
238
+
239
+ def get_value(self, bucket: "BucketData[_R]") -> Any:
240
+ if not isinstance(bucket["key"], datetime):
241
+ # Elasticsearch returns key=None instead of 0 for date 1970-01-01,
242
+ # so we need to set key to 0 to avoid TypeError exception
243
+ if bucket["key"] is None:
244
+ bucket["key"] = 0
245
+ # Preserve milliseconds in the datetime
246
+ return datetime.utcfromtimestamp(int(cast(int, bucket["key"])) / 1000.0)
247
+ else:
248
+ return bucket["key"]
249
+
250
+ def get_value_filter(self, filter_value: Any) -> Range:
251
+ for interval_type in ("calendar_interval", "fixed_interval"):
252
+ if interval_type in self._params:
253
+ break
254
+ else:
255
+ interval_type = "interval"
256
+
257
+ return Range(
258
+ self._params["field"],
259
+ {
260
+ "gte": filter_value,
261
+ "lt": self.DATE_INTERVALS[self._params[interval_type]](filter_value),
262
+ },
263
+ _expand__to_dot=False,
264
+ )
265
+
266
+
267
+ class NestedFacet(Facet[_R]):
268
+ agg_type = "nested"
269
+
270
+ def __init__(self, path: str, nested_facet: Facet[_R]):
271
+ self._path = path
272
+ self._inner = nested_facet
273
+ super().__init__(path=path, aggs={"inner": nested_facet.get_aggregation()})
274
+
275
+ def get_values(
276
+ self, data: "BucketData[_R]", filter_values: List[FilterValueType]
277
+ ) -> List[Tuple[Any, int, bool]]:
278
+ return self._inner.get_values(data.inner, filter_values)
279
+
280
+ def add_filter(self, filter_values: List[FilterValueType]) -> Optional[Query]:
281
+ inner_q = self._inner.add_filter(filter_values)
282
+ if inner_q:
283
+ return Nested(path=self._path, query=inner_q)
284
+ return None
285
+
286
+
287
+ class FacetedResponse(Response[_R]):
288
+ if TYPE_CHECKING:
289
+ _faceted_search: "FacetedSearchBase[_R]"
290
+ _facets: Dict[str, List[Tuple[Any, int, bool]]]
291
+
292
+ @property
293
+ def query_string(self) -> Optional[Union[str, Query]]:
294
+ return self._faceted_search._query
295
+
296
+ @property
297
+ def facets(self) -> Dict[str, List[Tuple[Any, int, bool]]]:
298
+ if not hasattr(self, "_facets"):
299
+ super(AttrDict, self).__setattr__("_facets", AttrDict({}))
300
+ for name, facet in self._faceted_search.facets.items():
301
+ self._facets[name] = facet.get_values(
302
+ getattr(getattr(self.aggregations, "_filter_" + name), name),
303
+ self._faceted_search.filter_values.get(name, []),
304
+ )
305
+ return self._facets
306
+
307
+
308
+ class FacetedSearchBase(Generic[_R]):
309
+ """
310
+ Abstraction for creating faceted navigation searches that takes care of
311
+ composing the queries, aggregations and filters as needed as well as
312
+ presenting the results in an easy-to-consume fashion::
313
+
314
+ class BlogSearch(FacetedSearch):
315
+ index = 'blogs'
316
+ doc_types = [Blog, Post]
317
+ fields = ['title^5', 'category', 'description', 'body']
318
+
319
+ facets = {
320
+ 'type': TermsFacet(field='_type'),
321
+ 'category': TermsFacet(field='category'),
322
+ 'weekly_posts': DateHistogramFacet(field='published_from', interval='week')
323
+ }
324
+
325
+ def search(self):
326
+ ' Override search to add your own filters '
327
+ s = super(BlogSearch, self).search()
328
+ return s.filter('term', published=True)
329
+
330
+ # when using:
331
+ blog_search = BlogSearch("web framework", filters={"category": "python"})
332
+
333
+ # supports pagination
334
+ blog_search[10:20]
335
+
336
+ response = blog_search.execute()
337
+
338
+ # easy access to aggregation results:
339
+ for category, hit_count, is_selected in response.facets.category:
340
+ print(
341
+ "Category %s has %d hits%s." % (
342
+ category,
343
+ hit_count,
344
+ ' and is chosen' if is_selected else ''
345
+ )
346
+ )
347
+
348
+ """
349
+
350
+ index: Optional[str] = None
351
+ doc_types: Optional[List[Union[str, Type["DocumentBase"]]]] = None
352
+ fields: Sequence[str] = []
353
+ facets: Dict[str, Facet[_R]] = {}
354
+ using = "default"
355
+
356
+ if TYPE_CHECKING:
357
+
358
+ def search(self) -> "SearchBase[_R]": ...
359
+
360
+ def __init__(
361
+ self,
362
+ query: Optional[Union[str, Query]] = None,
363
+ filters: Dict[str, FilterValueType] = {},
364
+ sort: Sequence[str] = [],
365
+ ):
366
+ """
367
+ :arg query: the text to search for
368
+ :arg filters: facet values to filter
369
+ :arg sort: sort information to be passed to :class:`~elasticsearch.dsl.Search`
370
+ """
371
+ self._query = query
372
+ self._filters: Dict[str, Query] = {}
373
+ self._sort = sort
374
+ self.filter_values: Dict[str, List[FilterValueType]] = {}
375
+ for name, value in filters.items():
376
+ self.add_filter(name, value)
377
+
378
+ self._s = self.build_search()
379
+
380
+ def __getitem__(self, k: Union[int, slice]) -> Self:
381
+ self._s = self._s[k]
382
+ return self
383
+
384
+ def add_filter(
385
+ self, name: str, filter_values: Union[FilterValueType, List[FilterValueType]]
386
+ ) -> None:
387
+ """
388
+ Add a filter for a facet.
389
+ """
390
+ # normalize the value into a list
391
+ if not isinstance(filter_values, (tuple, list)):
392
+ if filter_values is None:
393
+ return
394
+ filter_values = [
395
+ filter_values,
396
+ ]
397
+
398
+ # remember the filter values for use in FacetedResponse
399
+ self.filter_values[name] = filter_values
400
+
401
+ # get the filter from the facet
402
+ f = self.facets[name].add_filter(filter_values)
403
+ if f is None:
404
+ return
405
+
406
+ self._filters[name] = f
407
+
408
+ def query(
409
+ self, search: "SearchBase[_R]", query: Union[str, Query]
410
+ ) -> "SearchBase[_R]":
411
+ """
412
+ Add query part to ``search``.
413
+
414
+ Override this if you wish to customize the query used.
415
+ """
416
+ if query:
417
+ if self.fields:
418
+ return search.query("multi_match", fields=self.fields, query=query)
419
+ else:
420
+ return search.query("multi_match", query=query)
421
+ return search
422
+
423
+ def aggregate(self, search: "SearchBase[_R]") -> None:
424
+ """
425
+ Add aggregations representing the facets selected, including potential
426
+ filters.
427
+ """
428
+ for f, facet in self.facets.items():
429
+ agg = facet.get_aggregation()
430
+ agg_filter: Query = MatchAll()
431
+ for field, filter in self._filters.items():
432
+ if f == field:
433
+ continue
434
+ agg_filter &= filter
435
+ search.aggs.bucket("_filter_" + f, "filter", filter=agg_filter).bucket(
436
+ f, agg
437
+ )
438
+
439
+ def filter(self, search: "SearchBase[_R]") -> "SearchBase[_R]":
440
+ """
441
+ Add a ``post_filter`` to the search request narrowing the results based
442
+ on the facet filters.
443
+ """
444
+ if not self._filters:
445
+ return search
446
+
447
+ post_filter: Query = MatchAll()
448
+ for f in self._filters.values():
449
+ post_filter &= f
450
+ return search.post_filter(post_filter)
451
+
452
+ def highlight(self, search: "SearchBase[_R]") -> "SearchBase[_R]":
453
+ """
454
+ Add highlighting for all the fields
455
+ """
456
+ return search.highlight(
457
+ *(f if "^" not in f else f.split("^", 1)[0] for f in self.fields)
458
+ )
459
+
460
+ def sort(self, search: "SearchBase[_R]") -> "SearchBase[_R]":
461
+ """
462
+ Add sorting information to the request.
463
+ """
464
+ if self._sort:
465
+ search = search.sort(*self._sort)
466
+ return search
467
+
468
+ def params(self, **kwargs: Any) -> None:
469
+ """
470
+ Specify query params to be used when executing the search. All the
471
+ keyword arguments will override the current values. See
472
+ https://elasticsearch-py.readthedocs.io/en/latest/api/elasticsearch.html#elasticsearch.Elasticsearch.search
473
+ for all available parameters.
474
+ """
475
+ self._s = self._s.params(**kwargs)
476
+
477
+ def build_search(self) -> "SearchBase[_R]":
478
+ """
479
+ Construct the ``Search`` object.
480
+ """
481
+ s = self.search()
482
+ if self._query is not None:
483
+ s = self.query(s, self._query)
484
+ s = self.filter(s)
485
+ if self.fields:
486
+ s = self.highlight(s)
487
+ s = self.sort(s)
488
+ self.aggregate(s)
489
+ return s