eodag 3.9.1__py3-none-any.whl → 4.0.0a1__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 (71) hide show
  1. eodag/api/core.py +378 -419
  2. eodag/api/product/__init__.py +3 -3
  3. eodag/api/product/_product.py +68 -40
  4. eodag/api/product/drivers/__init__.py +3 -5
  5. eodag/api/product/drivers/base.py +1 -18
  6. eodag/api/product/metadata_mapping.py +151 -215
  7. eodag/api/search_result.py +13 -7
  8. eodag/cli.py +72 -395
  9. eodag/config.py +46 -50
  10. eodag/plugins/apis/base.py +2 -2
  11. eodag/plugins/apis/ecmwf.py +20 -21
  12. eodag/plugins/apis/usgs.py +37 -33
  13. eodag/plugins/authentication/aws_auth.py +36 -1
  14. eodag/plugins/authentication/base.py +18 -3
  15. eodag/plugins/authentication/sas_auth.py +15 -0
  16. eodag/plugins/crunch/filter_date.py +3 -3
  17. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  18. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  19. eodag/plugins/download/aws.py +45 -41
  20. eodag/plugins/download/base.py +13 -14
  21. eodag/plugins/download/http.py +65 -65
  22. eodag/plugins/manager.py +28 -29
  23. eodag/plugins/search/__init__.py +3 -4
  24. eodag/plugins/search/base.py +128 -77
  25. eodag/plugins/search/build_search_result.py +105 -107
  26. eodag/plugins/search/cop_marine.py +44 -47
  27. eodag/plugins/search/csw.py +33 -33
  28. eodag/plugins/search/qssearch.py +335 -354
  29. eodag/plugins/search/stac_list_assets.py +1 -1
  30. eodag/plugins/search/static_stac_search.py +31 -31
  31. eodag/resources/{product_types.yml → collections.yml} +2353 -2429
  32. eodag/resources/ext_collections.json +1 -0
  33. eodag/resources/ext_product_types.json +1 -1
  34. eodag/resources/providers.yml +2432 -2714
  35. eodag/resources/stac_provider.yml +46 -90
  36. eodag/types/queryables.py +55 -91
  37. eodag/types/search_args.py +1 -1
  38. eodag/utils/__init__.py +94 -21
  39. eodag/utils/exceptions.py +6 -6
  40. eodag/utils/free_text_search.py +3 -3
  41. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/METADATA +11 -88
  42. eodag-4.0.0a1.dist-info/RECORD +92 -0
  43. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
  44. eodag/plugins/authentication/oauth.py +0 -60
  45. eodag/plugins/download/creodias_s3.py +0 -64
  46. eodag/plugins/download/s3rest.py +0 -351
  47. eodag/plugins/search/data_request_search.py +0 -565
  48. eodag/resources/stac.yml +0 -294
  49. eodag/resources/stac_api.yml +0 -2105
  50. eodag/rest/__init__.py +0 -24
  51. eodag/rest/cache.py +0 -70
  52. eodag/rest/config.py +0 -67
  53. eodag/rest/constants.py +0 -26
  54. eodag/rest/core.py +0 -764
  55. eodag/rest/errors.py +0 -210
  56. eodag/rest/server.py +0 -604
  57. eodag/rest/server.wsgi +0 -6
  58. eodag/rest/stac.py +0 -1032
  59. eodag/rest/templates/README +0 -1
  60. eodag/rest/types/__init__.py +0 -18
  61. eodag/rest/types/collections_search.py +0 -44
  62. eodag/rest/types/eodag_search.py +0 -386
  63. eodag/rest/types/queryables.py +0 -174
  64. eodag/rest/types/stac_search.py +0 -272
  65. eodag/rest/utils/__init__.py +0 -207
  66. eodag/rest/utils/cql_evaluate.py +0 -119
  67. eodag/rest/utils/rfc3339.py +0 -64
  68. eodag-3.9.1.dist-info/RECORD +0 -115
  69. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
  70. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
  71. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/top_level.txt +0 -0
@@ -38,7 +38,7 @@ from eodag.config import PluginConfig
38
38
  from eodag.plugins.search import PreparedSearch
