xcube Server fails to resolve datasets for relative parent paths
**Describe the bug**

xcube Server can no longer locate local datasets when they identidied by relative paths including parent folders. 

**To Reproduce**

In `examples/server/demo/config.yml` add a datasets entry:

```yaml
Datasets:
  - Identifier: cmems
    Path: ../../../dataset-bal-analysis-forecast-wav-hourly.zarr
    FileSystem: file
    Style: default
```

My current working directory is my xcube sources folder: `D:/Projects/xcube`. Run xcube Server:

    xcube serve --loglevel DEBUG serve -vvvv --traceperf -c examples/serve/demo/config.yml 

   

After GET http://localhost:8080/datasets?details=1, one can find in the logs 

```
...
[I 2022-10-24 15:28:50,438 xcube] Loading details for dataset cmems
[I 2022-10-24 15:28:50,442 xcube] Opened dataset 'cmems' from data store 'file_2': took 4.25ms
[W 2022-10-24 15:28:50,442 xcube] Skipping dataset cmems: could not open dataset: Failed to open dataset 'D:/Projects/xcube/examples/serve/demo/dataset-bal-analysis-forecast-wav-hourly.zarr': group not found at path ''
...
```

**Expected behavior**

Dataset should be opened from `${config-dir}/../../../dataset-bal-analysis-forecast-wav-hourly.zarr`, in the example above namely `D:/Projects/xcube/dataset-bal-analysis-forecast-wav-hourly.zarr`.


**EDIT**

The same happens when specifying an absolute path:

```yaml
Datasets:
  - Identifier: cmems
    Path: D:/Projects/xcube/dataset-bal-analysis-forecast-wav-hourly.zarr
    FileSystem: file
    Style: default
```

It only works if specifying a relative path inside the config directory. After copying the dataset into the configuration directory,  then

```yaml
Datasets:
  - Identifier: cmems
    Path: dataset-bal-analysis-forecast-wav-hourly.zarr
    FileSystem: file
    Style: default
```

works.

diff --git a/CHANGES.md b/CHANGES.md
index 2915c45b..64fe8975 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,9 @@
 ## Changes in 0.13.0.dev4
 
