mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-10 03:32:52 +00:00
refactor: simplify geo queries
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -536,9 +536,9 @@
|
||||
}
|
||||
},
|
||||
"@openstapps/core": {
|
||||
"version": "0.65.1",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.65.1.tgz",
|
||||
"integrity": "sha512-ZffFN3bRMC4eq96KSOnZScfTdx/ioiDuNLVrm0azqBqz+PKncknO65k+hoAClHi8BOlvZc2bPk4/19suas7PQQ==",
|
||||
"version": "0.66.0",
|
||||
"resolved": "https://registry.npmjs.org/@openstapps/core/-/core-0.66.0.tgz",
|
||||
"integrity": "sha512-79oNo8TLjs/oaq/MzcPQx2xiM89kOI4bjV8sQakycmkuke3cI6sdhAaRSAYCNsjCz74VIx/8ht01zKx3jll+Cg==",
|
||||
"requires": {
|
||||
"@openstapps/core-tools": "0.30.0",
|
||||
"@types/geojson": "1.0.6",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "5.6.22",
|
||||
"@openstapps/core": "0.65.1",
|
||||
"@openstapps/core": "0.66.0",
|
||||
"@openstapps/core-tools": "0.30.0",
|
||||
"@openstapps/logger": "0.8.0",
|
||||
"@types/express-prometheus-middleware": "1.2.1",
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
ESFunctionScoreQuery,
|
||||
ESFunctionScoreQueryFunction,
|
||||
ESGenericRange,
|
||||
ESGenericSort,
|
||||
ESGenericSort, ESGeoBoundingBoxFilter,
|
||||
ESGeoDistanceFilter,
|
||||
ESGeoDistanceFilterArguments,
|
||||
ESGeoDistanceSort,
|
||||
@@ -96,7 +96,7 @@ export function buildBooleanFilter(booleanFilter: SCSearchBooleanFilter): ESBool
|
||||
* @param filter A search filter for the retrieval of the data
|
||||
*/
|
||||
export function buildFilter(filter: SCSearchFilter):
|
||||
ESTermFilter | ESGeoDistanceFilter | ESGeoShapeFilter | ESBooleanFilter<unknown> | ESRangeFilter {
|
||||
ESTermFilter | ESGeoDistanceFilter | ESBooleanFilter<ESGeoShapeFilter | ESGeoBoundingBoxFilter> | ESGeoShapeFilter | ESBooleanFilter<unknown> | ESRangeFilter {
|
||||
|
||||
switch (filter.type) {
|
||||
case 'value':
|
||||
@@ -125,10 +125,10 @@ export function buildFilter(filter: SCSearchFilter):
|
||||
case 'distance':
|
||||
const geoObject: ESGeoDistanceFilterArguments = {
|
||||
distance: `${filter.arguments.distance}m`,
|
||||
};
|
||||
geoObject[filter.arguments.field] = {
|
||||
lat: filter.arguments.position[1],
|
||||
lon: filter.arguments.position[0],
|
||||
[`${filter.arguments.field}.point.coordinates`]: {
|
||||
lat: filter.arguments.position[1],
|
||||
lon: filter.arguments.position[0],
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -179,12 +179,50 @@ export function buildFilter(filter: SCSearchFilter):
|
||||
|
||||
return dateRangeFilter;
|
||||
case 'geo':
|
||||
return {
|
||||
[filter.arguments.field]: {
|
||||
shape: filter.arguments.shape,
|
||||
relation: filter.arguments.spatialRelation,
|
||||
// TODO: on ES upgrade, use just geo_shape filters
|
||||
const geoShapeFilter: ESGeoShapeFilter = {
|
||||
geo_shape: {
|
||||
/**
|
||||
* https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-shape-query.html#_ignore_unmapped_3
|
||||
*/
|
||||
// tslint:disable-next-line:ban-ts-ignore
|
||||
// @ts-ignore unfortunately, typescript is stupid and won't allow me to map this to an actual type.
|
||||
ignore_unmapped: true,
|
||||
[`${filter.arguments.field}.polygon`]: {
|
||||
shape: filter.arguments.shape,
|
||||
relation: filter.arguments.spatialRelation,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if ((typeof filter.arguments.spatialRelation === 'undefined' || filter.arguments.spatialRelation === 'intersects')
|
||||
&& filter.arguments.shape.type === 'envelope'
|
||||
) {
|
||||
return {
|
||||
bool: {
|
||||
minimum_should_match: 1,
|
||||
should: [
|
||||
geoShapeFilter,
|
||||
{
|
||||
geo_bounding_box: {
|
||||
/**
|
||||
* https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-shape-query.html#_ignore_unmapped_3
|
||||
*/
|
||||
// tslint:disable-next-line:ban-ts-ignore
|
||||
// @ts-ignore unfortunately, typescript is stupid and won't allow me to map this to an actual type.
|
||||
ignore_unmapped: true,
|
||||
[`${filter.arguments.field}.point.coordinates`]: {
|
||||
bottom_right: filter.arguments.shape.coordinates[0],
|
||||
top_left: filter.arguments.shape.coordinates[1],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return geoShapeFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,7 +433,7 @@ export function buildSort(
|
||||
unit: 'm',
|
||||
};
|
||||
|
||||
args[sort.arguments.field] = {
|
||||
args[`${sort.arguments.field}.point.coordinates`] = {
|
||||
lat: sort.arguments.position[1],
|
||||
lon: sort.arguments.position[0],
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ import {SCThing, SCThingType} from '@openstapps/core';
|
||||
// tslint:disable-next-line:no-implicit-dependencies
|
||||
import {NameList} from 'elasticsearch';
|
||||
// tslint:disable-next-line:no-implicit-dependencies
|
||||
import {Polygon} from 'geojson';
|
||||
import {Polygon, Position} from 'geojson';
|
||||
|
||||
/**
|
||||
* An elasticsearch aggregation bucket
|
||||
@@ -366,23 +366,67 @@ export interface ESGeoDistanceFilter {
|
||||
geo_distance: ESGeoDistanceFilterArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* A rectangular geo shape, representing the top-left and bottom-right corners
|
||||
*
|
||||
* This is an extension of the Geojson type
|
||||
* http://geojson.org/geojson-spec.html
|
||||
*/
|
||||
export interface ESEnvelope {
|
||||
/**
|
||||
* The top-left and bottom-right corners of the bounding box
|
||||
*/
|
||||
coordinates: [Position, Position];
|
||||
|
||||
/**
|
||||
* The type of the geometry
|
||||
*/
|
||||
type: 'envelope';
|
||||
}
|
||||
|
||||
/**
|
||||
* An Elasticsearch geo bounding box filter
|
||||
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-bounding-box-query.html
|
||||
*/
|
||||
export interface ESGeoBoundingBoxFilter {
|
||||
/**
|
||||
* An Elasticsearch geo bounding box filter
|
||||
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-bounding-box-query.html
|
||||
*/
|
||||
geo_bounding_box: {
|
||||
[fieldName: string]: {
|
||||
/**
|
||||
* Geo Shape
|
||||
*/
|
||||
bottom_right: Position;
|
||||
|
||||
/**
|
||||
* Geo Shape
|
||||
*/
|
||||
top_left: Position;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* An Elasticsearch geo shape filter
|
||||
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-shape-query.html
|
||||
*/
|
||||
export interface ESGeoShapeFilter {
|
||||
[fieldName: string]: {
|
||||
/**
|
||||
* Relation of the two shapes
|
||||
*
|
||||
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-shape-query.html#_spatial_relations
|
||||
*/
|
||||
relation?: 'intersects' | 'disjoint' | 'within' | 'contains';
|
||||
geo_shape: {
|
||||
[fieldName: string]: {
|
||||
/**
|
||||
* Relation of the two shapes
|
||||
*
|
||||
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-geo-shape-query.html#_spatial_relations
|
||||
*/
|
||||
relation?: 'intersects' | 'disjoint' | 'within' | 'contains';
|
||||
|
||||
/**
|
||||
* Geo Shape
|
||||
*/
|
||||
shape: Polygon;
|
||||
/**
|
||||
* Geo Shape
|
||||
*/
|
||||
shape: Polygon | ESEnvelope;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
ESGeoDistanceFilter,
|
||||
ESGeoDistanceSort,
|
||||
ESTermFilter,
|
||||
ScriptSort
|
||||
ScriptSort,
|
||||
} from '../../../src/storage/elasticsearch/types/elasticsearch';
|
||||
import {configFile} from '../../../src/common';
|
||||
import {buildBooleanFilter, buildFilter, buildQuery, buildSort} from '../../../src/storage/elasticsearch/query';
|
||||
@@ -205,10 +205,37 @@ describe('Query', function () {
|
||||
type: 'distance',
|
||||
arguments: {
|
||||
distance: 1000,
|
||||
field: 'geo.point.coordinates',
|
||||
field: 'geo',
|
||||
position: [50.123, 8.123],
|
||||
}
|
||||
},
|
||||
geoPoint: {
|
||||
type: 'geo',
|
||||
arguments: {
|
||||
field: 'geo',
|
||||
shape: {
|
||||
type: 'envelope',
|
||||
coordinates: [
|
||||
[50.123, 8.123],
|
||||
[50.123, 8.123],
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
geoShape: {
|
||||
type: 'geo',
|
||||
arguments: {
|
||||
field: 'geo',
|
||||
spatialRelation: 'contains',
|
||||
shape: {
|
||||
type: 'envelope',
|
||||
coordinates: [
|
||||
[50.123, 8.123],
|
||||
[50.123, 8.123],
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
boolean: {
|
||||
type: 'boolean',
|
||||
arguments: {
|
||||
@@ -447,6 +474,64 @@ describe('Query', function () {
|
||||
expect(filter).to.be.eql(expectedFilter);
|
||||
});
|
||||
|
||||
it('should build geo filter for shapes and points', function () {
|
||||
const filter = buildFilter(searchFilters.geoPoint);
|
||||
const expectedFilter = {
|
||||
bool: {
|
||||
minimum_should_match: 1,
|
||||
should: [
|
||||
{
|
||||
geo_shape: {
|
||||
'geo.polygon': {
|
||||
relation: undefined,
|
||||
shape: {
|
||||
type: 'envelope',
|
||||
coordinates: [
|
||||
[50.123, 8.123],
|
||||
[50.123, 8.123]
|
||||
]
|
||||
},
|
||||
},
|
||||
ignore_unmapped: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
geo_bounding_box: {
|
||||
'geo.point.coordinates': {
|
||||
bottom_right: [50.123, 8.123],
|
||||
top_left: [50.123, 8.123]
|
||||
},
|
||||
ignore_unmapped: true,
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
expect(filter).to.be.eql(expectedFilter);
|
||||
});
|
||||
|
||||
it('should build geo filter for shapes only', function () {
|
||||
const filter = buildFilter(searchFilters.geoShape);
|
||||
const expectedFilter = {
|
||||
geo_shape: {
|
||||
'geo.polygon': {
|
||||
relation: 'contains',
|
||||
shape: {
|
||||
type: 'envelope',
|
||||
coordinates: [
|
||||
[50.123, 8.123],
|
||||
[50.123, 8.123]
|
||||
]
|
||||
},
|
||||
},
|
||||
ignore_unmapped: true,
|
||||
}
|
||||
};
|
||||
|
||||
expect(filter).to.be.eql(expectedFilter);
|
||||
});
|
||||
|
||||
it('should build boolean filter', function () {
|
||||
const filter = buildFilter(searchFilters.boolean);
|
||||
const expectedFilter: ESBooleanFilter<any> = {
|
||||
@@ -497,7 +582,7 @@ describe('Query', function () {
|
||||
type: 'distance',
|
||||
order: 'desc',
|
||||
arguments: {
|
||||
field: 'geo.point',
|
||||
field: 'geo',
|
||||
position: [8.123, 50.123]
|
||||
},
|
||||
},
|
||||
@@ -523,7 +608,7 @@ describe('Query', function () {
|
||||
mode: 'avg',
|
||||
order: 'desc',
|
||||
unit: 'm',
|
||||
'geo.point': {
|
||||
'geo.point.coordinates': {
|
||||
lat: 50.123,
|
||||
lon: 8.123
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user