From d659f17f0de30d99fdc5693c37d764f0a4d662c4 Mon Sep 17 00:00:00 2001 From: rccoe Date: Wed, 23 Dec 2020 01:10:41 -0500 Subject: [PATCH] feat: Add ParentAggregation (#129) Parent aggregation was added in Elasticsearch v6.6 --- docs/documentation.yml | 1 + src/aggregations/bucket-aggregations/index.js | 1 + .../bucket-aggregations/parent-aggregation.js | 79 +++++++++++++++++++ src/index.d.ts | 41 ++++++++++ src/index.js | 4 + test/aggregations-test/parent-agg.test.js | 31 ++++++++ test/index.test.js | 3 + 7 files changed, 160 insertions(+) create mode 100644 src/aggregations/bucket-aggregations/parent-aggregation.js create mode 100644 test/aggregations-test/parent-agg.test.js diff --git a/docs/documentation.yml b/docs/documentation.yml index f8ab6375..1a2b4eb3 100644 --- a/docs/documentation.yml +++ b/docs/documentation.yml @@ -122,6 +122,7 @@ toc: - IpRangeAggregation - MissingAggregation - NestedAggregation + - ParentAggregation - RangeAggregation - ReverseNestedAggregation - SamplerAggregation diff --git a/src/aggregations/bucket-aggregations/index.js b/src/aggregations/bucket-aggregations/index.js index ee52b7ab..e1a16b96 100644 --- a/src/aggregations/bucket-aggregations/index.js +++ b/src/aggregations/bucket-aggregations/index.js @@ -23,6 +23,7 @@ exports.HistogramAggregation = require('./histogram-aggregation'); exports.IpRangeAggregation = require('./ip-range-aggregation'); exports.MissingAggregation = require('./missing-aggregation'); exports.NestedAggregation = require('./nested-aggregation'); +exports.ParentAggregation = require('./parent-aggregation'); exports.RangeAggregation = require('./range-aggregation'); exports.ReverseNestedAggregation = require('./reverse-nested-aggregation'); exports.SamplerAggregation = require('./sampler-aggregation'); diff --git a/src/aggregations/bucket-aggregations/parent-aggregation.js b/src/aggregations/bucket-aggregations/parent-aggregation.js new file mode 100644 index 00000000..0eeb02bf --- /dev/null +++ b/src/aggregations/bucket-aggregations/parent-aggregation.js @@ -0,0 +1,79 @@ +'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-parent-aggregation.html'; + +/** + * A special single bucket aggregation that enables aggregating + * from buckets on child document types to buckets on parent documents. + * + * This aggregation relies on the `_parent` field in the mapping. + * + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-parent-aggregation.html) + * + * @example + * const reqBody = esb.requestBodySearch() + * .agg( + * esb.termsAggregation('top-names', 'owner.display_name.keyword') + * .size(10) + * .agg( + * esb.parentAggregation('to-questions') + * .type('answer') + * .agg( + * esb.termsAggregation( + * 'top-tags', + * 'tags.keyword' + * ).size(10) + * ) + * ) + * ) + * .size(0); + * + * @param {string} name The name which will be used to refer to this aggregation. + * @param {string=} type The type of the child document. + * + * @extends BucketAggregationBase + */ +class ParentAggregation extends BucketAggregationBase { + // eslint-disable-next-line require-jsdoc + constructor(name, type) { + super(name, 'parent'); + + if (!isNil(type)) this.type(type); + } + + /** + * @override + * @throws {Error} This method cannot be called on ParentAggregation + */ + field() { + console.log(`Please refer ${ES_REF_URL}`); + throw new Error('field is not supported in ParentAggregation'); + } + + /** + * @override + * @throws {Error} This method cannot be called on ParentAggregation + */ + script() { + console.log(`Please refer ${ES_REF_URL}`); + throw new Error('script is not supported in ParentAggregation'); + } + + /** + * Sets the child type/mapping for aggregation. + * + * @param {string} type The child type that the buckets in the parent space should be mapped to. + * @returns {ParentAggregation} returns `this` so that calls can be chained + */ + type(type) { + this._aggsDef.type = type; + return this; + } +} + +module.exports = ParentAggregation; diff --git a/src/index.d.ts b/src/index.d.ts index 1d1857dd..1767dcc6 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -5812,6 +5812,47 @@ declare namespace esb { path?: string ): NestedAggregation; + /** + * A special single bucket aggregation that enables aggregating + * from buckets on child document types to buckets on parent documents. + * This aggregation relies on the `_parent` field in the mapping. + * + * NOTE: This query was added in elasticsearch v6.6. + * + * @param {string} name The name which will be used to refer to this aggregation. + * @extends BucketAggregationBase + */ + export class ParentAggregation extends BucketAggregationBase { + constructor(name: string); + /** + * @override + * @throws {Error} This method cannot be called on ParentAggregation + */ + field(): never; + + /** + * @override + * @throws {Error} This method cannot be called on ParentAggregation + */ + script(): never; + + /** + * Sets the child type/mapping for aggregation. + * + * @param {string} type The child type that the buckets in the parent space should be mapped to. + */ + type(type: string): this; + } + + /** + * A special single bucket aggregation that enables aggregating + * from buckets on child document types to buckets on parent documents. + * This aggregation relies on the `_parent` field in the mapping. + * + * @param {string} name The name which will be used to refer to this aggregation. + */ + export function parentAggregation(name: string): ParentAggregation; + /** * A multi-bucket value source based aggregation that enables the user to * define a set of ranges - each representing a bucket. During the aggregation diff --git a/src/index.js b/src/index.js index e742f8c0..f8d88de7 100644 --- a/src/index.js +++ b/src/index.js @@ -116,6 +116,7 @@ const { IpRangeAggregation, MissingAggregation, NestedAggregation, + ParentAggregation, RangeAggregation, ReverseNestedAggregation, SamplerAggregation, @@ -421,6 +422,9 @@ exports.missingAggregation = constructorWrapper(MissingAggregation); exports.NestedAggregation = NestedAggregation; exports.nestedAggregation = constructorWrapper(NestedAggregation); +exports.ParentAggregation = ParentAggregation; +exports.parentAggregation = constructorWrapper(ParentAggregation); + exports.RangeAggregation = RangeAggregation; exports.rangeAggregation = constructorWrapper(RangeAggregation); diff --git a/test/aggregations-test/parent-agg.test.js b/test/aggregations-test/parent-agg.test.js new file mode 100644 index 00000000..4cdcd6f6 --- /dev/null +++ b/test/aggregations-test/parent-agg.test.js @@ -0,0 +1,31 @@ +import test from 'ava'; +import { ParentAggregation } from '../../src'; +import { illegalCall, setsAggType } from '../_macros'; + +test(setsAggType, ParentAggregation, 'parent'); +test(illegalCall, ParentAggregation, 'field', 'my_agg'); +test(illegalCall, ParentAggregation, 'script', 'my_agg'); + +test('constructor sets type', t => { + const value = new ParentAggregation('to_questions', 'answer').toJSON(); + const expected = { + to_questions: { + parent: { + type: 'answer' + } + } + }; + t.deepEqual(value, expected); +}); + +test('type is set', t => { + const value = new ParentAggregation('to_questions').type('answer').toJSON(); + const expected = { + to_questions: { + parent: { + type: 'answer' + } + } + }; + t.deepEqual(value, expected); +}); diff --git a/test/index.test.js b/test/index.test.js index 2bac1a21..c55fb664 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -268,6 +268,9 @@ test('aggregations are exported', t => { t.truthy(esb.NestedAggregation); t.truthy(esb.nestedAggregation); + t.truthy(esb.ParentAggregation); + t.truthy(esb.parentAggregation); + t.truthy(esb.RangeAggregation); t.truthy(esb.rangeAggregation);