Skip to content

Commit

Permalink
Return empty results when searching for a nonexistent collection (#185)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc authored Feb 23, 2022
1 parent 4d818d6 commit 246ff66
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- Open-ended datetime intervals using either empty string or '..' now work
- Correct content types are now returned
- Searching for a nonexistent collection returns empty results

### Changed

Expand Down
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"got": "^11.0.0",
"http-errors": "^2.0.0",
"js-yaml": "^3.13.1",
"lodash": "^4.17.21",
"memorystream": "^0.3.1",
"morgan": "^1.10.0",
"pump": "^3.0.0",
Expand Down
30 changes: 23 additions & 7 deletions src/lib/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const { pickBy } = require('lodash')
const gjv = require('geojson-validation')
const extent = require('@mapbox/extent')
const { isIndexNotFoundError } = require('./es')
const logger = console

// max number of collections to retrieve
Expand Down Expand Up @@ -388,20 +390,34 @@ const searchItems = async function (collectionId, queryParameters, backend, endp
}

// Keep only existing parameters
const searchParameters = Object.keys(parameters)
.filter((key) => parameters[key])
.reduce((obj, key) => ({
...obj,
[key]: parameters[key]
}), {})
const searchParameters = pickBy(parameters)

let newEndpoint = `${endpoint}/search`
if (collectionId) {
searchParameters.collections = [collectionId]
newEndpoint = `${endpoint}/collections/${collectionId}/items`
}
logger.debug(`Search parameters: ${JSON.stringify(searchParameters)}`)
const results = await backend.search(searchParameters, page, limit)

let results
try {
results = await backend.search(searchParameters, page, limit)
} catch (error) {
if (isIndexNotFoundError(error)) {
results = {
context: {
matched: 0,
returned: 0,
page,
limit
},
results: []
}
} else {
throw error
}
}

const { 'results': itemsResults, 'context': itemsMeta } = results
const pageLinks = buildPageLinks(itemsMeta, searchParameters, newEndpoint, httpMethod)
const items = addItemLinks(itemsResults, endpoint)
Expand Down
12 changes: 11 additions & 1 deletion src/lib/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ const logger = console //require('./logger')
const COLLECTIONS_INDEX = process.env.COLLECTIONS_INDEX || 'collections'
const ITEMS_INDEX = process.env.ITEMS_INDEX || 'items'

const isIndexNotFoundError = (e) => (
e instanceof Error
&& e.name === 'ResponseError'
&& e.message.includes('index_not_found_exception'))

/*
This module is used for connecting to an Elasticsearch instance, writing records,
searching records, and managing the indexes. It looks for the ES_HOST environment
Expand Down Expand Up @@ -327,7 +332,11 @@ async function constructSearchParams(parameters, page, limit) {

async function search(parameters, page = 1, limit = 10) {
const searchParams = await constructSearchParams(parameters, page, limit)
const esResponse = await esQuery(searchParams)
const esResponse = await esQuery({
ignore_unavailable: true,
allow_no_indices: true,
...searchParams
})

const results = esResponse.body.hits.hits.map((r) => (r._source))
const response = {
Expand All @@ -354,6 +363,7 @@ async function search(parameters, page = 1, limit = 10) {
module.exports = {
getCollection,
getCollections,
isIndexNotFoundError,
search,
editPartialItem,
constructSearchParams,
Expand Down
42 changes: 42 additions & 0 deletions tests/system/test-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const { apiClient } = require('../helpers/api-client')
const intersectsGeometry = require('../fixtures/stac/intersectsGeometry.json')
const noIntersectsGeometry = require('../fixtures/stac/noIntersectsGeometry.json')
const { refreshIndices } = require('../helpers/es')
const { randomId } = require('../helpers/utils')

test('/collections', async (t) => {
await refreshIndices()
Expand Down Expand Up @@ -188,6 +189,47 @@ test('/', async (t) => {
t.true(Array.isArray(response.links))
})

test('GET /search returns an empty list of results for a collection that does not exist', async (t) => {
const collectionId = randomId('collection')
const searchParams = new URLSearchParams({ collections: [collectionId] })

const response = await apiClient.get('search', { searchParams })

t.true(Array.isArray(response.features))
t.is(response.features.length, 0)
})

test('POST /search returns an empty list of results for a collection that does not exist', async (t) => {
const response = await apiClient.post('search', {
json: {
collections: [randomId('collection')]
},
searchParams: {
collections: randomId('collection')
}
})

t.true(Array.isArray(response.features))
t.is(response.features.length, 0)
})

test("POST /search returns results when one collection exists and another doesn't", async (t) => {
const response = await apiClient.post('search', {
json: {
collections: [
'collection2',
randomId('collection')
]
},
searchParams: {
collections: randomId('collection')
}
})

t.true(Array.isArray(response.features))
t.true(response.features.length > 0)
})

test.skip('/search bbox', async (t) => {
let response = await apiClient.post('search', {
json: {
Expand Down

0 comments on commit 246ff66

Please sign in to comment.