Skip to content

Commit 51f7698

Browse files
author
Phil Varner
committed
add support for Query Extension neq, startsWith, endsWith, and contains
1 parent 541a485 commit 51f7698

File tree

3 files changed

+139
-10
lines changed

3 files changed

+139
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
- Publish ingest results to a post-ingest SNS topic
1313
- Add datetime and bbox attributes to post-ingest SNS messages
14+
- Support for Query Extension operators neq, startsWith, endsWith, and contains.
1415

1516
### Changed
1617

src/lib/database.js

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,36 +74,99 @@ export function buildDatetimeQuery(parameters) {
7474

7575
function buildQuery(parameters) {
7676
const eq = 'eq'
77+
const neq = 'neq'
7778
const inop = 'in'
79+
const startsWith = 'startsWith'
80+
const endsWith = 'endsWith'
81+
const contains = 'contains'
82+
7883
const { query, intersects, collections, ids } = parameters
84+
7985
let filterQueries = []
86+
let mustNotQueries = []
87+
8088
if (query) {
8189
// Using reduce rather than map as we don't currently support all
8290
// stac query operators.
8391
filterQueries = Object.keys(query).reduce((accumulator, property) => {
8492
const operatorsObject = query[property]
8593
const operators = Object.keys(operatorsObject)
94+
95+
// eq
8696
if (operators.includes(eq)) {
87-
const termQuery = {
97+
accumulator.push({
8898
term: {
8999
[`properties.${property}`]: operatorsObject.eq
90100
}
91-
}
92-
accumulator.push(termQuery)
93-
} else if (operators.includes(inop)) {
94-
const termsQuery = {
101+
})
102+
}
103+
104+
// in
105+
if (operators.includes(inop)) {
106+
accumulator.push({
95107
terms: {
96108
[`properties.${property}`]: operatorsObject.in
97109
}
98-
}
99-
accumulator.push(termsQuery)
110+
})
111+
}
112+
113+
// startsWith
114+
if (operators.includes(startsWith)) {
115+
accumulator.push({
116+
prefix: {
117+
[`properties.${property}`]: {
118+
value: operatorsObject.startsWith
119+
}
120+
}
121+
})
122+
}
123+
124+
// endsWith
125+
if (operators.includes(endsWith)) {
126+
accumulator.push({
127+
wildcard: {
128+
[`properties.${property}`]: {
129+
value: `*${operatorsObject.endsWith}`
130+
}
131+
}
132+
})
133+
}
134+
135+
// contains
136+
if (operators.includes(contains)) {
137+
accumulator.push({
138+
wildcard: {
139+
[`properties.${property}`]: {
140+
value: `*${operatorsObject.contains}*`
141+
}
142+
}
143+
})
100144
}
145+
146+
// lt, lte, gt, gte
101147
const rangeQuery = buildRangeQuery(property, operators, operatorsObject)
102148
if (rangeQuery) {
103149
accumulator.push(rangeQuery)
104150
}
151+
105152
return accumulator
106153
}, filterQueries)
154+
155+
mustNotQueries = Object.keys(query).reduce((accumulator, property) => {
156+
const operatorsObject = query[property]
157+
const operators = Object.keys(operatorsObject)
158+
159+
// neq
160+
if (operators.includes(neq)) {
161+
accumulator.push({
162+
term: {
163+
[`properties.${property}`]: operatorsObject.neq
164+
}
165+
})
166+
}
167+
168+
return accumulator
169+
}, mustNotQueries)
107170
}
108171

109172
if (ids) {
@@ -133,15 +196,14 @@ function buildQuery(parameters) {
133196
const datetimeQuery = buildDatetimeQuery(parameters)
134197
if (datetimeQuery instanceof Error) {
135198
throw datetimeQuery
136-
}
137-
138-
if (datetimeQuery) {
199+
} else if (datetimeQuery) {
139200
filterQueries.push(datetimeQuery)
140201
}
141202

142203
return {
143204
query: {
144205
bool: {
206+
must_not: mustNotQueries,
145207
filter: filterQueries
146208
}
147209
}

tests/system/test-api-search-post.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,3 +447,69 @@ test('/search preserve bbox in prev and next links', async (t) => {
447447
t.is(linkRel(response, 'next').body.datetime, datetime)
448448
t.deepEqual(linkRel(response, 'next').body.bbox, bbox)
449449
})
450+
451+
test('/search Query Extension', async (t) => {
452+
let response = null
453+
454+
// 3 items, 2 with platform landsat-8, 1 with platform2
455+
456+
response = await t.context.api.client.post('search', {
457+
json: {}
458+
})
459+
t.is(response.features.length, 3)
460+
461+
response = await t.context.api.client.post('search', {
462+
json: {
463+
query: {
464+
platform: {
465+
eq: 'landsat-8'
466+
}
467+
}
468+
}
469+
})
470+
t.is(response.features.length, 2)
471+
472+
response = await t.context.api.client.post('search', {
473+
json: {
474+
query: {
475+
platform: {
476+
neq: 'landsat-8'
477+
}
478+
}
479+
}
480+
})
481+
t.is(response.features.length, 1)
482+
483+
response = await t.context.api.client.post('search', {
484+
json: {
485+
query: {
486+
platform: {
487+
startsWith: 'land'
488+
}
489+
}
490+
}
491+
})
492+
t.is(response.features.length, 2)
493+
494+
response = await t.context.api.client.post('search', {
495+
json: {
496+
query: {
497+
platform: {
498+
endsWith: '-8'
499+
}
500+
}
501+
}
502+
})
503+
t.is(response.features.length, 2)
504+
505+
response = await t.context.api.client.post('search', {
506+
json: {
507+
query: {
508+
platform: {
509+
contains: 'ndsa'
510+
}
511+
}
512+
}
513+
})
514+
t.is(response.features.length, 2)
515+
})

0 commit comments

Comments
 (0)