Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
jclausen committed Apr 18, 2024
2 parents 7b13c65 + 86103e2 commit 22318c0
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 25 deletions.
2 changes: 1 addition & 1 deletion box.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name":"Elasticsearch for the Coldbox Framework",
"author":"Ortus Solutions <[email protected]",
"location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/cbelasticsearch/@build.version@/[email protected]@[email protected]@.zip",
"version":"3.2.5",
"version":"3.3.0",
"slug":"cbelasticsearch",
"type":"modules",
"homepage":"https://cbelasticsearch.ortusbooks.com",
Expand Down
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

----
## [3.3.0] - 04-18-2024

### Added
* Added `addRuntimeMapping()` method for mapping runtime fields at search time

### Fixed
* Improved error handling for rare cases where ElasticSearch exceptions are missing a `reason` key.

## [3.2.5] - 11-26-2023
### Added
* Added `asyncTimeout` setting with default of 5000ms
Expand Down
11 changes: 10 additions & 1 deletion docs/Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ If you would prefer to set up Elasticsearch yourself, make sure you start this a
ELASTICSEARCH_PROTOCOL=http
ELASTICSEARCH_HOST=127.0.0.1
ELASTICSEARCH_PORT=9200
```
```

## Releases

To issue a new release:

1. Update (and commit) `changelog.md` with each addition, bugfix, or security issue.
1. These should be placed under the version number heading: `## [x.y.z] - dd-mm-yyyy`. Later this will be automated to use the `## Unreleased` section.
2. Set and commit the new version number in `box.json`, following semantic versioning format.
3. Run the release script: `box recipe build/release.boxr`
34 changes: 17 additions & 17 deletions docs/Pipelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ Let's say we want to automatically set a field on a document when we save it. We

```js
var myPipeline = getInstance( "Pipeline@cbelasticsearch" ).new( {
"id" : "foo-pipeline",
"description" : "A test pipeline",
"version" : 1,
"processors" : [
{
"set" : {
"if" : "ctx.foo == null",
"field" : "foo",
"value" : "bar"
}
}
]
} );
"id" : "foo-pipeline",
"description" : "A test pipeline",
"version" : 1,
"processors" : [
{
"set" : {
"if" : "ctx.foo == null",
"field" : "foo",
"value" : "bar"
}
}
]
} );
```

With this pipeline, if a value of `foo` is not defined ( note that `ctx` is the document reference in the `if` conditional ) in the inbound document, then the value of that field will automatically be set to `'bar'`.
Expand All @@ -46,7 +46,7 @@ getInstance( "Client@cbElasticsearch" ).applyPipeline( myPipeline );

Note that if you are using a [secondary cluster](Configuration.md), you will need to perform your CRUD operations through the client, as the `save` method in the pipeline object will route through the top level client.

## Retrieving pipeline definitions
## Retrieving Pipeline Definitions

If we know the name of our pipeline, we can retreive the definition from Elasticsearch by using the `getPipeline` method of the client:

Expand All @@ -67,8 +67,8 @@ We can modify pipelines using the pipeline object, as well. Let's do this by ret

```js
var pipeline = getInstance( "Pipeline@cbElasticsearch" )
.new( getInstance( "Client@cbElasticsearch" )
.getPipeline( "foo-pipeline" ) );
.new( getInstance( "Client@cbElasticsearch" )
.getPipeline( "foo-pipeline" ) );

pipeline.addProcessor(
{
Expand All @@ -92,7 +92,7 @@ getInstance( "Client@cbElastisearch" )
.deletePipeline( "foo-pipeline" );
```

## Using pipelines When Saving Documents
## Using Pipelines When Saving Documents

Pipelines may be used when saving individual or multiple documents. See the [Documents](Documents.md) section for more information on document creation.

Expand Down
32 changes: 31 additions & 1 deletion docs/Searching/Search.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
30 changes: 30 additions & 0 deletions models/SearchBuilder.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}

}
8 changes: 5 additions & 3 deletions models/io/HyperClient.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ component accessors="true" threadSafe singleton {
} else if ( reindexResult.keyExists( "error" ) ) {
throw(
type = "cbElasticsearch.HyperClient.ReindexFailedException",
message = "The reindex action failed with response code [#reindexResult.status#]. The cause of this exception was #reindexResult.error.reason#",
message = "The reindex action failed with response code [#reindexResult.status#]. The cause of this exception was #reindexResult.error.reason ?: "None"#",
extendedInfo = getUtil().toJSON( reindexResult )
);
}
Expand Down Expand Up @@ -1211,10 +1211,12 @@ component accessors="true" threadSafe singleton {
item.update.keyExists( "error" )
&& item.update.error.keyExists( "root_cause" )
)
? " Reason: #isArray( item.update.error.root_cause ) ? item.update.error.root_cause[ 1 ].reason : item.update.error.root_cause.reason#"
? " Reason: #isArray( item.update.error.root_cause ) ? ( item.update.error.root_cause[ 1 ].reason ?: "None" ) : (
item.update.error.root_cause.reason ?: "None"
)#"
: (
structKeyExists( item.update, "error" )
? " Reason: #item.update.error.reason#"
? " Reason: #item.update.error.reason ?: "None"#"
: ""
);
throw(
Expand Down
6 changes: 4 additions & 2 deletions models/util/Util.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,15 @@ component accessors="true" singleton {
&& !isSimpleValue( errorPayload.error )
&& errorPayload.error.keyExists( "root_cause" )
)
? " Reason: #isArray( errorPayload.error.root_cause ) ? errorPayload.error.root_cause[ 1 ].reason : errorPayload.error.root_cause.reason#"
? " Reason: #isArray( errorPayload.error.root_cause ) ? ( errorPayload.error.root_cause[ 1 ].reason ?: "None" ) : (
errorPayload.error.root_cause.reason ?: "None"
)#"
: (
structKeyExists( errorPayload, "error" )
? (
isSimpleValue( errorPayload.error )
? " Reason: #errorPayload.error# "
: " Reason: #errorPayload.error.reason#"
: " Reason: #errorPayload.error.reason ?: "None"#"
)
: ""
);
Expand Down
14 changes: 14 additions & 0 deletions test-harness/tests/specs/unit/SearchBuilderTest.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -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" );

Expand Down

0 comments on commit 22318c0

Please sign in to comment.