Background
In STAC API 1.0, `numberMatched` and `numberReturned` were optional fields in `/search` and `/collections/{id}/items` responses. In STAC API 1.1.0 (the version this API now targets), `numberReturned` is required per the OGC API – Features alignment, while `numberMatched` remains optional but strongly recommended for first-page responses.
The current implementation returns neither field, which is non-conformant with 1.1.0 and reduces usability for clients that display result counts (STAC Browser, QGIS STAC plugin).
Proposed fix
Two separate concerns with different cost profiles:
1. numberReturned — free, implement unconditionally
`numberReturned` is simply `len(features)` on the response. Zero database cost. Override `_search_base` in `NdcCoreCrudClient` (api/src/main.py) to inject it into every ItemCollection response:
```python
async def _search_base(self, search_request, request: Request):
result = await super()._search_base(search_request, request=request)
# Inject numberReturned (free — no extra DB query)
if isinstance(result, dict) and "features" in result:
result.setdefault("numberReturned", len(result["features"]))
return result
```
2. numberMatched — expensive, opt-in via query parameter
A full `COUNT(*)` on broad queries against the 35M+ row pgSTAC partition is too expensive to run unconditionally. Two options:
Option A — ?count=true opt-in (recommended)
Add a custom query parameter. When absent, omit `numberMatched`. When present, pgSTAC's `search` function is called with `nocount=false` to return the total.
Option B — enable stac-fastapi `ContextExtension`
`ContextExtension` adds `context: {returned, matched, limit}` to every response. pgSTAC supports this natively but always runs the COUNT, which is problematic for large collections without a `bbox` or tight `datetime` filter.
Option A is preferred — exposes the capability without making it the default for unauthenticated broad queries.
Notes
- The DB schema update mentioned in the original issue is complete (pgSTAC 0.9.11 baseline in place).
- `numberReturned` should be shipped first as it is zero-cost and immediately improves 1.1.0 conformance.
- The STAC validator (`test/run_stac_validator.py`) should catch the missing field once the conformance check for OGC Features alignment is enabled.
Background
In STAC API 1.0, `numberMatched` and `numberReturned` were optional fields in `/search` and `/collections/{id}/items` responses. In STAC API 1.1.0 (the version this API now targets), `numberReturned` is required per the OGC API – Features alignment, while `numberMatched` remains optional but strongly recommended for first-page responses.
The current implementation returns neither field, which is non-conformant with 1.1.0 and reduces usability for clients that display result counts (STAC Browser, QGIS STAC plugin).
Proposed fix
Two separate concerns with different cost profiles:
1.
numberReturned— free, implement unconditionally`numberReturned` is simply `len(features)` on the response. Zero database cost. Override `_search_base` in `NdcCoreCrudClient` (
api/src/main.py) to inject it into every ItemCollection response:```python
async def _search_base(self, search_request, request: Request):
result = await super()._search_base(search_request, request=request)
# Inject numberReturned (free — no extra DB query)
if isinstance(result, dict) and "features" in result:
result.setdefault("numberReturned", len(result["features"]))
return result
```
2.
numberMatched— expensive, opt-in via query parameterA full `COUNT(*)` on broad queries against the 35M+ row pgSTAC partition is too expensive to run unconditionally. Two options:
Option A —
?count=trueopt-in (recommended)Add a custom query parameter. When absent, omit `numberMatched`. When present, pgSTAC's `search` function is called with `nocount=false` to return the total.
Option B — enable stac-fastapi `ContextExtension`
`ContextExtension` adds `context: {returned, matched, limit}` to every response. pgSTAC supports this natively but always runs the COUNT, which is problematic for large collections without a `bbox` or tight `datetime` filter.
Option A is preferred — exposes the capability without making it the default for unauthenticated broad queries.
Notes