Skip to content

Commit

Permalink
featL Add support for runtime_mappings (#196)
Browse files Browse the repository at this point in the history
runtime_mappings was added in Elasticsearch v7.11.

Co-authored-by: Julien Maitrehenry <julien@kumojin.com>
  • Loading branch information
atreids and jmaitrehenry authored Mar 10, 2024
1 parent 3de7f2d commit 2572468
Show file tree
Hide file tree
Showing 9 changed files with 401 additions and 2 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// Place your settings in this file to overwrite default and user settings.
{
"eslint.enable": true,
"eslint.autoFixOnSave": true,
"files.eol": "\n",
"vsicons.presets.angular": false,
"editor.detectIndentation": true,
"[json]": {
"editor.tabSize": 2
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"author": "Suhas Karanth <sudo.suhas@gmail.com>",
"contributors": [
"austin ce <austin.cawley@gmail.com>",
"Julien Maitrehenry <julien.maitrehenry@me.com>",
"ochan12 <mateochando@gmail.com>",
"kennylindahl <haxxblaster@gmail.com>",
"foxstarius <aj.franzon@gmail.com>",
Expand Down
2 changes: 2 additions & 0 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ exports.SearchTemplate = require('./search-template');
exports.consts = require('./consts');

exports.util = require('./util');

exports.RuntimeField = require('./runtime-field');
82 changes: 82 additions & 0 deletions src/core/request-body-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const Query = require('./query'),
InnerHits = require('./inner-hits');

const { checkType, setDefault, recursiveToJSON } = require('./util');
const RuntimeField = require('./runtime-field');

/**
* Helper function to call `recursiveToJSON` on elements of array and assign to object.
Expand Down Expand Up @@ -406,6 +407,87 @@ class RequestBodySearch {
return this;
}

/**
* Computes a document property dynamically based on the supplied `runtimeField`.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @example
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMapping(
* 'sessionId-name',
* esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['name'].value)`
* )
* )
*
* @example
* // runtime fields can also be used in query aggregation
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMapping(
* 'sessionId-eventName',
* esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['eventName'].value)`,
* )
* )
* .agg(esb.cardinalityAggregation('uniqueCount', `sessionId-eventName`)),;
*
* @param {string} runtimeFieldName Name for the computed runtime mapping field.
* @param {RuntimeField} runtimeField Instance of RuntimeField
*
* @returns {RequestBodySearch} returns `this` so that calls can be chained
*
*/
runtimeMapping(runtimeFieldName, runtimeField) {
checkType(runtimeField, RuntimeField);

setDefault(this._body, 'runtime_mappings', {});
this._body.runtime_mappings[runtimeFieldName] = runtimeField;
return this;
}

/**
* Computes one or more document properties dynamically based on supplied `RuntimeField`s.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @example
* const fieldA = esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['name'].value)`
* );
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMappings({
* 'sessionId-name': fieldA,
* })
*
* @param {Object} runtimeMappings Object with `runtimeFieldName` as key and instance of `RuntimeField` as the value.
* @returns {RequestBodySearch} returns `this` so that calls can be chained
*/
runtimeMappings(runtimeMappings) {
checkType(runtimeMappings, Object);

Object.keys(runtimeMappings).forEach(runtimeFieldName =>
this.runtimeMapping(
runtimeFieldName,
runtimeMappings[runtimeFieldName]
)
);

return this;
}

/**
* Computes a document property dynamically based on the supplied `Script`.
*
Expand Down
91 changes: 91 additions & 0 deletions src/core/runtime-field.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict';

const isNil = require('lodash.isnil');
const validType = [
'boolean',
'composite',
'date',
'double',
'geo_point',
'ip',
'keyword',
'long',
'lookup'
];

/**
* Class supporting the Elasticsearch runtime field.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @param {string=} type One of `boolean`, `composite`, `date`, `double`, `geo_point`, `ip`, `keyword`, `long`, `lookup`.
* @param {string=} script Source of the script.
*
* @example
* const field = esb.runtimeField('keyword', `emit(doc['sessionId'].value + '::' + doc['name'].value)`);
*/
class RuntimeField {
// eslint-disable-next-line require-jsdoc
constructor(type, script) {
this._body = {};
this._isTypeSet = false;
this._isScriptSet = false;

if (!isNil(type)) {
this.type(type);
}

if (!isNil(script)) {
this.script(script);
}
}

/**
* Sets the source of the script.
* @param {string} script
* @returns {void}
*/
script(script) {
this._body.script = {
source: script
};
this._isScriptSet = true;
}

/**
* Sets the type of the runtime field.
* @param {string} type One of `boolean`, `composite`, `date`, `double`, `geo_point`, `ip`, `keyword`, `long`, `lookup`.
* @returns {void}
*/
type(type) {
const typeLower = type.toLowerCase();
if (!validType.includes(typeLower)) {
throw new Error(`\`type\` must be one of ${validType.join(', ')}`);
}
this._body.type = typeLower;
this._isTypeSet = true;
}

/**
* Override default `toJSON` to return DSL representation for the `script`.
*
* @override
* @returns {Object} returns an Object which maps to the elasticsearch query DSL
*/
toJSON() {
if (!this._isTypeSet) {
throw new Error('`type` should be set');
}

if (!this._isScriptSet) {
throw new Error('`script` should be set');
}

return this._body;
}
}

module.exports = RuntimeField;
124 changes: 124 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,73 @@ declare namespace esb {
*/
storedFields(fields: object | string): this;



/**
* Computes a document property dynamically based on the supplied `runtimeField`.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @example
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMapping(
* 'sessionId-name',
* esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['name'].value)`
* )
* )
*
* @example
* // runtime fields can also be used in query aggregation
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMapping(
* 'sessionId-eventName',
* esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['eventName'].value)`,
* )
* )
* .agg(esb.cardinalityAggregation('uniqueCount', `sessionId-eventName`)),;
*
* @param {string} runtimeFieldName Name for the computed runtime mapping field.
* @param {RuntimeField} runtimeField Instance of RuntimeField
*
* @returns {RequestBodySearch} returns `this` so that calls can be chained
*
*/
runtimeMapping(runtimeFieldName: string, runtimeField: RuntimeField): this;


/**
* Computes one or more document properties dynamically based on supplied `RuntimeField`s.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @example
* const fieldA = esb.runtimeField(
* 'keyword',
* `emit(doc['session_id'].value + '::' + doc['name'].value)`
* );
* const reqBody = esb.requestBodySearch()
* .query(esb.matchAllQuery())
* .runtimeMappings({
* 'sessionId-name': fieldA,
* })
*
* @param {Object} runtimeMappings Object with `runtimeFieldName` as key and instance of `RuntimeField` as the value.
* @returns {RequestBodySearch} returns `this` so that calls can be chained
*/
runtimeMappings(runtimeMappings: object): this;

/**
* Computes a document property dynamically based on the supplied `Script`.
*
Expand Down Expand Up @@ -8836,6 +8903,63 @@ declare namespace esb {
*/
export function highlight(fields?: string | string[]): Highlight;

/**
* Class supporting the Elasticsearch runtime field.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @param {string=} type One of `boolean`, `composite`, `date`, `double`, `geo_point`, `ip`, `keyword`, `long`, `lookup`.
* @param {string=} script Source of the script.
*
* @example
* const field = esb.runtimeField('keyword', `emit(doc['sessionId'].value + '::' + doc['name'].value)`);
*/
export class RuntimeField {
constructor(type?: string, script?: string);

/**
* Sets the type of the runtime field.
*
* @param {string} type One of `boolean`, `composite`, `date`, `double`, `geo_point`, `ip`, `keyword`, `long`, `lookup`.
* @returns {void}
*/
type(type: 'boolean' | 'composite' | 'date' | 'double' | 'geo_point' | 'ip' | 'keyword' | 'long' | 'lookup');

/**
* Sets the source of the script.
*
* @param {string} script
* @returns {void}
*/
script(script: string);

/**
* Override default `toJSON` to return DSL representation for the `script`.
*
* @override
*/
toJSON(): object;
}

/**
* Class supporting the Elasticsearch runtime field.
*
* [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html)
*
* Added in Elasticsearch v7.11.0
* [Release note](https://www.elastic.co/guide/en/elasticsearch/reference/7.11/release-notes-7.11.0.html)
*
* @param {string=} type One of `boolean`, `composite`, `date`, `double`, `geo_point`, `ip`, `keyword`, `long`, `lookup`.
* @param {string=} script Source of the script.
*
* @example
* const field = esb.runtimeField('keyword', `emit(doc['sessionId'].value + '::' + doc['name'].value)`);
*/
export function runtimeField(type?: 'boolean' | 'composite' | 'date' | 'double' | 'geo_point' | 'ip' | 'keyword' | 'long' | 'lookup', script?: string): RuntimeField;

/**
* Class supporting the Elasticsearch scripting API.
*
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const {
Sort,
Rescore,
InnerHits,
RuntimeField,
SearchTemplate,
Query,
util: { constructorWrapper }
Expand Down Expand Up @@ -650,6 +651,9 @@ exports.innerHits = constructorWrapper(InnerHits);
exports.SearchTemplate = SearchTemplate;
exports.searchTemplate = constructorWrapper(SearchTemplate);

exports.RuntimeField = RuntimeField;
exports.runtimeField = constructorWrapper(RuntimeField);

exports.prettyPrint = function prettyPrint(obj) {
console.log(JSON.stringify(obj, null, 2));
};
Expand Down
Loading

0 comments on commit 2572468

Please sign in to comment.