Skip to content

Commit 8f588ea

Browse files
tomkralidiskalxas
authored andcommitted
replace federatedcatalogues with distributedsearch
1 parent c9cd698 commit 8f588ea

37 files changed

Lines changed: 364 additions & 275 deletions

File tree

default-sample.yml

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,23 @@ logging:
5454
profiles:
5555
- apiso
5656

57-
federatedcatalogues:
58-
- id: arctic-sdi-csw
59-
type: CSW
60-
title: Arctic SDI
61-
url: https://catalogue.arctic-sdi.org/csw
62-
- id: pycsw-cite-demo
63-
type: OARec
64-
title: pycsw OGC CITE demo and Reference Implementation
65-
uresultsrl: https://demo.pycsw.org/cite
66-
- id: fedcat03
67-
type: STAC-API
68-
title: Copernicus Data Space Ecosystem (CDSE) asset-level STAC catalogue
69-
url: https://stac.dataspace.copernicus.eu/v1
70-
collections:
71-
- daymet-annual-pr
57+
distributedsearch:
58+
merge_results: false
59+
catalogues:
60+
- id: arctic-sdi-csw
61+
type: CSW
62+
title: Arctic SDI
63+
url: https://catalogue.arctic-sdi.org/csw
64+
- id: pycsw-cite-demo
65+
type: OARec
66+
title: pycsw OGC CITE demo and Reference Implementation
67+
uresultsrl: https://demo.pycsw.org/cite
68+
- id: fedcat03
69+
type: STAC-API
70+
title: Copernicus Data Space Ecosystem (CDSE) asset-level STAC catalogue
71+
url: https://stac.dataspace.copernicus.eu/v1
72+
collections:
73+
- daymet-annual-pr
7274

7375
manager:
7476
transactions: false

docker/compose/pycsw.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ logging:
5151
profiles:
5252
- apiso
5353

54-
federatedcatalogues:
55-
- id: fedcat01
56-
type: CSW
57-
title: Arctic SDI
58-
url: https://catalogue.arctic-sdi.org/csw
54+
distributedsearch:
55+
catalogues:
56+
- id: fedcat01
57+
type: CSW
58+
title: Arctic SDI
59+
url: https://catalogue.arctic-sdi.org/csw
5960

6061
manager:
6162
transactions: false

docker/helm/values.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ pycsw:
4747
# logfile: /tmp/pycsw.log
4848
profiles:
4949
- apiso
50-
# federatedcatalogues:
51-
# - id: fedcat01
52-
# type: CSW
53-
# title: Arctic SDI
54-
# url: https://catalogue.arctic-sdi.org/csw
50+
# distributedsearch:
51+
# catalogues:
52+
# - id: fedcat01
53+
# type: CSW
54+
# title: Arctic SDI
55+
# url: https://catalogue.arctic-sdi.org/csw
5556
manager:
5657
transactions: "false"
5758
allowed_ips:

docker/kubernetes/pycsw-configmap.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ data:
5656
profiles:
5757
- apiso
5858
59-
federatedcatalogues:
60-
- id: fedcat01
61-
type: CSW
62-
title: Arctic SDI
63-
url: https://catalogue.arctic-sdi.org/csw
59+
distributedsearch:
60+
catalogues:
61+
- id: fedcat01
62+
type: CSW
63+
title: Arctic SDI
64+
url: https://catalogue.arctic-sdi.org/csw
6465
6566
manager:
6667
transactions: false

docker/pycsw.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ logging:
5151
profiles:
5252
- apiso
5353

54-
federatedcatalogues:
55-
- id: fedcat01
56-
type: CSW
57-
title: Arctic SDI
58-
url: https://catalogue.arctic-sdi.org/csw
54+
distributedsearch:
55+
catalogues:
56+
- id: fedcat01
57+
type: CSW
58+
title: Arctic SDI
59+
url: https://catalogue.arctic-sdi.org/csw
5960

6061
manager:
6162
transactions: false

