From c42f7ac1cd098a0621d74e7253e4f762a9ec3c9b Mon Sep 17 00:00:00 2001 From: Nestor Carvantes Date: Thu, 22 Apr 2021 00:48:27 -0700 Subject: [PATCH] feat: support Period type fields for date params --- .../typeQueries/common/prefixRange.ts | 103 +++++++- .../typeQueries/dateQuery.test.ts | 242 +++++++++++++++--- .../elasticSearchService.test.ts.snap | 21 +- 3 files changed, 317 insertions(+), 49 deletions(-) diff --git a/src/QueryBuilder/typeQueries/common/prefixRange.ts b/src/QueryBuilder/typeQueries/common/prefixRange.ts index 3f13ed6..365820d 100644 --- a/src/QueryBuilder/typeQueries/common/prefixRange.ts +++ b/src/QueryBuilder/typeQueries/common/prefixRange.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { InvalidSearchParameterError } from 'fhir-works-on-aws-interface'; - interface Range { start: Date | number; end: Date | number; @@ -20,6 +18,91 @@ interface DateRange { end: Date; } +// See the interpretation of prefixes when applied to ranges: https://www.hl7.org/fhir/search.html#prefix. It can be counterintuitive at first +const prefixRangePeriod = (prefix: string, range: Range, periodTypeField: string): any => { + const { start, end } = range; + const startField = `${periodTypeField}.start`; + const endField = `${periodTypeField}.end`; + + const eqQuery = { + bool: { + must: [ + { + range: { + [startField]: { + gte: start, + }, + }, + }, + { + range: { + [endField]: { + lte: end, + }, + }, + }, + ], + }, + }; + + const saQuery = { + range: { + [startField]: { + gt: end, + }, + }, + }; + const ebQuery = { + range: { + [endField]: { + lt: start, + }, + }, + }; + + switch (prefix) { + case 'eq': + return eqQuery; + case 'ne': + return { + bool: { + must_not: eqQuery, + }, + }; + case 'lt': + case 'le': + return { + range: { + [startField]: { + lte: end, + }, + }, + }; + case 'gt': + case 'ge': + return { + range: { + [endField]: { + gte: start, + }, + }, + }; + case 'eb': + return ebQuery; + case 'sa': + return saQuery; + case 'ap': + return { + bool: { + must_not: [ebQuery, saQuery], + }, + }; + default: + // this should never happen + throw new Error(`unknown search prefix: ${prefix}`); + } +}; + const prefixRange = (prefix: string, range: Range, path: string): any => { const { start, end } = range; @@ -87,7 +170,12 @@ const prefixRange = (prefix: string, range: Range, path: string): any => { }; break; case 'ap': // approximately - throw new InvalidSearchParameterError('Unsupported prefix: ap'); + elasticSearchRange = { + // same as eq for now + gte: start, + lte: end, + }; + break; default: // this should never happen throw new Error(`unknown search prefix: ${prefix}`); @@ -116,5 +204,12 @@ export const prefixRangeNumber = (prefix: string, number: number, implicitRange: }; export const prefixRangeDate = (prefix: string, range: DateRange, path: string): any => { - return prefixRange(prefix, range, path); + return { + bool: { + should: [ + prefixRange(prefix, range, path), // date, dateTime, instant + prefixRangePeriod(prefix, range, path), // Period + ], + }, + }; }; diff --git a/src/QueryBuilder/typeQueries/dateQuery.test.ts b/src/QueryBuilder/typeQueries/dateQuery.test.ts index 173ef6b..368bb87 100644 --- a/src/QueryBuilder/typeQueries/dateQuery.test.ts +++ b/src/QueryBuilder/typeQueries/dateQuery.test.ts @@ -122,11 +122,37 @@ describe('dateQuery', () => { test('no prefix', () => { expect(dateQuery(birthdateParam, '1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "gte": 1999-09-09T00:00:00.000Z, - "lte": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gte": 1999-09-09T00:00:00.000Z, + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "range": Object { + "birthDate.start": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], + }, + }, + ], }, } `); @@ -134,11 +160,37 @@ describe('dateQuery', () => { test('eq', () => { expect(dateQuery(birthdateParam, 'eq1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "gte": 1999-09-09T00:00:00.000Z, - "lte": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gte": 1999-09-09T00:00:00.000Z, + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "bool": Object { + "must": Array [ + Object { + "range": Object { + "birthDate.start": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], + }, + }, + ], }, } `); @@ -149,16 +201,46 @@ describe('dateQuery', () => { "bool": Object { "should": Array [ Object { - "range": Object { - "birthDate": Object { - "gt": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gt": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "range": Object { + "birthDate": Object { + "lt": 1999-09-09T00:00:00.000Z, + }, + }, + }, + ], }, }, Object { - "range": Object { - "birthDate": Object { - "lt": 1999-09-09T00:00:00.000Z, + "bool": Object { + "must_not": Object { + "bool": Object { + "must": Array [ + Object { + "range": Object { + "birthDate.start": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], + }, }, }, }, @@ -170,10 +252,23 @@ describe('dateQuery', () => { test('lt', () => { expect(dateQuery(birthdateParam, 'lt1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "lt": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "lt": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.start": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], }, } `); @@ -181,10 +276,23 @@ describe('dateQuery', () => { test('le', () => { expect(dateQuery(birthdateParam, 'le1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "lte": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.start": Object { + "lte": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], }, } `); @@ -192,10 +300,23 @@ describe('dateQuery', () => { test('gt', () => { expect(dateQuery(birthdateParam, 'gt1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "gt": 1999-09-09T00:00:00.000Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gt": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + ], }, } `); @@ -203,10 +324,23 @@ describe('dateQuery', () => { test('ge', () => { expect(dateQuery(birthdateParam, 'ge1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "gte": 1999-09-09T00:00:00.000Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "gte": 1999-09-09T00:00:00.000Z, + }, + }, + }, + ], }, } `); @@ -214,10 +348,23 @@ describe('dateQuery', () => { test('sa', () => { expect(dateQuery(birthdateParam, 'sa1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "gt": 1999-09-09T23:59:59.999Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gt": 1999-09-09T23:59:59.999Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.start": Object { + "gt": 1999-09-09T23:59:59.999Z, + }, + }, + }, + ], }, } `); @@ -225,10 +372,23 @@ describe('dateQuery', () => { test('eb', () => { expect(dateQuery(birthdateParam, 'eb1999-09-09')).toMatchInlineSnapshot(` Object { - "range": Object { - "birthDate": Object { - "lt": 1999-09-09T00:00:00.000Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "lt": 1999-09-09T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "lt": 1999-09-09T00:00:00.000Z, + }, + }, + }, + ], }, } `); diff --git a/src/__snapshots__/elasticSearchService.test.ts.snap b/src/__snapshots__/elasticSearchService.test.ts.snap index 6cb1fb8..fe5912d 100644 --- a/src/__snapshots__/elasticSearchService.test.ts.snap +++ b/src/__snapshots__/elasticSearchService.test.ts.snap @@ -1587,10 +1587,23 @@ Array [ }, }, Object { - "range": Object { - "birthDate": Object { - "gt": 1990-01-01T00:00:00.000Z, - }, + "bool": Object { + "should": Array [ + Object { + "range": Object { + "birthDate": Object { + "gt": 1990-01-01T00:00:00.000Z, + }, + }, + }, + Object { + "range": Object { + "birthDate.end": Object { + "gte": 1990-01-01T00:00:00.000Z, + }, + }, + }, + ], }, }, ],