geopython / geopython/pygeoapi

STAC: Make `/search` compliant with Item-Search spec

Open
#2,401 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Python
Stars
624
Forks
326
Avg merge
8h 25m
Merged PRs (30d)
2

Description

**Note:** This Issue is subject to RFC4: https://pygeoapi.io/development/rfc/4

## Is your feature request related to a problem? Please describe.

Per the [STAC API spec](https://github.com/radiantearth/stac-api-spec/blob/release/v1.0.0/overview.md#endpoints), `/search` is Item Search: it returns an `ItemCollection` of STAC Items, and its filters (`bbox`, `datetime`, `ids`, `collections`, `intersects`) select Items across one or more collections.

Producing STAC Items has two parts. #2355 established that the backend-specific part — reading `collection`, `assets`, and `links` out of whatever shape a store keeps them in, and placing them at the top level of a Feature — [belongs in a provider, not in `stac.py`](https://github.com/geopython/pygeoapi/issues/2355#issuecomment). This issue covers the backend-agnostic part: converting a shape-correct Feature into a conformant Item Search response in core.

Today [`pygeoapi.api.stac.search`](https://github.com/geopython/pygeoapi/blob/b5844a03ada922c3268b1c9160b4027212ac8bf1/pygeoapi/api/stac.py#L293-L301) unions the features of each `type: stac-collection` resource by flipping the resource `type` to `collection` and calling `itemtypes.get_collection_items` ([L359-L363](https://github.com/geopython/pygeoapi/blob/b5844a03ada922c3268b1c9160b4027212ac8bf1/pygeoapi/api/stac.py#L359-L363)), then post-processing each feature ([L372-L386](https://github.com/geopython/pygeoapi/blob/b5844a03ada922c3268b1c9160b4027212ac8bf1/pygeoapi/api/stac.py#L372-L386)). Two required pieces are not produced, and neither can come from a provider because both derive from the request rather than from stored data:

1. **Per-Item links are not generated.** A STAC Item requires `self`, `parent`, `collection`, and `root` links, built from the pygeoapi base URL plus the collection id and item id. `search()` adds only response-level `root`/`prev`/`next` links; each feature's `links` is defaulted to `[]` ([L382-L383](https://github.com/geopython/pygeoapi/blob/b5844a03ada922c3268b1c9160b4027212ac8bf1/pygeoapi/api/stac.py#L382-L383)).

2. **The response is not declared or framed as Item Search.** No STAC API Item Search conformance class is advertised, and the code frames results as collections (the docstring reads "STAC API Queries stac-collection"; results are collected into a `collections` variable).

Because `/search` is not explicitly Item Search, the `collections` filter proposed in #2372 has no correct target: it selects Items by parent collection, which only has meaning once results are Items that carry a `collection`.

## Describe the solution you'd like

Handle Item Search in core, operating on shape-correct Features supplied by a provider:

- Generate the `self`, `parent`, `collection`, and `root` links for each Item from the request context.
- Advertise the STAC API Item Search conformance class in the STAC landing page `conformsTo`.
- Update the naming and docstring of `pygeoapi.api.stac.search` to reflect Item Search.

This follows how OGC API Records is implemented: backend-agnostic profiling and conformance live in the API layer (`itemtypes.get_collection_items` branches on `feature` vs `record` at [L336-L342](https://github.com/geopython/pygeoapi/blob/b5844a03ada922c3268b1c9160b4027212ac8bf1/pygeoapi/api/itemtypes.py#L336-L342) and declares Records conformance classes), while backend-specific shaping lives in providers. STAC Item Search follows the same split: `stac.py` for the agnostic conversion and links, a provider for backend shaping.

## Describe alternatives you've considered

- **Perform the backend shaping in `stac.py`** (for example, relocating `collection`/`assets`/`links` out of `feature.properties`). Rejected in #2355 because the source shape is backend-specific and `stac.py` should stay backend-agnostic. Excluded here.
- **Leave `/search` unchanged and correct results client-side.** Non-conformant; per-Item links cannot be reconstructed by a client, and `/search` is defined as Item Search.
- **Model one `stac-collection` resource per STAC collection.** Fits pygeoapi's one-resource-per-collection model, but does not scale to catalogs holding many collections' items in a single store. Item Search over a shared, multi-collection store, filtered by `collections`, is the target use case.

## Additional context

Part of a sequence of STAC API changes for the USGS NHGF work (continuation of #2129, #2153, #2340):

- This issue — core Item Search: per-Item links, conformance, and naming.
- (separate, forthcoming) — a STAC provider that supplies shape-correct Features from a SQL store (the backend half of #2355).
- #2372 — add `ids` and `collections` filters to `/search` (depends on this issue).
- (separate, forthcoming) — a Collection Search endpoint at `/collections`.
- (separate, forthcoming) — rename the `stac-collection` resource `type`, which becomes ambiguous once Collection Search exists, with a back-compatible alias.

Contributor guide

Open the contributing guide

Research direction

Start in pygeoapi/api/stac.py at search(), using the linked lines to trace current result framing and per-feature link handling; review itemtypes.py for the existing API-layer conformance pattern. Update Item Search naming, request-derived per-Item links, and the STAC landing page conformance declaration, then verify that /search returns conformant Item Search responses.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.