docs/configuration.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pycsw's runtime configuration is defined by ``default.yml``. pycsw ships with a
1616
- **level**: the logging level (see https://docs.python.org/library/logging.html#logging-levels)
1717
- **logfile**: the full file path to the logfile
1818
- **ogc_schemas_base**: base URL of OGC XML schemas tree file structure (default is http://schemas.opengis.net)
19-
- **federatedcatalogues**: arrray of distributed catalogue endpoints to be used for distributed searching, if requested by the client (see :ref:`distributedsearching`)
19+
- **distributedsearch**: distributed catalogue endpoints to be used for distributed searching, if requested by the client (see :ref:`distributedsearching`)
2020
- **pretty_print**: whether to pretty print the output (``true`` or ``false``). Default is ``false``
2121
- **gzip_compresslevel**: gzip compression level, lowest is ``1``, highest is ``9``. Default is off. **NOTE**: if gzip compression is already enabled via your web server, do not enable this directive (or else the server will try to compress the response twice, resulting in degraded performance)
2222
- **domainquerytype**: for GetDomain operations, how to output domain values. Accepted values are ``list`` and ``range`` (min/max). Default is ``list``

docs/distributedsearching.rst

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Distributed Searching
1515
CSW 2 / 3
1616
---------
1717

18-
pycsw has the ability to perform distributed searching against other CSW servers. Distributed searching is disabled by default; to enable, ``federatedcatalogues`` must be set. A CSW client must issue a GetRecords request with ``csw:DistributedSearch`` specified, along with an optional ``hopCount`` attribute (see subclause 10.8.4.13 of the CSW specification). When enabled, pycsw will search all specified catalogues and return a unified set of search results to the client. Due to the distributed nature of this functionality, requests will take extra time to process compared to queries against the local repository.
18+
pycsw has the ability to perform distributed searching against other CSW servers. Distributed searching is disabled by default; to enable, ``distributedsearch`` must be set. A CSW client must issue a GetRecords request with ``csw:DistributedSearch`` specified, along with an optional ``hopCount`` attribute (see subclause 10.8.4.13 of the CSW specification). When enabled, pycsw will search all specified catalogues and return a unified set of search results to the client. Due to the distributed nature of this functionality, requests will take extra time to process compared to queries against the local repository.
1919

2020
Scenario: Federated Search
2121
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,19 +56,21 @@ in the CSW-all configuration:
5656

5757
.. code-block:: yaml
5858
59-
federatedcatalogues:
60-
- id: fedcat01
61-
type: CSW
62-
title: Federated catalogue 1
63-
url: http://localhost/pycsw/csw.py?config=CSW-1.yml
64-
- id: fedcat02
65-
type: CSW
66-
title: Federated catalogue 2
67-
url: http://localhost/pycsw/csw.py?config=CSW-2.yml
68-
- id: fedcat03
69-
type: CSW
70-
title: Federated catalogue 3
71-
url: http://localhost/pycsw/csw.py?config=CSW-3.yml
59+
distributedsearch:
60+
merge_results: false
61+
catalogues:
62+
- id: fedcat01
63+
type: CSW
64+
title: Federated catalogue 1
65+
url: http://localhost/pycsw/csw.py?config=CSW-1.yml
66+
- id: fedcat02
67+
type: CSW
68+
title: Federated catalogue 2
69+
url: http://localhost/pycsw/csw.py?config=CSW-2.yml
70+
- id: fedcat03
71+
type: CSW
72+
title: Federated catalogue 3
73+
url: http://localhost/pycsw/csw.py?config=CSW-3.yml
7274
7375
At which point a CSW client request to CSW-all with ``distributedsearch=TRUE``, while specifying an optional ``hopCount``. Query network topology:
7476

@@ -101,42 +103,49 @@ Experimental support for distibuted searching is available in pycsw's OGC API -
101103

102104
.. note::
103105

104-
The ``federatedcatalogues`` directives must point to an OGC API - Records **collections** endpoint.
106+
The ``distributedsearch.catalogues`` directives must point to an OGC API - Records **collections** endpoint.
105107

106108
.. code-block:: yaml
107109
108-
federatedcatalogues:
109-
- id: fedcat01
110-
type: OARec
111-
title: Federated catalogue 1
112-
url: https://example.org/collections/collection1
113-
- id: fedcat02
114-
type: OARec
115-
title: Federated catalogue 2
116-
url: https://example.org/collections/collection2
110+
distributedsearch
111+
catalogues:
112+
- id: fedcat01
113+
type: OARec
114+
title: Federated catalogue 1
115+
url: https://example.org/collections/collection1
116+
- id: fedcat02
117+
type: OARec
118+
title: Federated catalogue 2
119+
url: https://example.org/collections/collection2
117120
118121
With the above configured, a distributed search can be invoked as follows:
119122

120123
http://localhost/collections/metadata:main/items?distributedSearch=true
121124

125+
Merging results
126+
^^^^^^^^^^^^^^^
127+
128+
When `distributedsearch.merge_results` exists and is set to ``true``, pycsw will merge all results in ``federatedSearchResults``. To prevent identifier collision, merged federated search results will have identifiers prefixed by their catalogue ``id`` (as defined in ``distributedsearch.catalogues[*].id``. In addition, a ``federatedCatalogueId`` property is added to the feature with the catalogue id.
129+
122130
STAC API
123131
--------
124132

125133
Experimental support for distibuted searching is available in pycsw's STAC API support to allow for searching remote services. The implementation uses the same approach as described above.
126134

127135
.. note::
128136

129-
The ``federatedcatalogues`` directives must point to a STAC API endpoint.
137+
The ``distributedsearch.catalogues`` directives must point to a STAC API endpoint.
130138

131139
.. code-block:: yaml
132140
133-
federatedcatalogues:
134-
- id: fedcat03
135-
type: STAC-API
136-
title: Copernicus Data Space Ecosystem (CDSE) asset-level STAC catalogue
137-
url: https://stac.dataspace.copernicus.eu/v1
138-
collections:
139-
- daymet-annual-pr
141+
distributedsearch:
142+
catalogues:
143+
- id: fedcat03
144+
type: STAC-API
145+
title: Copernicus Data Space Ecosystem (CDSE) asset-level STAC catalogue
146+
url: https://stac.dataspace.copernicus.eu/v1
147+
collections:
148+
- daymet-annual-pr
140149
141150
142151
.. note::
@@ -148,4 +157,10 @@ With the above configured, a distributed search can be invoked as follows:
148157

149158
http://localhost/stac/search?distributedSearch=true
150159

160+
Merging results
161+
^^^^^^^^^^^^^^^
162+
163+
Merging behaviour is implemented in the same manner as OGC API - Records support.
164+
165+
151166
.. _`OGC API - Records - Part 4: Federated Search`: https://github.com/opengeospatial/ogcapi-records/blob/master/extensions/federated-search/document.adoc

pycsw/core/admin.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,9 @@ def cli_migrate_config(ctx, config, verbosity):
618618
'inspire': {}
619619
},
620620
'profiles': [],
621-
'federatedcatalogues': [],
621+
'distributedsearch': {
622+
'catalogues': []
623+
},
622624
'repository': {}
623625
}
624626

@@ -634,7 +636,7 @@ def cli_migrate_config(ctx, config, verbosity):
634636
elif name == 'federatedcatalogues':
635637
dict_[name] = []
636638
for count, fc in enumerate(value.split(',')):
637-
dict_[name].append({'id': f'fedcat{count}', 'url': fc})
639+
dict_['distributedsearch']['catalogues'].append({'id': f'fedcat{count}', 'url': fc})
638640
else:
639641
dict_['server'][name] = get_typed_value(value)
640642

pycsw/ogc/api/records.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ def collection(self, headers_, args, collection='metadata:main'):
483483
'hreflang': self.config['server']['language']
484484
}]
485485