+### Fixes
+
+* xcube serve correctly resolves relative paths to datasets (#758)
+
 ## Changes in 0.13.0.dev3
 
 ### Other
diff --git a/test/webapi/datasets/test_context.py b/test/webapi/datasets/test_context.py
index 8dfcf602..c2a3a021 100644
--- a/test/webapi/datasets/test_context.py
+++ b/test/webapi/datasets/test_context.py
@@ -530,3 +530,48 @@ class MaybeAssignStoreInstanceIdsTest(unittest.TestCase):
 
         self.assertEqual(dataset_configs[0]['StoreInstanceId'],
                          dataset_configs[1]['StoreInstanceId'])
+
+    def test_mix_of_absolute_and_relative_paths(self):
+        configs = [
+            {
+                'Identifier': 'z_0',
+                'FileSystem': 'file',
+                'Path': '/path/abc.zarr'
+            },
+            {
+                'Identifier': 'z_1',
+                'FileSystem': 'file',
+                'Path': 'def.zarr'
+            },
+            {
+                'Identifier': 'z_2',
+                'FileSystem': 'file',
+                'Path': 'relative/ghi.zarr'
+            }
+        ]
+
+        ctx = self.get_datasets_ctx(Datasets=configs)
+        dataset_configs = ctx.get_dataset_configs()
+
+        expected_dataset_configs = [
+            {
+                'Identifier': 'z_0',
+                'FileSystem': 'file',
+                'Path': 'abc.zarr',
+                'StoreInstanceId': 'file_1'
+            },
+            {
+                'Identifier': 'z_1',
+                'FileSystem': 'file',
+                'Path': 'def.zarr',
+                'StoreInstanceId': 'file_3'
+            },
+            {
+                'Identifier': 'z_2',
+                'FileSystem': 'file',
+                'Path': 'ghi.zarr',
+                'StoreInstanceId': 'file_2'
+            }
+        ]
+
+        self.assertEqual(expected_dataset_configs, dataset_configs)
diff --git a/xcube/webapi/datasets/context.py b/xcube/webapi/datasets/context.py
index 1d628958..260c5396 100644
--- a/xcube/webapi/datasets/context.py
+++ b/xcube/webapi/datasets/context.py
@@ -265,34 +265,9 @@ class DatasetsContext(ResourcesContext):
         for file_system, store_params, config_list in config_lists:
             # Retrieve paths per configuration
             paths = [dc['Path'] for dc in config_list]
-            list.sort(paths)
             # Determine common prefixes of paths (and call them roots)
-            prefixes = _get_common_prefixes(paths)
-            if len(prefixes) < 1:
-                roots = ['']
-            else:
-                # perform further step to merge prefixes with same start
-                prefixes = list(set(prefixes))
-                prefixes.sort()
-                roots = []
-                root_candidate = prefixes[0]
-                for root in prefixes[1:]:
-                    common_root = os.path.commonprefix([root_candidate, root])
-                    if _is_not_empty(common_root):
-                        root_candidate = common_root
-                    else:
-                        roots.append(root_candidate)
-                        root_candidate = root
-                roots.append(root_candidate)
+            roots = _get_roots(paths)
             for root in roots:
-                # ensure root does not end with full or partial directory
-                # or file name
-                while not root.endswith("/") \
-                        and not root.endswith("\\") \
-                        and len(root) > 0:
-                    root = root[:-1]
-                if root.endswith("/") or root.endswith("\\"):
-                    root = root[:-1]
                 abs_root = root
                 # For local file systems:
                 # Determine absolute root from base dir
@@ -320,7 +295,8 @@ class DatasetsContext(ResourcesContext):
                     data_store_pool.add_store_config(store_instance_id,
                                                      data_store_config)
                 for config in config_list:
-                    if config['Path'].startswith(root):
+                    if config['Path'].startswith(root) and \
+                            config.get('StoreInstanceId') is None:
                         config['StoreInstanceId'] = store_instance_id
                         new_path = config['Path'][len(root):]
                         while new_path.startswith("/") or \
@@ -842,11 +818,38 @@ def _is_not_empty(prefix):
     return prefix != '' and prefix != '/' and prefix != '\\'
 
 
+def _get_roots(paths):
+    list.sort(paths)
+    prefixes = _get_common_prefixes(paths)
+    if len(prefixes) < 1:
+        roots = ['']
+    else:
+        # perform further step to merge prefixes with same start
+        prefixes = list(set(prefixes))
+        prefixes.sort()
+        roots = []
+        root_candidate = prefixes[0]
+        for root in prefixes[1:]:
+            common_root = os.path.commonprefix([root_candidate, root])
+            if _is_not_empty(common_root):
+                root_candidate = common_root
+            else:
+                roots.append(root_candidate)
+                root_candidate = root
+        roots.append(root_candidate)
+    if roots[0] == '':
+        roots.append(roots.pop(0))
+    return roots
+
+
 def _get_common_prefixes(p):
     # Recursively examine a list of paths for common prefixes:
     # If no common prefix is found, split the list in half and
     # examine each half separately
     prefix = os.path.commonprefix(p)
+    # ensure prefix does not end with full or partial directory
+    # or file name
+    prefix = prefix[:max(_lastindex(prefix, '/'), _lastindex(prefix, '\\'), 0)]
     if _is_not_empty(prefix) or len(p) == 1:
         return [prefix]
     else:
@@ -854,6 +857,13 @@ def _get_common_prefixes(p):
                _get_common_prefixes(p[int(len(p) / 2):])
 
 
+def _lastindex(prefix, symbol):
+    try:
+        return prefix.rindex(symbol)
+    except ValueError:
+        return -1
+
+
 _MULTI_LEVEL_DATASET_OPENERS = {
     "memory": _open_ml_dataset_from_python_code,
 }
['CHANGES.md', 'xcube/webapi/datasets/context.py', 'test/webapi/datasets/test_context.py']