diff --git a/docs/Searching/Search.md b/docs/Searching/Search.md index ce3e21b..3a19af6 100644 --- a/docs/Searching/Search.md +++ b/docs/Searching/Search.md @@ -162,7 +162,7 @@ var interest = searchBuilder.execute().getHits().map( (document) => document.get ### Runtime Fields -Elasticsearch also allows the creation of runtime fields, which are fields defined in the index mapping but populated at search time via a script. +Elasticsearch also supports defining runtime fields, which are fields defined in the index mapping but populated at search time via a script. You can [define these in the index mapping](../Indices/Managing-Indices.md#creating-runtime-fields), or [define them at search time](#define-runtime-fields-at-search-time). {% hint style="info" %} See [Managing-Indices](../Indices/Managing-Indices.md#creating-runtime-fields) for more information on creating runtime fields. @@ -203,6 +203,36 @@ for( hit in result.getHits() ){ } ``` + +### Define Runtime Fields At Search Time + +Elasticsearch also allows you to [define runtime fields at search time](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-search-request.html), and unlike [script fields](#script-fields) these runtime fields are available to use in aggregations, search queries, and so forth. + +```js +searchBuilder.addRuntimeMapping( "hasPricing", { + "type" : "boolean", + "script": { + "source": "doc.containsKey( 'price' )" + } +} ); +``` + +Using `.addField()` ensures the field is returned with the document upon query completion: + +```js +searchBuilder.addRuntimeMapping( "hasPricing", ... ).addField( "hasPricing" ); +``` + +We can then retrieve the result field via the `getFields()` method: + +```js +var documentsWithPricing = searchBuilder.execute() + .getHits() + .filter( (document) => document.getFields()["hasPricing"] ); +``` + +or inlined with the document mento using `hit.getDocument( includeFields = true )`. + ### Advanced Query DSL The SearchBuilder also allows full use of the [Elasticsearch query language](https://www.elastic.co/guide/en/elasticsearch/reference/current/_introducing_the_query_language.html), allowing full configuration of your search queries. There are several methods to provide the raw query language to the Search Builder. One is during instantiation. diff --git a/models/SearchBuilder.cfc b/models/SearchBuilder.cfc index a496cab..40f86f1 100644 --- a/models/SearchBuilder.cfc +++ b/models/SearchBuilder.cfc @@ -57,6 +57,13 @@ component accessors="true" { */ property name="scriptFields" type="struct"; + /** + * Property containing elasticsearch "runtime_mappings" definition for runtime scripted fields + * + * https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html + */ + property name="runtimeMappings" type="struct"; + /** * Property containing "fields" array of fields to return for each hit * @@ -1247,6 +1254,10 @@ component accessors="true" { dsl[ "script_fields" ] = variables.scriptFields; } + if ( !isNull( variables.runtimeMappings ) ) { + dsl[ "runtime_mappings" ] = variables.runtimeMappings; + } + if ( !isNull( variables.fields ) ) { dsl[ "fields" ] = variables.fields; } @@ -1362,4 +1373,23 @@ component accessors="true" { return this; } + + /** + * Append a search-time (runtime) mapping to the search. + * + * @name Name of the runtime mapping field + * + * @script Script to use. `{ "script" : { "lang": "painless", "source" : } }` + */ + public SearchBuilder function addRuntimeMapping( + required string name, + struct script + ){ + if ( isNull( variables.runtimeMappings ) ) { + variables.runtimeMappings = {}; + } + variables.runtimeMappings[ arguments.name ] = arguments.script; + return this; + } + } diff --git a/test-harness/tests/specs/unit/SearchBuilderTest.cfc b/test-harness/tests/specs/unit/SearchBuilderTest.cfc index 2cd82e4..eea2f4f 100644 --- a/test-harness/tests/specs/unit/SearchBuilderTest.cfc +++ b/test-harness/tests/specs/unit/SearchBuilderTest.cfc @@ -830,6 +830,20 @@ component extends="coldbox.system.testing.BaseTestCase" { expect( searchBuilder.getDSL()[ "script_fields" ] ).toHaveKey( "with5PercentDiscount" ); } ); + it( "Tests the addRuntimeMapping() method", function(){ + var searchBuilder = variables.model.new( variables.testIndexName, "testdocs" ); + + searchBuilder.addRuntimeMapping( "hasPricing", { + "type" : "boolean", + "script": { + "source": "doc.containsKey( 'price' )" + } + } ); + + expect( searchBuilder.getDSL() ).toBeStruct().toHaveKey( "runtime_mappings" ); + expect( searchBuilder.getDSL()[ "runtime_mappings" ] ).toHaveKey( "hasPricing" ); + } ); + it( "Tests the addField() method for retrieving runtime or other fields", function(){ var search = variables.model.new( variables.testIndexName, "testdocs" );