From 9ef6622ad561abcb61ce6838b7b099d983d647f7 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:00:36 -0700 Subject: [PATCH] [Backport 2.x] [Geospatial] Add new geo shape filter field to support geospatial search query (#3681) * Add geo shape filter field to supported filter formats (#3605) Add new filter query, 'geo_shape' to search geospatial field types . This new filter query will replace geo_polygon query later. With this fiter, dashboard can perform spatial relationssips with shape or predefined shape from an index against an index. Signed-off-by: Vijayan Balasubramanian (cherry picked from commit 71eb3f280e35d2c8573fa0caa4e38f242f8a2e8c) Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: Josh Romero Co-authored-by: Anan Zhuang --- CHANGELOG.md | 1 + .../filters/geo_shape_filter.test.ts | 67 +++++++++++++++++++ .../filters/geo_shape_filter.ts | 53 +++++++++++++++ .../filters/get_filter_field.ts | 4 ++ .../common/opensearch_query/filters/index.ts | 1 + .../common/opensearch_query/filters/types.ts | 1 + 6 files changed, 127 insertions(+) create mode 100644 src/plugins/data/common/opensearch_query/filters/geo_shape_filter.test.ts create mode 100644 src/plugins/data/common/opensearch_query/filters/geo_shape_filter.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 851b5ee5fd1d..f42bd752ca24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 📈 Features/Enhancements - [Monaco editor] Add json worker support ([#3424](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3424)) +- [Data] Add geo shape filter field ([#3605](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3605)) ### 🐛 Bug Fixes diff --git a/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.test.ts b/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.test.ts new file mode 100644 index 000000000000..18a642fdbb4f --- /dev/null +++ b/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { GeoShapeFilter, getGeoShapeFilterField, Polygon, ShapeFilter } from './geo_shape_filter'; +import { GeoShapeRelation } from '@opensearch-project/opensearch/api/types'; + +describe('geo shape filter', function () { + describe('getGeoShapeFilterField', function () { + it('should return the name of the field a geo_shape query is targeting', () => { + const polygon: Polygon = { + coordinates: [ + [ + [74.006, 40.7128], + [71.0589, 42.3601], + [73.7562, 42.6526], + [74.006, 40.7128], + ], + [ + [72.6734, 41.7658], + [72.6506, 41.5623], + [73.0515, 41.5582], + [72.6734, 41.7658], + ], + ], + type: 'Polygon', + }; + const geoShapeQuery: { + shape: ShapeFilter; + relation: GeoShapeRelation; + } = { + shape: polygon, + relation: 'intersects', + }; + const filter: GeoShapeFilter = { + geo_shape: { + geoPointField: geoShapeQuery, + ignore_unmapped: true, + }, + meta: { + disabled: false, + negate: false, + alias: null, + params: geoShapeQuery, + }, + }; + const result = getGeoShapeFilterField(filter); + expect(result).toBe('geoPointField'); + }); + it('should return undefined if filter.geo_shape is undefined', () => { + const filter: GeoShapeFilter = { + geo_shape: undefined, + meta: { + disabled: false, + negate: false, + alias: null, + params: { + shape: undefined, + }, + }, + }; + const result = getGeoShapeFilterField(filter); + expect(result).toBeUndefined(); + }); + }); +}); diff --git a/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.ts b/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.ts new file mode 100644 index 000000000000..007a42e31362 --- /dev/null +++ b/src/plugins/data/common/opensearch_query/filters/geo_shape_filter.ts @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { GeoShapeRelation } from '@opensearch-project/opensearch/api/types'; +import { Filter, FilterMeta } from './meta_filter'; + +export type Position = number[]; + +export interface PreIndexedShapeFilter { + index: string; + id: string; + path: string; + routing?: string; +} + +export interface Polygon { + type: 'Polygon'; + coordinates: Position[][]; +} + +export interface MultiPolygon { + type: 'MultiPolygon'; + coordinates: Position[][][]; +} + +// TODO: support other geometries too. +export type ShapeFilter = Polygon | MultiPolygon; + +export type GeoShapeFilterMeta = FilterMeta & { + params: { + shape?: ShapeFilter; + indexed_shape?: PreIndexedShapeFilter; + relation?: GeoShapeRelation; + }; +}; + +export type GeoShapeFilter = Filter & { + meta: GeoShapeFilterMeta; + geo_shape: any; +}; + +export const isGeoShapeFilter = (filter: any): filter is GeoShapeFilter => filter?.geo_shape; + +export const getGeoShapeFilterField = (filter: GeoShapeFilter): string | undefined => { + if (filter?.geo_shape === undefined) { + return undefined; + } + return ( + filter?.geo_shape && Object.keys(filter.geo_shape).find((key) => key !== 'ignore_unmapped') + ); +}; diff --git a/src/plugins/data/common/opensearch_query/filters/get_filter_field.ts b/src/plugins/data/common/opensearch_query/filters/get_filter_field.ts index dff6866b4917..9f44e658c80f 100644 --- a/src/plugins/data/common/opensearch_query/filters/get_filter_field.ts +++ b/src/plugins/data/common/opensearch_query/filters/get_filter_field.ts @@ -36,6 +36,7 @@ import { getPhraseFilterField, isPhraseFilter } from './phrase_filter'; import { getPhrasesFilterField, isPhrasesFilter } from './phrases_filter'; import { getRangeFilterField, isRangeFilter } from './range_filter'; import { getMissingFilterField, isMissingFilter } from './missing_filter'; +import { getGeoShapeFilterField, isGeoShapeFilter } from './geo_shape_filter'; export const getFilterField = (filter: Filter) => { if (isExistsFilter(filter)) { @@ -59,6 +60,9 @@ export const getFilterField = (filter: Filter) => { if (isMissingFilter(filter)) { return getMissingFilterField(filter); } + if (isGeoShapeFilter(filter)) { + return getGeoShapeFilterField(filter); + } return; }; diff --git a/src/plugins/data/common/opensearch_query/filters/index.ts b/src/plugins/data/common/opensearch_query/filters/index.ts index ac11b067c3cb..f098767540d6 100644 --- a/src/plugins/data/common/opensearch_query/filters/index.ts +++ b/src/plugins/data/common/opensearch_query/filters/index.ts @@ -35,6 +35,7 @@ export * from './build_filters'; export * from './custom_filter'; export * from './exists_filter'; export * from './geo_bounding_box_filter'; +export * from './geo_shape_filter'; export * from './geo_polygon_filter'; export * from './get_display_value'; export * from './get_filter_field'; diff --git a/src/plugins/data/common/opensearch_query/filters/types.ts b/src/plugins/data/common/opensearch_query/filters/types.ts index e467cb2d87e2..c0afd31134b1 100644 --- a/src/plugins/data/common/opensearch_query/filters/types.ts +++ b/src/plugins/data/common/opensearch_query/filters/types.ts @@ -59,5 +59,6 @@ export enum FILTERS { RANGE = 'range', GEO_BOUNDING_BOX = 'geo_bounding_box', GEO_POLYGON = 'geo_polygon', + GEO_SHAPE = 'geo_shape', SPATIAL_FILTER = 'spatial_filter', }