diff --git a/eodag/api/core.py b/eodag/api/core.py index 5906b7481..d6aa7d84b 100644 --- a/eodag/api/core.py +++ b/eodag/api/core.py @@ -1064,14 +1064,11 @@ def search( ) if search_kwargs.get("id"): - # adds minimal pagination to be able to check only 1 product is returned - search_kwargs.update( - page=1, - items_per_page=2, - raise_errors=raise_errors, - ) return self._search_by_id( - search_kwargs.pop("id"), provider=provider, **search_kwargs + search_kwargs.pop("id"), + provider=provider, + raise_errors=raise_errors, + **search_kwargs, ) # remove datacube query string from kwargs which was only needed for search-by-id search_kwargs.pop("_dc_qs", None) @@ -1454,16 +1451,51 @@ def _search_by_id( # datacube query string _dc_qs = kwargs.pop("_dc_qs", None) + results = SearchResult([]) + for plugin in search_plugins: logger.info( "Searching product with id '%s' on provider: %s", uid, plugin.provider ) logger.debug("Using plugin class for search: %s", plugin.__class__.__name__) plugin.clear() + + # adds maximal pagination to be able to do a search-all + crunch if more + # than one result are returned + items_per_page = plugin.config.pagination.get( + "max_items_per_page", DEFAULT_MAX_ITEMS_PER_PAGE + ) + kwargs.update(items_per_page=items_per_page) if isinstance(plugin, BuildPostSearchResult): - results, _ = self._do_search(plugin, id=uid, _dc_qs=_dc_qs, **kwargs) + kwargs.update( + items_per_page=items_per_page, + _dc_qs=_dc_qs, + ) else: - results, _ = self._do_search(plugin, id=uid, **kwargs) + kwargs.update( + items_per_page=items_per_page, + ) + + try: + # if more than one results are found, try getting them all and then filter using crunch + for page_results in self.search_iter_page_plugin( + search_plugin=plugin, + id=uid, + **kwargs, + ): + results.data.extend(page_results.data) + except Exception: + if kwargs.get("raise_errors"): + raise + continue + + # try using crunch to get unique result + if ( + len(results) > 1 + and len(filtered := results.filter_property(id=uid)) == 1 + ): + results = filtered + if len(results) == 1: if not results[0].product_type: # guess product type from properties @@ -1473,17 +1505,6 @@ def _search_by_id( results[0].driver = results[0].get_driver() return results, 1 elif len(results) > 1: - if getattr(plugin.config, "two_passes_id_search", False): - # check if id of one product exactly matches id that was searched for - # required if provider does not offer search by id and therefore other - # parameters which might not given an exact result are used - for result in results: - if result.properties["id"] == uid.split(".")[0]: - return SearchResult([results[0]]), 1 - # try using crunch to get unique result - if len(filtered := results.filter_property(id=uid)) == 1: - return filtered, 1 - logger.info( "Several products found for this id (%s). You may try searching using more selective criteria.", results, diff --git a/tests/units/test_core.py b/tests/units/test_core.py index c2245378e..9d12a2ed7 100644 --- a/tests/units/test_core.py +++ b/tests/units/test_core.py @@ -1949,7 +1949,7 @@ def test__prepare_search_unknown_product_type(self, mock_fetch_product_types_lis @mock.patch( "eodag.api.core.EODataAccessGateway._do_search", autospec=True, - return_value=([mock.Mock()], 1), + return_value=(SearchResult([mock.Mock()]), 1), ) @mock.patch("eodag.plugins.manager.PluginManager.get_auth_plugin", autospec=True) @mock.patch( @@ -1961,6 +1961,21 @@ def test__search_by_id( self, mock_get_search_plugins, mock_get_auth_plugin, mock__do_search ): """_search_by_id must filter search plugins using given kwargs, clear plugin and perform search""" + # max_items_per_page plugin conf + mock_config = mock.Mock() + type(mock_config).pagination = mock.PropertyMock( + return_value={"max_items_per_page": 100} + ) + type(mock_get_search_plugins.return_value[0]).config = mock.PropertyMock( + return_value=mock_config + ) + type( + mock_get_search_plugins.return_value[0] + ).next_page_query_obj = mock.PropertyMock(return_value={}) + # mocked search result id + type(mock__do_search.return_value[0][0]).properties = mock.PropertyMock( + return_value={"id": "foo"} + ) found = self.dag._search_by_id(uid="foo", productType="bar", provider="baz") @@ -1981,18 +1996,22 @@ def test__search_by_id( mock_get_search_plugins.return_value[0], id="foo", productType="bar", + count=False, + raise_errors=True, + page=1, + items_per_page=100, ) self.assertEqual(found, mock__do_search.return_value) mock__do_search.reset_mock() # return None if more than 1 product is found - m = mock.MagicMock() - p = EOProduct( - "peps", {"id": "a", "geometry": {"type": "Point", "coordinates": [1, 1]}} + mock__do_search.return_value = (SearchResult([mock.Mock(), mock.Mock()]), 2) + type(mock__do_search.return_value[0][0]).properties = mock.PropertyMock( + return_value={"id": "foo"} + ) + type(mock__do_search.return_value[0][1]).properties = mock.PropertyMock( + return_value={"id": "foo"} ) - m.__len__.return_value = 2 - m.__iter__.return_value = [p, p] - mock__do_search.return_value = (m, 2) with self.assertLogs(level="INFO") as cm: found = self.dag._search_by_id(uid="foo", productType="bar", provider="baz") self.assertEqual(found, (SearchResult([]), 0)) @@ -2000,13 +2019,13 @@ def test__search_by_id( mock__do_search.reset_mock() # return 1 result if more than 1 product is found but only 1 has the matching id - m = mock.MagicMock() - p2 = EOProduct( - "peps", {"id": "foo", "geometry": {"type": "Point", "coordinates": [1, 1]}} + mock__do_search.return_value = (SearchResult([mock.Mock(), mock.Mock()]), 2) + type(mock__do_search.return_value[0][0]).properties = mock.PropertyMock( + return_value={"id": "foo"} + ) + type(mock__do_search.return_value[0][1]).properties = mock.PropertyMock( + return_value={"id": "foooooo"} ) - m.__len__.return_value = 2 - m.__iter__.return_value = [p, p2] - mock__do_search.return_value = (m, 2) found = self.dag._search_by_id(uid="foo", productType="bar", provider="baz") self.assertEqual(found[1], 1) self.assertEqual(len(found[0]), 1)