Repository: opendatacube/datacube-core

Issue:
Searching for UUIDs from the command line
### Expected behaviour
I would like to be able to search for UUIDs from the command line, e.g.:

    datacube dataset search id = 26931d17-7a4e-4b55-98e7-d6777fb61df6

### Actual behaviour
If I try to run this command, `datacube` crashes with the following Exception:

```
  File "/g/data/v10/public/modules/dea/20200828/lib/python3.6/site-packages/datacube/ui/click.py", line 356, in my_parse
    return parse_expressions(*list(value))
  File "/g/data/v10/public/modules/dea/20200828/lib/python3.6/site-packages/datacube/ui/expression.py", line 112, in parse_expressions
    tree = expr_parser.parse(' '.join(expression_text))
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/lark.py", line 391, in parse
    return self.parser.parse(text, start=start)
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/parser_frontends.py", line 199, in parse
    return self._parse(text, start)
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/parser_frontends.py", line 53, in _parse
    return self.parser.parse(input, start, *args)
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/parsers/earley.py", line 293, in parse
    to_scan = self._parse(stream, columns, to_scan, start_symbol)
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/parsers/xearley.py", line 138, in _parse
    to_scan = scan(i, to_scan)
  File "/g/data/v10/public/modules/dea-env/20200713/lib/python3.6/site-packages/lark/parsers/xearley.py", line 115, in scan
    raise UnexpectedCharacters(stream, i, text_line, text_column, {item.expect.name for item in to_scan}, set(to_scan))
lark.exceptions.UnexpectedCharacters: No terminal defined for '-' at line 1 col 14

id = 26931d17-7a4e-4b55-98e7-d6777fb61df6
             ^

Expecting: {'EQUAL', 'IN'}
```

### Cause
This happens because the search expression parser is treating the UUID as a number instead of a string. It's possible to work around this by surrounding the UUID in quotes. e.g.:

    datacube dataset search id = \"26931d17-7a4e-4b55-98e7-d6777fb61df6\"


### Intended changes

It should be possible to improve the parser to recognise UUIDs, to avoid having to manually quote them from the shell. The search expression 'language' was intended to be shell escaping friendly, so this seems like an obvious improvement. 

### Environment information

* Which ``datacube --version`` are you using?
`Open Data Cube core, version 1.8.3`




Discussion:
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.

This also occurs when searching for region codes that start with digits, but should be interpreted as strings. We may be able to catch these as well.
eg.
```
$ datacube dataset search "region_code = 56KKD" --limit=10
lark.exceptions.UnexpectedEOF: Unexpected end-of-input. Expected one of: 
        * IN
        * EQUAL
```

Resolving patch:
diff --git a/datacube/ui/expression.py b/datacube/ui/expression.py
--- a/datacube/ui/expression.py
+++ b/datacube/ui/expression.py
@@ -43,7 +43,7 @@
           | ESCAPED_STRING -> string
           | SIMPLE_STRING -> simple_string
           | URL_STRING -> url_string
-
+          | UUID -> simple_string
 
     ?date_range: date -> single_date
                | "[" date "," date "]" -> date_pair
@@ -55,14 +55,16 @@
     YEAR: DIGIT ~ 4
     MONTH: DIGIT ~ 1..2
     DAY: DIGIT ~ 1..2
-    SIMPLE_STRING: /[a-zA-Z][\w._-]*/
+    SIMPLE_STRING: /[a-zA-Z][\w._-]*/ | /[0-9]+[\w_-][\w._-]*/
     URL_STRING: /[a-z0-9+.-]+:\/\/([:\/\w._-])*/
+    UUID: HEXDIGIT~8 "-" HEXDIGIT~4 "-" HEXDIGIT~4 "-" HEXDIGIT~4 "-" HEXDIGIT~12
 
 
     %import common.ESCAPED_STRING
     %import common.SIGNED_NUMBER
     %import common.INT
     %import common.DIGIT
+    %import common.HEXDIGIT
     %import common.CNAME
     %import common.WS
     %ignore WS


Test patch:
diff --git a/tests/ui/test_expression_parsing.py b/tests/ui/test_expression_parsing.py
--- a/tests/ui/test_expression_parsing.py
+++ b/tests/ui/test_expression_parsing.py
@@ -14,6 +14,21 @@ def test_parse_empty_str():
     assert q == {}
 
 
+def test_id_search():
+    q = parse_expressions("id = 26931d17-7a4e-4b55-98e7-d6777fb61df6")
+    assert q['id'] == "26931d17-7a4e-4b55-98e7-d6777fb61df6"
+
+    q = parse_expressions('id = "26931d17-7a4e-4b55-98e7-d6777fb61df6"')
+    assert q['id'] == "26931d17-7a4e-4b55-98e7-d6777fb61df6"
+
+
+def test_simple_string():
+    q = parse_expressions("region_code = 56KKD", "i=10", "f=10.3")
+    assert q['region_code'] == '56KKD'
+    assert q['i'] == 10
+    assert q['f'] == 10.3
+
+
 def test_between_expression():
     q = parse_expressions('time in [2014, 2015]')
     assert 'time' in q