39
39
  from eodag.plugins.search.static_stac_search import StaticStacSearch
40
40
  from eodag.utils import get_bucket_name_and_prefix, get_geometry_from_various
41
- from eodag.utils.exceptions import RequestError, UnsupportedProductType, ValidationError
41
+ from eodag.utils.exceptions import RequestError, UnsupportedCollection, ValidationError
42
42
 
43
43
  if TYPE_CHECKING:
44
44
  from mypy_boto3_s3 import S3Client
@@ -112,7 +112,7 @@ def _check_int_values_properties(properties: dict[str, Any]):
112
112
  class CopMarineSearch(StaticStacSearch):
113
113
  """class that implements search for the Copernicus Marine provider
114
114
 
115
- It calls :meth:`~eodag.plugins.search.static_stac_search.StaticStacSearch.discover_product_types`
115
+ It calls :meth:`~eodag.plugins.search.static_stac_search.StaticStacSearch.discover_collections`
116
116
  inherited from :class:`~eodag.plugins.search.static_stac_search.StaticStacSearch`
117
117
  but for the actual search a special method which fetches the urls of the available products from an S3 storage and
118
118
  filters them has been written.
@@ -132,40 +132,40 @@ class CopMarineSearch(StaticStacSearch):
132
132
  # reset to original metadata mapping from config (changed in super class init)
133
133
  self.config.metadata_mapping = original_metadata_mapping
134
134
 
135
- def _get_product_type_info(
136
- self, product_type: str
135
+ def _get_collection_info(
136
+ self, collection: str
137
137
  ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
138
- """Fetch product type and associated datasets info"""
138
+ """Fetch collection and associated datasets info"""
139
139
 
140
- fetch_url = cast(str, self.config.discover_product_types["fetch_url"]).format(
140
+ fetch_url = cast(str, self.config.discover_collections["fetch_url"]).format(
141
141
  **self.config.__dict__
142
142
  )
143
143
 
144
- logger.debug("fetch data for collection %s", product_type)
145
- provider_product_type = self.config.products.get(product_type, {}).get(
146
- "productType", None
144
+ logger.debug("fetch data for collection %s", collection)
145
+ provider_collection = self.config.products.get(collection, {}).get(
146
+ "_collection", None
147
147
  )
148
- if not provider_product_type:
149
- provider_product_type = product_type
148
+ if not provider_collection:
149
+ provider_collection = collection
150
150
  collection_url = (
151
- fetch_url.replace("catalog.stac.json", provider_product_type)
151
+ fetch_url.replace("catalog.stac.json", provider_collection)
152
152
  + "/product.stac.json"
153
153
  )
154
154
  try:
155
155
  collection_data = requests.get(collection_url).json()
156
156
  except requests.RequestException as exc:
157
157
  if exc.errno == 404:
158
- logger.error("product %s not found", product_type)
159
- raise UnsupportedProductType(product_type)
160
- logger.error("data for product %s could not be fetched", product_type)
158
+ logger.error("product %s not found", collection)
159
+ raise UnsupportedCollection(collection)
160
+ logger.error("data for product %s could not be fetched", collection)
161
161
  raise RequestError.from_error(
162
- exc, f"data for product {product_type} could not be fetched"
162
+ exc, f"data for product {collection} could not be fetched"
163
163
  ) from exc
164
164
 
165
165
  datasets = []
166
166
  for link in [li for li in collection_data["links"] if li["rel"] == "item"]:
167
167
  dataset_url = (
168
- fetch_url.replace("catalog.stac.json", provider_product_type)
168
+ fetch_url.replace("catalog.stac.json", provider_collection)
169
169
  + "/"
170
170
  + link["href"]
171
171
  )
@@ -182,7 +182,7 @@ class CopMarineSearch(StaticStacSearch):
182
182
  collection_objects: ListObjectsOutputTypeDef,
183
183
  product_id: str,
184
184
  s3_url: str,
185
- product_type: str,
185
+ collection: str,
186
186
  dataset_item: dict[str, Any],
187
187
  collection_dict: dict[str, Any],
188
188
  ):
@@ -194,7 +194,7 @@ class CopMarineSearch(StaticStacSearch):
194
194
  for obj in collection_objects["Contents"]:
195
195
  if product_id in obj["Key"]:
196
196
  return self._create_product(
197
- product_type,
197
+ collection,
198
198
  obj["Key"],
199
199
  s3_url,
200
200
  dataset_item,
@@ -205,7 +205,7 @@ class CopMarineSearch(StaticStacSearch):
205
205
 
206
206
  def _create_product(
207
207
  self,
208
- product_type: str,
208
+ collection: str,
209
209
  item_key: str,
210
210
  s3_url: str,
211
211
  dataset_item: dict[str, Any],
@@ -217,21 +217,21 @@ class CopMarineSearch(StaticStacSearch):
217
217
  download_url = s3_url + "/" + item_key
218
218
  geometry = (
219
219
  get_geometry_from_various(**dataset_item)
220
- or self.config.metadata_mapping["defaultGeometry"]
220
+ or self.config.metadata_mapping["eodag:default_geometry"]
221
221
  )
222
222
  properties = {
223
223
  "id": item_id,
224
224
  "title": item_id,
225
225
  "geometry": geometry,
226
- "downloadLink": download_url,
226
+ "eodag:download_link": download_url,
227
227
  "dataset": dataset_item["id"],
228
228
  }
229
229
  if use_dataset_dates:
230
230
  dates = _get_dates_from_dataset_data(dataset_item)
231
231
  if not dates:
232
232
  return None
233
- properties["startTimeFromAscendingNode"] = dates["start"]
234
- properties["completionTimeFromAscendingNode"] = dates["end"]
233
+ properties["start_datetime"] = dates["start"]
234
+ properties["end_datetime"] = dates["end"]
235
235
  else:
236
236
  item_dates = re.findall(r"(\d{4})(0[1-9]|1[0-2])([0-3]\d)", item_id)
237
237
  if not item_dates:
@@ -244,12 +244,10 @@ class CopMarineSearch(StaticStacSearch):
244
244
  item_end = _get_date_from_yyyymmdd(item_dates[1], item_key)
245
245
  else: # only date and created_at timestamps
246
246
  item_end = item_start
247
- properties["startTimeFromAscendingNode"] = item_start.strftime(
247
+ properties["start_datetime"] = item_start.strftime("%Y-%m-%dT%H:%M:%SZ")
248
+ properties["end_datetime"] = (item_end or item_start).strftime(
248
249
  "%Y-%m-%dT%H:%M:%SZ"
249
250
  )
250
- properties["completionTimeFromAscendingNode"] = (
251
- item_end or item_start
252
- ).strftime("%Y-%m-%dT%H:%M:%SZ")
253
251
 
254
252
  for key, value in collection_dict["properties"].items():
255
253
  if key not in ["id", "title", "start_datetime", "end_datetime", "datetime"]:
@@ -258,7 +256,7 @@ class CopMarineSearch(StaticStacSearch):
258
256
  if key not in ["id", "title", "start_datetime", "end_datetime", "datetime"]:
259
257
  properties[key] = value
260
258
 
261
- code_mapping = self.config.products.get(product_type, {}).get(
259
+ code_mapping = self.config.products.get(collection, {}).get(
262
260
  "code_mapping", None
263
261
  )
264
262
  if code_mapping:
@@ -274,9 +272,11 @@ class CopMarineSearch(StaticStacSearch):
274
272
 
275
273
  _check_int_values_properties(properties)
276
274
 
277
- properties["thumbnail"] = collection_dict["assets"]["thumbnail"]["href"]
275
+ properties["eodag:thumbnail"] = collection_dict["assets"]["thumbnail"]["href"]
278
276
  if "omiFigure" in collection_dict["assets"]:
279
- properties["quicklook"] = collection_dict["assets"]["omiFigure"]["href"]
277
+ properties["eodag:quicklook"] = collection_dict["assets"]["omiFigure"][
278
+ "href"
279
+ ]
280
280
  assets = {
281
281
  "native": {
282
282
  "title": "native",
@@ -286,10 +286,7 @@ class CopMarineSearch(StaticStacSearch):
286
286
  }
287
287
  additional_assets = self.get_assets_from_mapping(dataset_item)
288
288
  assets.update(additional_assets)
289
- product = EOProduct(self.provider, properties, productType=product_type)
290
- # use product_type_config as default properties
291
- product_type_config = getattr(self.config, "product_type_config", {})
292
- product.properties = dict(product_type_config, **product.properties)
289
+ product = EOProduct(self.provider, properties, collection=collection)
293
290
  product.assets = AssetsDict(product, assets)
294
291
  return product
295
292
 
@@ -311,12 +308,12 @@ class CopMarineSearch(StaticStacSearch):
311
308
  if page is None or items_per_page is None or page > 1 and items_per_page <= 0:
312
309
  return ([], 0) if prep.count else ([], None)
313
310
 
314
- product_type = kwargs.get("productType", prep.product_type)
315
- if not product_type:
311
+ collection = kwargs.get("collection", prep.collection)
312
+ if not collection:
316
313
  raise ValidationError(
317
- "parameter product type is required for search with cop_marine provider"
314
+ "parameter collection is required for search with cop_marine provider"
318
315
  )
319
- collection_dict, datasets_items_list = self._get_product_type_info(product_type)
316
+ collection_dict, datasets_items_list = self._get_collection_info(collection)
320
317
  geometry = kwargs.pop("geometry", None)
321
318
  products: list[EOProduct] = []
322
319
  start_index = items_per_page * (page - 1) + 1
@@ -331,16 +328,16 @@ class CopMarineSearch(StaticStacSearch):
331
328
  logger.debug("searching data for dataset %s", dataset_item["id"])
332
329
 
333
330
  # date bounds
334
- if "startTimeFromAscendingNode" in kwargs:
335
- start_date = isoparse(kwargs["startTimeFromAscendingNode"])
331
+ if "start_datetime" in kwargs:
332
+ start_date = isoparse(kwargs["start_datetime"])
336
333
  elif "start_datetime" in dataset_item["properties"]:
337
334
  start_date = isoparse(dataset_item["properties"]["start_datetime"])
338
335
  else:
339
336
  start_date = isoparse(dataset_item["properties"]["datetime"])
340
337
  if not start_date.tzinfo:
341
338
  start_date = start_date.replace(tzinfo=tzutc())
342
- if "completionTimeFromAscendingNode" in kwargs:
343
- end_date = isoparse(kwargs["completionTimeFromAscendingNode"])
339
+ if "end_datetime" in kwargs:
340
+ end_date = isoparse(kwargs["end_datetime"])
344
341
  elif "end_datetime" in dataset_item["properties"]:
345
342
  end_date = isoparse(dataset_item["properties"]["end_datetime"])
346
343
  else:
@@ -352,7 +349,7 @@ class CopMarineSearch(StaticStacSearch):
352
349
  s3_url = dataset_item["assets"]["native"]["href"]
353
350
  except KeyError as e:
354
351
  logger.warning(
355
- f"Unable to extract info from {product_type} item #{i}: {str(e)}"
352
+ f"Unable to extract info from {collection} item #{i}: {str(e)}"
356
353
  )
357
354
  continue
358
355
 
@@ -371,7 +368,7 @@ class CopMarineSearch(StaticStacSearch):
371
368
  continue
372
369
  if len(products) < items_per_page or items_per_page < 0:
373
370
  product = self._create_product(
374
- product_type,
371
+ collection,
375
372
  collection_path,
376
373
  endpoint_url + "/" + bucket,
377
374
  dataset_item,
@@ -406,7 +403,7 @@ class CopMarineSearch(StaticStacSearch):
406
403
  s3_objects,
407
404
  kwargs["id"],
408
405
  endpoint_url + "/" + bucket,
409
- product_type,
406
+ collection,
410
407
  dataset_item,
411
408
  collection_dict,
412
409
  )
@@ -470,7 +467,7 @@ class CopMarineSearch(StaticStacSearch):
470
467
  continue
471
468
  if len(products) < items_per_page or items_per_page < 0:
472
469
  product = self._create_product(
473
- product_type,
470
+ collection,
474
471
  item_key,
475
472
  endpoint_url + "/" + bucket,
476
473
  dataset_item,
@@ -62,7 +62,7 @@ class CSWSearch(Search):
62
62
  * :attr:`~eodag.config.PluginConfig.version` (``str``): OGC Catalogue Service version; default: ``2.0.2``
63
63
  * :attr:`~eodag.config.PluginConfig.search_definition` (``dict[str, Any]``) (**mandatory**):
64
64
 
65
- * **product_type_tags** (``list[dict[str, Any]``): dict of product type tags
65
+ * **collection_tags** (``list[dict[str, Any]``): dict of collection tags
66
66
  * **resource_location_filter** (``str``): regex string
67
67
  * **date_tags** (``dict[str, Any]``): tags for start and end
68
68
 
@@ -74,15 +74,15 @@ class CSWSearch(Search):
74
74
  specification of Python string formatting, with a special behaviour added to it. For example,
75
75
  an entry in the metadata mapping of this kind::
76
76
 
77
- completionTimeFromAscendingNode:
78
- - 'f=acquisition.endViewingDate:lte:{completionTimeFromAscendingNode#timestamp}'
77
+ end_datetime:
78
+ - 'f=acquisition.endViewingDate:lte:{end_datetime#timestamp}'
79
79
  - '$.properties.acquisition.endViewingDate'
80
80
 
81
81
  means that the search url will have a query string parameter named ``f`` with a value of
82
82
  ``acquisition.endViewingDate:lte:1543922280.0`` if the search was done with the value
83
- of ``completionTimeFromAscendingNode`` being ``2018-12-04T12:18:00``. What happened is that
84
- ``{completionTimeFromAscendingNode#timestamp}`` was replaced with the timestamp of the value
85
- of ``completionTimeFromAscendingNode``. This example shows all there is to know about the
83
+ of ``end_datetime`` being ``2018-12-04T12:18:00``. What happened is that
84
+ ``{end_datetime#timestamp}`` was replaced with the timestamp of the value
85
+ of ``end_datetime``. This example shows all there is to know about the
86
86
  semantics of the query string formatting introduced by this plugin: any eodag search parameter
87
87
  can be referenced in the query string with an additional optional conversion function that
88
88
  is separated from it by a ``#`` (see :func:`~eodag.api.product.metadata_mapping.format_metadata` for further
@@ -109,8 +109,8 @@ class CSWSearch(Search):
109
109
  **kwargs: Any,
110
110
  ) -> tuple[list[EOProduct], Optional[int]]:
111
111
  """Perform a search on a OGC/CSW-like interface"""
112
- product_type = kwargs.get("productType")
113
- if product_type is None:
112
+ collection = kwargs.get("collection")
113
+ if collection is None:
114
114
  return ([], 0) if prep.count else ([], None)
115
115
  auth = kwargs.get("auth")
116
116
  if auth:
@@ -119,16 +119,16 @@ class CSWSearch(Search):
119
119
  self.__init_catalog()
120
120
  results: list[EOProduct] = []
121
121
  if self.catalog:
122
- provider_product_type = self.config.products[product_type]["productType"]
123
- for product_type_def in self.config.search_definition["product_type_tags"]:
124
- product_type_search_tag = product_type_def["name"]
122
+ provider_collection = self.config.products[collection]["collection"]
123
+ for collection_def in self.config.search_definition["collection_tags"]:
124
+ collection_search_tag = collection_def["name"]
125
125
  logger.debug(
126
- "Querying <%s> tag for product type %s",
127
- product_type_search_tag,
128
- provider_product_type,
126
+ "Querying <%s> tag for collection %s",
127
+ collection_search_tag,
128
+ provider_collection,
129
129
  )
130
130
  constraints = self.__convert_query_params(
131
- product_type_def, provider_product_type, kwargs
131
+ collection_def, provider_collection, kwargs
132
132
  )
133
133
  with patch_owslib_requests(verify=True):
134
134
  try:
@@ -139,20 +139,20 @@ class CSWSearch(Search):
139
139
  import traceback as tb
140
140
 
141
141
  logger.warning(
142
- "Failed to query %s for product type %s : %s",
143
- product_type_search_tag,
144
- product_type,
142
+ "Failed to query %s for collection %s : %s",
143
+ collection_search_tag,
144
+ collection,
145
145
  tb.format_exc(),
146
146
  )
147
147
  continue
148
148
  partial_results = [
149
- self.__build_product(record, product_type, **kwargs)
149
+ self.__build_product(record, collection, **kwargs)
150
150
  for record in self.catalog.records.values()
151
151
  ]
152
152
  logger.info(
153
153
  "Found %s results querying %s",
154
154
  len(partial_results),
155
- product_type_search_tag,
155
+ collection_search_tag,
156
156
  )
157
157
  results.extend(partial_results)
158
158
  logger.info("Found %s overall results", len(results))
@@ -182,7 +182,7 @@ class CSWSearch(Search):
182
182
  e,
183
183
  )
184
184
 
185
- def __build_product(self, rec: Any, product_type: str, **kwargs: Any) -> EOProduct:
185
+ def __build_product(self, rec: Any, collection: str, **kwargs: Any) -> EOProduct:
186
186
  """Enable search results to be handled by http download plugin"""
187
187
  download_url = ""
188
188
  resource_filter = re.compile(
@@ -217,11 +217,11 @@ class CSWSearch(Search):
217
217
  else:
218
218
  properties["geometry"] = wkt.loads(properties["geometry"])
219
219
  return EOProduct(
220
- product_type,
220
+ collection,
221
221
  self.provider,
222
222
  # TODO: EOProduct has no more *args in its __init__ (search_args attribute removed)
223
223
  # Not sure why download_url was here in the first place, needs to be updated,
224
- # possibly by having instead 'downloadLink' in the properties
224
+ # possibly by having instead 'eodag:download_link' in the properties
225
225
  # download_url,
226
226
  properties,
227
227
  searched_bbox=kwargs.get("footprints"),
@@ -229,8 +229,8 @@ class CSWSearch(Search):
229
229
 
230
230
  def __convert_query_params(
231
231
  self,
232
- product_type_def: dict[str, Any],
233
- product_type: str,
232
+ collection_def: dict[str, Any],
233
+ collection: str,
234
234
  params: dict[str, Any],
235
235
  ) -> Union[list[OgcExpression], list[list[OgcExpression]]]:
236
236
  """Translates eodag search to CSW constraints using owslib constraint classes"""
@@ -238,17 +238,17 @@ class CSWSearch(Search):
238
238
  # How the match should be performed (fuzzy, prefix, postfix or exact).
239
239
  # defaults to fuzzy
240
240
  pt_tag, matching = (
241
- product_type_def["name"],
242
- product_type_def.get("matching", "fuzzy"),
241
+ collection_def["name"],
242
+ collection_def.get("matching", "fuzzy"),
243
243
  )
244
244
  if matching == "prefix":
245
- constraints.append(PropertyIsLike(pt_tag, "{}%".format(product_type)))
245
+ constraints.append(PropertyIsLike(pt_tag, "{}%".format(collection)))
246
246
  elif matching == "postfix":
247
- constraints.append(PropertyIsLike(pt_tag, "%{}".format(product_type)))
247
+ constraints.append(PropertyIsLike(pt_tag, "%{}".format(collection)))
248
248
  elif matching == "exact":
249
- constraints.append(PropertyIsEqualTo(pt_tag, product_type))
249
+ constraints.append(PropertyIsEqualTo(pt_tag, collection))
250
250
  else: # unknown matching is considered to be equal to 'fuzzy'
251
- constraints.append(PropertyIsLike(pt_tag, "%{}%".format(product_type)))
251
+ constraints.append(PropertyIsLike(pt_tag, "%{}%".format(collection)))
252
252
 
253
253
  # `footprint`
254
254
  fp = params.get("geometry")
@@ -259,8 +259,8 @@ class CSWSearch(Search):
259
259
 
260
260
  # dates
261
261
  start, end = (
262
- params.get("startTimeFromAscendingNode"),
263
- params.get("completionTimeFromAscendingNode"),
262
+ params.get("start_datetime"),
263
+ params.get("end_datetime"),
264
264
  )
265
265
  if start and "date_tags" in self.config.search_definition:
266
266
  constraints.append(