From 561f5ef1c490c698b5d0c291141758e14af48f04 Mon Sep 17 00:00:00 2001 From: Mykola Yashchenko Date: Mon, 3 Jul 2023 06:04:52 +0300 Subject: [PATCH] feat: add Geo Hex Grid Aggregation (#180) --- .../geo-hex-grid-aggregation.js | 95 +++++++++++++++++++ src/aggregations/bucket-aggregations/index.js | 1 + src/index.d.ts | 70 ++++++++++++++ src/index.js | 4 + .../geo-hex-grid-agg.test.js | 36 +++++++ test/index.test.js | 3 + 6 files changed, 209 insertions(+) create mode 100644 src/aggregations/bucket-aggregations/geo-hex-grid-aggregation.js create mode 100644 test/aggregations-test/geo-hex-grid-agg.test.js diff --git a/src/aggregations/bucket-aggregations/geo-hex-grid-aggregation.js b/src/aggregations/bucket-aggregations/geo-hex-grid-aggregation.js new file mode 100644 index 0000000..9c846e3 --- /dev/null +++ b/src/aggregations/bucket-aggregations/geo-hex-grid-aggregation.js @@ -0,0 +1,95 @@ +'use strict'; + +const isNil = require('lodash.isnil'); + +const BucketAggregationBase = require('./bucket-aggregation-base'); + +const ES_REF_URL = + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohexgrid-aggregation.html'; + +/** + * A multi-bucket aggregation that groups geo_point and geo_shape values into buckets + * that represent a grid. The resulting grid can be sparse and only contains cells + * that have matching data. Each cell corresponds to a H3 cell index and is labeled + * using the H3Index representation. + + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohexgrid-aggregation.html) + * + * NOTE: This aggregation was added in elasticsearch v8.1.0. + * + * @example + * const agg = esb.geoHexGridAggregation('hex-grid', 'location').precision(3); + * + * @param {string} name The name which will be used to refer to this aggregation. + * @param {string=} field The field to aggregate on + * + * @extends BucketAggregationBase + */ +class GeoHexGridAggregation extends BucketAggregationBase { + // eslint-disable-next-line require-jsdoc + constructor(name, field) { + super(name, 'geohex_grid', field); + } + + /** + * @override + * @throws {Error} This method cannot be called on GeoHexGridAggregation + */ + format() { + console.log(`Please refer ${ES_REF_URL}`); + throw new Error('format is not supported in GeoHexGridAggregation'); + } + + /** + * @override + * @throws {Error} This method cannot be called on GeoHexGridAggregation + */ + script() { + console.log(`Please refer ${ES_REF_URL}`); + throw new Error('script is not supported in GeoHexGridAggregation'); + } + + /** + * Sets the precision for the generated geohex. + * + * @param {number} precision Precision can be between 0 and 15 + * @returns {GeoHexGridAggregation} returns `this` so that calls can be chained + * @throws {Error} If precision is not between 0 and 15. + */ + precision(precision) { + if (isNil(precision) || precision < 0 || precision > 15) { + throw new Error('`precision` can only be value from 0 to 15.'); + } + + this._aggsDef.precision = precision; + return this; + } + + /** + * Sets the maximum number of geohex buckets to return. + * When results are trimmed, buckets are prioritised + * based on the volumes of documents they contain. + * + * @param {number} size Optional. The maximum number of geohex + * buckets to return (defaults to 10,000). + * @returns {GeoHexGridAggregation} returns `this` so that calls can be chained + */ + size(size) { + this._aggsDef.size = size; + return this; + } + + /** + * Determines how many geohex_grid the coordinating node + * will request from each shard. + * + * @param {number} shardSize Optional. + * @returns {GeoHexGridAggregation} returns `this` so that calls can be chained + */ + shardSize(shardSize) { + this._aggsDef.shard_size = shardSize; + return this; + } +} + +module.exports = GeoHexGridAggregation; diff --git a/src/aggregations/bucket-aggregations/index.js b/src/aggregations/bucket-aggregations/index.js index bd85216..c73d2b4 100644 --- a/src/aggregations/bucket-aggregations/index.js +++ b/src/aggregations/bucket-aggregations/index.js @@ -18,6 +18,7 @@ exports.FilterAggregation = require('./filter-aggregation'); exports.FiltersAggregation = require('./filters-aggregation'); exports.GeoDistanceAggregation = require('./geo-distance-aggregation'); exports.GeoHashGridAggregation = require('./geo-hash-grid-aggregation'); +exports.GeoHexGridAggregation = require('./geo-hex-grid-aggregation'); exports.GeoTileGridAggregation = require('./geo-tile-grid-aggregation'); exports.GlobalAggregation = require('./global-aggregation'); exports.HistogramAggregation = require('./histogram-aggregation'); diff --git a/src/index.d.ts b/src/index.d.ts index 2c4fa35..394c117 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -5694,6 +5694,76 @@ declare namespace esb { field?: string ): GeoHashGridAggregation; + /** + * A multi-bucket aggregation that groups geo_point and geo_shape values into buckets + * that represent a grid. The resulting grid can be sparse and only contains cells + * that have matching data. Each cell corresponds to a H3 cell index and is labeled + * using the H3Index representation. + * + * NOTE: This aggregation was added in elasticsearch v8.1.0. + * + * @param {string} name The name which will be used to refer to this aggregation. + * @param {string=} field The field to aggregate on + * @extends BucketAggregationBase + */ + export class GeoHexGridAggregation extends BucketAggregationBase { + constructor(name: string, field?: string); + + /** + * @override + * @throws {Error} This method cannot be called on GeoHexGridAggregation + */ + format(): never; + + /** + * @override + * @throws {Error} This method cannot be called on GeoHexGridAggregation + */ + script(): never; + + /** + * Sets the precision for the generated geohex. + * + * @param {number} precision Precision can be between 0 and 15 + * @throws {Error} If precision is not between 0 and 15. + */ + precision(precision: number): this; + + /** + * Sets the maximum number of geohex buckets to return. + * When results are trimmed, buckets are prioritised + * based on the volumes of documents they contain. + * + * @param {number} size Optional. The maximum number of geohex + * buckets to return (defaults to 10,000). + */ + size(size: number): this; + + /** + * Determines how many geohex_grid the coordinating node + * will request from each shard. + * + * @param {number} shardSize Optional. + */ + shardSize(shardSize: number): this; + } + + /** + * A multi-bucket aggregation that groups geo_point and geo_shape values into buckets + * that represent a grid. The resulting grid can be sparse and only contains cells + * that have matching data. Each cell corresponds to a H3 cell index and is labeled + * using the H3Index representation. + * + * NOTE: This aggregation was added in elasticsearch v8.1.0. + * + * @param {string} name The name which will be used to refer to this aggregation. + * @param {string=} field The field to aggregate on + */ + export function geoHexGridAggregation( + name: string, + field?: string + ): GeoHexGridAggregation; + /** * A multi-bucket aggregation that works on geo_point fields and groups points * into buckets that represent cells in a grid. The resulting grid can be sparse diff --git a/src/index.js b/src/index.js index cb0dc4c..1867218 100644 --- a/src/index.js +++ b/src/index.js @@ -116,6 +116,7 @@ const { FiltersAggregation, GeoDistanceAggregation, GeoHashGridAggregation, + GeoHexGridAggregation, GeoTileGridAggregation, GlobalAggregation, HistogramAggregation, @@ -420,6 +421,9 @@ exports.geoDistanceAggregation = constructorWrapper(GeoDistanceAggregation); exports.GeoHashGridAggregation = GeoHashGridAggregation; exports.geoHashGridAggregation = constructorWrapper(GeoHashGridAggregation); +exports.GeoHexGridAggregation = GeoHexGridAggregation; +exports.geoHexGridAggregation = constructorWrapper(GeoHexGridAggregation); + exports.GeoTileGridAggregation = GeoTileGridAggregation; exports.geoTileGridAggregation = constructorWrapper(GeoTileGridAggregation); diff --git a/test/aggregations-test/geo-hex-grid-agg.test.js b/test/aggregations-test/geo-hex-grid-agg.test.js new file mode 100644 index 0000000..ffd345d --- /dev/null +++ b/test/aggregations-test/geo-hex-grid-agg.test.js @@ -0,0 +1,36 @@ +import test from 'ava'; +import { GeoHexGridAggregation } from '../../src'; +import { + illegalCall, + setsAggType, + nameTypeExpectStrategy, + makeSetsOptionMacro +} from '../_macros'; + +const getInstance = () => new GeoHexGridAggregation('my_geo_agg'); + +const setsOption = makeSetsOptionMacro( + getInstance, + nameTypeExpectStrategy('my_geo_agg', 'geohex_grid') +); + +test(setsAggType, GeoHexGridAggregation, 'geohex_grid'); +test(illegalCall, GeoHexGridAggregation, 'format', 'my_agg'); +test(illegalCall, GeoHexGridAggregation, 'script', 'my_agg'); +test(setsOption, 'precision', { param: 8 }); +test(setsOption, 'size', { param: 10000 }); +test(setsOption, 'shardSize', { param: 3 }); + +test('precision correctly validated', t => { + let err = t.throws(() => getInstance().precision(-1), Error); + t.is(err.message, '`precision` can only be value from 0 to 15.'); + + err = t.throws(() => getInstance().precision(16), Error); + t.is(err.message, '`precision` can only be value from 0 to 15.'); + + err = t.throws(() => getInstance().precision(null), Error); + t.is(err.message, '`precision` can only be value from 0 to 15.'); + + err = t.throws(() => getInstance().precision(undefined), Error); + t.is(err.message, '`precision` can only be value from 0 to 15.'); +}); diff --git a/test/index.test.js b/test/index.test.js index 4c7ca5e..87514b2 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -256,6 +256,9 @@ test('aggregations are exported', t => { t.truthy(esb.GeoHashGridAggregation); t.truthy(esb.geoHashGridAggregation); + t.truthy(esb.GeoHexGridAggregation); + t.truthy(esb.geoHexGridAggregation); + t.truthy(esb.GeoTileGridAggregation); t.truthy(esb.geoTileGridAggregation);