Repository: opendatacube/datacube-core

Issue:
Confusing error message from dc.load when misspelling product name
### Expected behaviour

When calling `dc.load` with misspelled product:

```python
xx = dc.load(product="no_such_product", time='2018', lon=.., lat..)
```

I expect to see an error message like `No such product: "no_such_product"`.

### Actual behaviour

Error message reads:

```
ValueError: No products match search terms: {'time': Range(begin=datetime.datetime(2018, 1, 1, 0, 0, tzinfo=), end=datetime.datetime(2018, 12, 31, 23, 59, 59, 999999, tzinfo=tzutc())), 
'lat': Range(begin=-20.25, end=-20.05), 
'lon': Range(begin=24.6, end=24.8), 'product': 'no_such_product'}
```

If `product=` is explicitly supplied by the user then error message should be more precise than above. Most users are not aware that `product=` can be omitted in some rare cases and instead can be inferred from the search terms used. This is not a feature that is commonly used, not sure how useful it is really, but it does make for confusing error reporting as above.



Discussion:
I think it is safe to deprecate the product finding feature, perhaps refactor it into a `find_products` method, and make lookup by name only the default.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


Resolving patch:
diff --git a/datacube/api/query.py b/datacube/api/query.py
--- a/datacube/api/query.py
+++ b/datacube/api/query.py
@@ -272,6 +272,9 @@ def _time_to_search_dims(time_range):
 
         if hasattr(time_range, '__iter__') and not isinstance(time_range, str):
             tmp = list(time_range)
+            if len(tmp) > 2:
+                raise ValueError(f"Please supply start and end date only for time query")
+
             tr_start, tr_end = tmp[0], tmp[-1]
 
         # Attempt conversion to isoformat
diff --git a/datacube/index/_datasets.py b/datacube/index/_datasets.py
--- a/datacube/index/_datasets.py
+++ b/datacube/index/_datasets.py
@@ -637,7 +637,11 @@ def _do_search_by_product(self, query, return_fields=False, select_field_names=N
 
         product_queries = list(self._get_product_queries(query))
         if not product_queries:
-            raise ValueError('No products match search terms: %r' % query)
+            product = query.get('product', None)
+            if product is None:
+                raise ValueError('No products match search terms: %r' % query)
+            else:
+                raise ValueError(f"No such product: {product}")
 
         for q, product in product_queries:
             dataset_fields = product.metadata_type.dataset_fields


Test patch:
diff --git a/integration_tests/test_end_to_end.py b/integration_tests/test_end_to_end.py
--- a/integration_tests/test_end_to_end.py
+++ b/integration_tests/test_end_to_end.py
@@ -109,6 +109,10 @@ def test_end_to_end(clirunner, index, testdata_dir, ingest_configs, datacube_env
     with pytest.raises(ValueError):
         dc.find_datasets(time='2019')  # no product supplied, raises exception
 
+    with pytest.raises(ValueError) as err:
+        dc.find_datasets(product='no-such-product')  # bad product name is supplied
+    assert "No such product" in str(err)
+
     check_open_with_dc(index)
     check_open_with_grid_workflow(index)
     check_load_via_dss(index)
diff --git a/tests/api/test_query.py b/tests/api/test_query.py
--- a/tests/api/test_query.py
+++ b/tests/api/test_query.py
@@ -71,6 +71,12 @@ def test_query_kwargs():
     query = Query(index=mock_index, time=('2001', '2002'))
     assert 'time' in query.search
 
+    with pytest.raises(ValueError):
+        query = Query(index=mock_index, time=('2001', '2002', '2003'))
+
+    with pytest.raises(ValueError):
+        query = Query(index=mock_index, time=['2001', '2002', '2003'])
+
     with pytest.raises(ValueError):
         Query(index=mock_index,
               y=-4174726, coordinate_reference_system='WGS84',
@@ -135,7 +141,7 @@ def format_test(start_out, end_out):
      format_test('2008-11-10T11:00', '2008-11-16T14:01:59.999999')),
     ((datetime.date(1995, 1, 1), datetime.date(1999, 1, 1)),
      format_test('1995-01-01T00:00:00', '1999-01-01T23:59:59.999999')),
-    ((datetime.datetime(2008, 1, 1), datetime.date(2008, 1, 4), datetime.datetime(2008, 1, 10, 23, 59, 40)),
+    ((datetime.datetime(2008, 1, 1), datetime.datetime(2008, 1, 10, 23, 59, 40)),
      format_test('2008-01-01T00:00:00', '2008-01-10T23:59:40.999999')),
     ((datetime.date(2008, 1, 1)),
      format_test('2008-01-01T00:00:00', '2008-01-01T23:59:59.999999'))