486-
if collection == 'metadata:main' and 'federatedcatalogues' in self.config:
486+
if collection == 'metadata:main' and 'distributedsearch' in self.config:
487487
LOGGER.debug('Adding federated catalogues')
488488
response['links'].append({
489489
'rel': 'http://www.opengis.net/def/rel/ogc/1.0/federatedCatalogues',
@@ -818,7 +818,10 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
818818
distributed = str2bool(args.get('distributedsearch', False))
819819

820820
if distributed:
821-
for fc in self.config.get('federatedcatalogues', []):
821+
distributed_search = self.config.get('distributedsearch', {})
822+
merge_results = distributed_search.get('merge_results', False)
823+
824+
for fc in distributed_search.get('catalogues', []):
822825
if fc['type'] != 'OARec':
823826
LOGGER.debug(f"Federated catalogue type {fc['type']} not supported; skipping")
824827
continue
@@ -833,10 +836,19 @@ def items(self, headers_, json_post_data, args, collection='metadata:main'):
833836
args.pop('distributedsearch')
834837
fc_results = w.collection_items(fc_collection, **args)
835838
for feature in fc_results['features']:
839+
if merge_results:
840+
feature['id'] = f"{fc['id']}::{feature['id']}"
841+
feature['federatedCatalogueId'] = fc['id']
836842
response['federatedSearchResults'][fc['id']]['features'].append(feature)
837843
except Exception as err:
838844
LOGGER.warning(err)
839845

846+
if merge_results:
847+
LOGGER.debug('Merging federated search results into main items')
848+
fsrs = response.pop('federatedSearchResults', None)
849+
for key, value in fsrs.items():
850+
response['features'].extend(value['features'])
851+
840852
LOGGER.debug('Creating links')
841853

842854
link_args = {**args}
@@ -970,7 +982,7 @@ def item(self, headers_, args, collection, item):
970982
distributed = str2bool(args.get('distributed', False))
971983

972984
if distributed:
973-
for fc in self.config.get('federatedcatalogues', []):
985+
for fc in self.config.get('distributedsearch', {}).get('catalogues', []):
974986
if fc['type'] != 'OARec':
975987
LOGGER.debug(f"Federated catalogue type {fc['type']} not supported; skipping")
976988
continue
@@ -1180,7 +1192,7 @@ def federated_catalogues(self, headers_, args, collection):
11801192
fedcats = []
11811193

11821194
if collection == 'metadata:main':
1183-
for fedcat_ in self.config.get('federatedcatalogues', []):
1195+
for fedcat_ in self.config.get('distributedsearch', {}).get('catalogues', []):
11841196
if fedcat_['type'] == 'OARec':
11851197
fedcats.append(fedcat_)
11861198

@@ -1213,7 +1225,7 @@ def federated_catalogue(self, headers_, args, collection, catalogue):
12131225
fedcat = None
12141226

12151227
if collection == 'metadata:main':
1216-
fedcats = self.config.get('federatedcatalogues')
1228+
fedcats = self.config.get('distributedsearch', {}).get('catalogues', [])
12171229
for fedcat_ in fedcats:
12181230
if fedcat_['id'] == catalogue and fedcat_['type'] == 'OARec':
12191231
fedcat = fedcat_

pycsw/ogc/csw/csw2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -825,15 +825,15 @@ def getrecords(self):
825825

826826
hopcount = int(self.parent.kvp.get('hopcount', 2)) - 1
827827

828-
if ('federatedcatalogues' in self.parent.config and
828+
if ('distributedsearch' in self.parent.config and
829829
self.parent.kvp.get('distributedsearch') and
830830
hopcount > 0):
831831

832832
LOGGER.debug('DistributedSearch specified (hopCount: %s).', hopcount)
833833

834834
from owslib.csw import CatalogueServiceWeb
835835
from owslib.ows import ExceptionReport
836-
for fedcat in self.parent.config.get('federatedcatalogues', []):
836+
for fedcat in self.parent.config.get('distributedsearch', {}).get('catalogues', []):
837837
if fedcat['type'] != 'CSW':
838838
LOGGER.debug(f"Federated catalogue type {fedcat['type']} not supported; skipping")
839839
continue

0 commit comments

Comments
 (0)