From e7b3804f3d7550fd2803a10b6e70aa803c058d63 Mon Sep 17 00:00:00 2001 From: Niki Date: Thu, 4 Feb 2021 13:09:52 +0300 Subject: [PATCH] feat: add post filter in query, add sub aggs --- package.json | 2 +- src/Builders/Aggregation/index.ts | 16 ++++-- src/Builders/Aggregation/types.ts | 26 ++++++---- src/Query/index.ts | 25 ++++++--- src/Query/types.ts | 12 +++-- src/__test__/Aggs/index.test.ts | 84 ++++++++++++++++++++++++++++++- src/__test__/Query/index.test.ts | 82 +++++++++++++++++++----------- 7 files changed, 193 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 402072f..9f88a9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "elastic-dsl-typescript", - "version": "1.0.7", + "version": "1.1.1", "description": "Node.js ElasticSearch search query builder", "main": "./", "scripts": { diff --git a/src/Builders/Aggregation/index.ts b/src/Builders/Aggregation/index.ts index deab7a9..2458cea 100644 --- a/src/Builders/Aggregation/index.ts +++ b/src/Builders/Aggregation/index.ts @@ -5,13 +5,22 @@ export class Aggregation extends AbstractBulde private _data = {}; public add(aggType: Type, name: string, d: SCHEMA[Type]) { - const { filter, ...all } = d.params as { filter?: any }; + const { filter, subAgg, ...all } = d.params as { filter?: any; subAgg?: any }; + let sub = {}; + if (subAgg) { + sub = { + aggs: { + ...subAgg + } + }; + } if (filter) { this._data[name] = { filter, aggs: { [`${name}_filtered`]: { - [aggType]: { ...(all as object), ...((d.opts as object) || {}) } + [aggType]: { ...all, ...((d.opts as object) || {}) }, + ...sub } } }; @@ -19,7 +28,8 @@ export class Aggregation extends AbstractBulde } this._data[name] = { - [aggType]: { ...(d.params as object), ...((d.opts as object) || {}) } + [aggType]: { ...all, ...((d.opts as object) || {}) }, + ...sub }; return this; } diff --git a/src/Builders/Aggregation/types.ts b/src/Builders/Aggregation/types.ts index 7a1910d..f9fafb2 100644 --- a/src/Builders/Aggregation/types.ts +++ b/src/Builders/Aggregation/types.ts @@ -1,5 +1,10 @@ import { AggSchema } from "../../Abstract/Schema"; -import { Range, RangeAggregation, PainLessScript } from "../../Types"; +import { RangeAggregation, PainLessScript } from "../../Types"; + + +export interface SubAggregation { + subAgg?: FilterAggregation; +} export interface AggregationSchema extends AggSchema { /** @@ -12,7 +17,10 @@ export interface AggregationSchema extends AggSchema { */ field: string; filter?: FilterAggregation; - }; + /** + * @property subAgg Вложенные sub аггрегации + */ + } & SubAggregation; opts?: { /** * @property Return basket size @@ -32,7 +40,7 @@ export interface AggregationSchema extends AggSchema { range: { params: { field: string; - + filter?: FilterAggregation; ranges: RangeAggregation; }; }; @@ -40,42 +48,42 @@ export interface AggregationSchema extends AggSchema { avg: { params: { field: string; - + filter?: FilterAggregation; script?: PainLessScript; }; }; max: { params: { field: string; - + filter?: FilterAggregation; script?: PainLessScript; }; }; min: { params: { field: string; - + filter?: FilterAggregation; script?: PainLessScript; }; }; sum: { params: { field: string; - + filter?: FilterAggregation; script?: PainLessScript; }; }; percentiles: { params: { field: string; - + filter?: FilterAggregation; percents?: [number, number, number]; }; }; value_count: { params: { field: string; - + filter?: FilterAggregation; script?: PainLessScript; }; }; diff --git a/src/Query/index.ts b/src/Query/index.ts index ef5f88e..b2aa62d 100644 --- a/src/Query/index.ts +++ b/src/Query/index.ts @@ -8,8 +8,9 @@ import { RawQuery } from './types'; * @Classdesc Generator queries for Elastic search */ export class Query extends AbstractBulder { - private _props: Omit = {}; - private _query: RawQuery['query'] = {}; + private _props: Omit = {}; + private _query: RawQuery["query"] = {}; + private _post_filter: Bool = new Bool(); /** * Add basic props to Query (size,from,_source,etc...) @@ -17,7 +18,7 @@ export class Query extends AbstractBulder { * @param data * @returns {this} */ - public addProps>(prop: K, data: RawQuery[K]) { + public addProps>(prop: K, data: RawQuery[K]) { this._props[prop] = data; return this; } @@ -38,13 +39,22 @@ export class Query extends AbstractBulder { * @returns {Bool} */ get bool(): Bool { - if (this.isNotExistInQuery('bool')) { + if (this.isNotExistInQuery("bool")) { this._query.bool = new Bool(); } return this._query.bool!; } - private isNotExistInQuery(prop: keyof RawQuery['query']) { + get postFilter(): Bool { + return this._post_filter; + } + + public addPostFilter(bool: Bool) { + this._post_filter = bool; + return this; + } + + private isNotExistInQuery(prop: keyof RawQuery["query"]) { return !(prop in this._query); } @@ -76,7 +86,10 @@ export class Query extends AbstractBulder { } query[prop] = val; } - obj['query'] = query; + obj["query"] = query; + if (this._post_filter.isNotEmty()) { + obj["post_filter"] = this._post_filter.build(); + } return obj; } diff --git a/src/Query/types.ts b/src/Query/types.ts index 2f276a6..712c97c 100644 --- a/src/Query/types.ts +++ b/src/Query/types.ts @@ -1,8 +1,8 @@ -import { AggregationSchema } from '..'; -import { Bool } from '../Builders/Bool'; -import { BoolSchema } from '../Builders/Bool/types'; -import { Sort, Range } from "../Types"; +import { AggregationSchema } from ".."; import { Aggregation } from "../Builders/Aggregation"; +import { Bool } from "../Builders/Bool"; +import { BoolSchema } from "../Builders/Bool/types"; +import { Range, Sort } from "../Types"; /** * @@ -35,6 +35,10 @@ export interface RawQuery { boost?: number; }; }; + /** + * Post aggregations filters + */ + post_filter?: Bool; sort?: Sort[]; aggs?: Aggregation; _source?: string[]; diff --git a/src/__test__/Aggs/index.test.ts b/src/__test__/Aggs/index.test.ts index f122b0c..8ee8e4e 100644 --- a/src/__test__/Aggs/index.test.ts +++ b/src/__test__/Aggs/index.test.ts @@ -1,4 +1,5 @@ -import { Aggregation } from '../../Builders/Aggregation'; +import { Aggregation } from "../../Builders/Aggregation"; +import { Bool } from "../../Builders/Bool"; describe('Check aggs builder', () => { beforeEach(() => { @@ -173,4 +174,85 @@ describe('Check aggs builder', () => { } }); }); + + test("Create range with sub aggs", async () => { + const a = new Aggregation().add("range", "my_range", { + params: { + field: "price", + filter: new Bool() + .Must("exists", { + params: { + fieldName: "testfield" + } + }) + .build(), + ranges: [ + { + from: 1 + } + ] + } + }); + + expect(a.build()).toEqual({ + my_range: { + aggs: { + my_range_filtered: { + range: { + field: "price", + ranges: [ + { + from: 1 + } + ] + } + } + }, + filter: { + bool: { + must: [ + { + exists: { + field: "testfield" + } + } + ] + } + } + } + }); + }); + test("Create term sub aggs", async () => { + const a = new Aggregation().add("terms", "testField", { + params: { + field: "testField", + subAgg: { + stores: { + terms: { + field: "availStores", + size: 5, + include: ["124214"] + } + } + } + } + }); + + expect(a.build()).toEqual({ + testField: { + aggs: { + stores: { + terms: { + field: "availStores", + include: ["124214"], + size: 5 + } + } + }, + terms: { + field: "testField" + } + } + }); + }); }); diff --git a/src/__test__/Query/index.test.ts b/src/__test__/Query/index.test.ts index 1c6b68e..eae3df2 100644 --- a/src/__test__/Query/index.test.ts +++ b/src/__test__/Query/index.test.ts @@ -1,12 +1,12 @@ -import { Aggregation, Bool, Query } from '../../index'; +import { Aggregation, Bool, BoolSchema, Query } from "../../index"; describe('ScriptFields tests', () => { test('Create aggs', async () => { const q = new Query().addProps( - 'aggs', - new Aggregation().add('sum', 'agg_sum', { + "aggs", + new Aggregation().add("sum", "agg_sum", { params: { - field: 'price' + field: "price" } }) ); @@ -14,7 +14,7 @@ describe('ScriptFields tests', () => { aggs: { agg_sum: { sum: { - field: 'price' + field: "price" } } }, @@ -23,40 +23,63 @@ describe('ScriptFields tests', () => { }); test('Create script field ', async () => { const q = new Query() - .addProps('_source', ['field']) - .addProps('explain', true) - .addProps('from', 0) - .addProps('size', 100) - .addProps('q', 'Lucene query string ') - .addQuery('match', { + .addProps("_source", ["field"]) + .addProps("explain", true) + .addProps("from", 0) + .addProps("size", 100) + .addProps("q", "Lucene query string ") + .addQuery("match", { message: { - query: 'query' - }, + query: "query" + } }) - .addQuery('term', { - field: 'f', - value: 'term', + .addQuery("term", { + field: "f", + value: "term" }) - .addQuery('range', { + .addQuery("range", { gt: 0, - gte: 0, - }); + gte: 0 + }) + .addPostFilter( + new Bool().Must_Not("exists", { + params: { + fieldName: "test" + } + }) + ); q.bool.addBuilder( - 'must', - new Bool().add('must', 'term', { - field: 'articul', + "must", + new Bool().add("must", "term", { + field: "articul", params: { - value: '111', - }, - }), + value: "111" + } + }) ); + console.log(JSON.stringify(q.build(), null, 2)); expect(q.isNotEmty()).toEqual(true); - expect(q.build()).toEqual({ - _source: ['field'], + expect(q.build()).toHaveProperty( + "post_filter", + expect.objectContaining({ + bool: { + must_not: [ + { + exists: { + field: "test" + } + } + ] + } + }) + ); + const { post_filter, ...data } = q.build() as any; + expect(data).toEqual({ + _source: ["field"], explain: true, from: 0, - q: 'Lucene query string ', + q: "Lucene query string ", query: { bool: { must: [ @@ -66,8 +89,7 @@ describe('ScriptFields tests', () => { { term: { articul: "111" - - }, + } }, ], },