Skip to content

Commit

Permalink
Merge pull request #139 from coldbox-modules/development
Browse files Browse the repository at this point in the history
3.3.1
  • Loading branch information
jclausen authored Sep 11, 2024
2 parents 22318c0 + c26c0de commit 3d7f83a
Show file tree
Hide file tree
Showing 36 changed files with 337 additions and 168 deletions.
13 changes: 10 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ jobs:
matrix:
cfengine: [ "lucee@5", "adobe@2018", "adobe@2021", "adobe@2023" ]
coldboxVersion: [ "^6", "^7" ]
ELASTICSEARCH_VERSION: [ "7.17.10", "8.8.1" ]
javaVersion: [ "11" ]
ELASTICSEARCH_VERSION: [ "7.17.10", "8.14.1" ]
experimental: [ false ]
include:
- cfengine: "lucee@6"
coldboxVersion: "^7"
ELASTICSEARCH_VERSION: "8.8.1"
javaVersion: "21"
ELASTICSEARCH_VERSION: "8.14.1"
experimental: true
steps:
- name: Checkout Repository
Expand All @@ -44,11 +46,16 @@ jobs:
uses: actions/setup-java@v3.9.0
with:
distribution: "temurin"
java-version: "11"
java-version: "${{ matrix.javaVersion }}"

- name: Setup CommandBox CLI
uses: Ortus-Solutions/setup-commandbox@v2.0.1

- name: Set ColdBox Version
working-directory: ./test-harness
run: |
box install coldbox@${{ matrix.coldboxVersion }}
- name: Install Dependencies
run: |
box run-script install:dependencies
Expand Down
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 <info@ortussolutions.com",
"location":"https://downloads.ortussolutions.com/ortussolutions/coldbox-modules/cbelasticsearch/@build.version@/cbelasticsearch-@build.version@+@build.number@.zip",
"version":"3.3.0",
"version":"3.3.1",
"slug":"cbelasticsearch",
"type":"modules",
"homepage":"https://cbelasticsearch.ortusbooks.com",
Expand Down
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ 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).

----
## [Unreleased]

### Added

* Improved error handling to include the status code in the error message if a "reason" could not be extracted from the response: `Elasticsearch server responded with [504 Gateway Timeout]. The response received was not JSON.`.

## [3.3.0] - 04-18-2024

### Added
Expand Down
64 changes: 64 additions & 0 deletions docs/Customizations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
description: Learn how to optimize and extend CBElasticsearch to better fit your app's needs.
---

# Customizations

Elasticsearch offers a TON of functionality, and while CBElasticsearch is able to cover the majority of the main features, some of the lesser-known API calls may not be currently supported. If you need to implement an Elasticsearch feature which is not included in CBElasticsearch, you can implement this yourself using the `getNodePool()` method to obtain a [HyperRequest object](https://hyper.ortusbooks.com/making-requests/hyperrequest) with an Elasticsearch connection:

```js
var myCustomESRequest = getInstance( "Client@cbElasticsearch" ).getNodePool();
```

From here, you can use Hyper methods to build and send the request:

```js
var response = getInstance( "Client@cbElasticsearch" ).getNodePool()
.newRequest( "myIndex/_close", "POST" )
.asJSON()
.send();
```

The response back will be a typical [HyperResponse object](https://hyper.ortusbooks.com/making-requests/hyperresponse), so you can use [`.isError()` to check for an error response](https://hyper.ortusbooks.com/making-requests/hyperresponse#iserror), [`.json()` to retrieve the JSON response](https://hyper.ortusbooks.com/making-requests/hyperresponse#json), etc.

As a simplistic example of handling a custom ES request and response:

```js
var response = getInstance( "Client@cbElasticsearch" ).getNodePool()
.newRequest( "#arguments.indexName#/_close", "POST" )
.asJSON()
.send();

if ( response.isError() ) {
throw( message = "Received failure from ES", extendedInfo = response.getData() );
} else {
return response.json();
}
```

The Elasticsearch client also offers many utility methods for either building the request or processing the result - see, for example, `parseParams()` and `onResponseFailure()`.

Putting this all together, here's a full example of a custom method which implements a nontypical Elasticsearch API call, supporting all query parameters and using CBElasticsearch's standard error handling:

```js
function closeIndex( required string indexName, any params ){
var elasticSearchClient = getInstance( "Client@cbElasticsearch" );
var requestBuilder = elasticSearchClient.getNodePool()
.newRequest( "#arguments.indexName#/_close", "POST" )
.asJSON();

if ( structKeyExists( arguments, "params" ) ) {
elasticSearchClient.parseParams( arguments.params ).each( function( param ){
requestBuilder.setQueryParam( param.name, param.value );
} );
}

var response = requestBuilder.send();

if ( response.isError() ) {
elasticSearchClient.onResponseFailure( response );
} else {
return response.json();
}
}
```
42 changes: 21 additions & 21 deletions docs/Documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Documents are the searchable, serialized objects within your indexes. As noted
The `Document` model is the primary object for creating and working with Documents. Let's say, again, we were going to create a new document in our index. We would do so, by first creating a `Document` object.

```js
var book = getInstance( "Document@cbElasticsearch" ).new(
var book = getInstance( "Document@cbelasticsearch" ).new(
index = "bookshop",
type = "_doc",
properties = {
Expand Down Expand Up @@ -60,7 +60,7 @@ To retrieve an existing document, we must first know the `_id` value. We can ei
Using the `Document` object's accessors:

```js
var existingDocument = getInstance( "Document@cbElasticsearch" )
var existingDocument = getInstance( "Document@cbelasticsearch" )
.setIndex( "bookshop" )
.setType( "_doc" )
.setId( bookId )
Expand All @@ -70,7 +70,7 @@ var existingDocument = getInstance( "Document@cbElasticsearch" )
Calling the `get()` method with explicit arguments:

```js
var existingDocument = getInstance( "Document@cbElasticsearch" )
var existingDocument = getInstance( "Document@cbelasticsearch" )
.get(
id = bookId,
index = "bookshop",
Expand All @@ -81,7 +81,7 @@ var existingDocument = getInstance( "Document@cbElasticsearch" )
Calling directly, using the same arguments, from the client:

```js
var existingDocument = getInstance( "Client@cbElasticsearch" )
var existingDocument = getInstance( "Client@cbelasticsearch" )
.get(
id = bookId,
index = "bookshop",
Expand All @@ -92,7 +92,7 @@ var existingDocument = getInstance( "Client@cbElasticsearch" )
The `get` method also accepts a struct of [query parameters](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html#docs-get-api-query-params) to pass to the document retrieval request. For example, we only want certain items returned in the document JSON, we can pass a `_source_includes` query parameter:

```js
var minimal = getInstance( "Client@cbElasticsearch" )
var minimal = getInstance( "Client@cbelasticsearch" )
.get(
id = bookId,
index = "bookshop",
Expand All @@ -116,15 +116,15 @@ existingDocument.populate( properties = myUpdatedBookStruct ).save()
You can also pass Document objects to the `Client`'s `save()` method:

```js
getInstance( "Client@cbElasticsearch" ).save( existingDocument );
getInstance( "Client@cbelasticsearch" ).save( existingDocument );
```

#### Save Documents with an Index Refresh

If you need your document available immediately (such as during a test or pipeline), you can pass `refresh = true` to [instruct Elasticsearch to refresh the relevant index shard immediately and synchronously](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-refresh.html):

```js
getInstance( "Client@cbElasticsearch" ).save(
getInstance( "Client@cbelasticsearch" ).save(
document = existingDocument,
refresh = true
);
Expand All @@ -133,7 +133,7 @@ getInstance( "Client@cbElasticsearch" ).save(
The refresh parameter also accepts a `wait_for` option, which tells Elasticsearch to wait until the next index refresh:

```js
getInstance( "Client@cbElasticsearch" ).save(
getInstance( "Client@cbelasticsearch" ).save(
document = existingDocument,
refresh = "wait_for"
);
Expand All @@ -144,7 +144,7 @@ getInstance( "Client@cbElasticsearch" ).save(
The `patch` method of the Client allows a user to update select fields, bypassing the need for a fully retrieved document. This is similar to an `UPDATE foo SET bar = 'xyz' WHERE id = :id` query on a relational database. The method requires an index name, identifier and a struct containing the keys to be updated:

```js
getInstance( "Client@cbElasticsearch" ).patch(
getInstance( "Client@cbelasticsearch" ).patch(
"bookshop",
bookId,
{
Expand All @@ -156,7 +156,7 @@ getInstance( "Client@cbElasticsearch" ).patch(
Nested keys can also be updated using dot-notation:

```js
getInstance( "Client@cbElasticsearch" ).patch(
getInstance( "Client@cbelasticsearch" ).patch(
"bookshop",
bookId,
{
Expand Down Expand Up @@ -202,7 +202,7 @@ var ops = [
}
];

getInstance( "Client@cbElasticsearch" ).processBulkOperation( ops, { "refresh" : true } );
getInstance( "Client@cbelasticsearch" ).processBulkOperation( ops, { "refresh" : true } );
```

#### Bulk Saving of Documents
Expand All @@ -213,7 +213,7 @@ Builk inserts and updates can be peformed by passing an array of `Document` obje
var documents = [];

for( var myStruct in myArray ){
var document = getInstance( "Document@cbElasticsearch" ).new(
var document = getInstance( "Document@cbelasticsearch" ).new(
index = myIndex,
type = myType,
properties = myStruct
Expand All @@ -222,7 +222,7 @@ for( var myStruct in myArray ){
arrayAppend( documents, document );
}

getInstance( "Client@cbElasticsearch" ).saveAll( documents );
getInstance( "Client@cbelasticsearch" ).saveAll( documents );
```


Expand All @@ -233,9 +233,9 @@ For advanced updates to documents in the index, the `updateByQuery` method can p
Let's say, for example, that you need to add a new key, with a default value, to every document in your index where the key does not already exist:

```js
var searchBuilder = getInstance( "SearchBuilder@cbElasticsearch" )
var searchBuilder = getInstance( "SearchBuilder@cbelasticsearch" )
.mustNotExist( "isInPrint" );
getInstance( "Client@cbElasticsearch" )
getInstance( "Client@cbelasticsearch" )
.updateByQuery(
searchBuilder,
{
Expand All @@ -252,12 +252,12 @@ Note the variable `ctx._source` used in the script, which is a reference to the
Note that a Painless script containing newlines, tabs, or space indentation will throw a parsing error. To work around this limitation, use CBElasticsearch's `Util.formatToPainless( string script )` method to remove newlines and indentation:

```js
getInstance( "Client@cbElasticsearch" )
getInstance( "Client@cbelasticsearch" )
.updateByQuery(
searchBuilder,
{
"lang" : "painless",
"script" : getInstance( "Util@cbElasticsearch" )
"script" : getInstance( "Util@cbelasticsearch" )
.formatToPainless( getReindexScript() )
}
);
Expand All @@ -269,7 +269,7 @@ getInstance( "Client@cbElasticsearch" )
Deleting documents is similar to the process of saving. The `Document` object may be used to delete a single item.

```js
var document = getInstance( "Document@cbElasticsearch" )
var document = getInstance( "Document@cbelasticsearch" )
.get(
id = documentId,
index = "bookshop",
Expand All @@ -283,13 +283,13 @@ if( !isNull( document ) ){
Documents may also be deleted by passing a `Document` instance to the client:

```js
getInstance( "Client@cbElasticsearch" ).delete( myDocument );
getInstance( "Client@cbelasticsearch" ).delete( myDocument );
```

Finally, documents may also be deleted by query, using the `SearchBuilder` ( more below ):

```js
getInstance( "SearchBuilder@cbElasticsearch" )
getInstance( "SearchBuilder@cbelasticsearch" )
.new( index="bookshop", type="books" )
.match( "name", "Elasticsearch for Coldbox" )
.deleteAll();
Expand All @@ -305,7 +305,7 @@ The search builder also supports the addition of URL parameters, which may be us
Of note are the throttling parameters, which are useful in dealing with large documents and/or indices. By default elasticsearch processes batch operations in groups of 1000 documents. Depending on the size of your documents and the collection, it may be preferable to throttle the batch to a smaller number of documents per batch:

```js
getInstance( "SearchBuilder@cbElasticsearch" )
getInstance( "SearchBuilder@cbelasticsearch" )
.new( index="bookshop", type="books" )
.match( "name", "Elasticsearch for Coldbox" )
.param( "scroll_size", 100 )
Expand Down
2 changes: 1 addition & 1 deletion docs/Getting-Started/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ By default the following are in place, without additional configuration:
moduleSettings = {
"cbElasticsearch" = {
//The native client Wirebox DSL for the transport client
client="HyperClient@cbElasticsearch",
client="HyperClient@cbelasticsearch",
// The default hosts - an array of host connections
// - REST-based clients (e.g. JEST): round robin connections will be used
// - Socket-based clients (e.g. Transport): cluster-aware routing used
Expand Down
6 changes: 3 additions & 3 deletions docs/Getting-Started/Secondary-Cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ var secondaryConfig = wirebox.getInstance( "Config@SecondaryCluster" );
// note that we need a full config structure passed in as an override to the coldbox settings
secondaryConfig.setConfigStruct( settings );

// note that we are using the native JEST client rather than Client@cbElasticsearch
// note that we are using the native JEST client rather than Client@cbelasticsearch
binder.map( "Client@SecondaryCluster" )
.to( "cbElasticsearch.models.JestClient" )
.initWith( configuration=secondaryConfig )
Expand All @@ -91,7 +91,7 @@ if( wirebox.containsInstance( "Client@SecondaryCluster" ) ){
Now you may perform a search, considering the caveat that the search must now be executed through the client:

```js
var searchBuilder = getInstance( "SearchBuilder@cbElasticsearch" ).new( "myOtherIndex" );
var searchBuilder = getInstance( "SearchBuilder@cbelasticsearch" ).new( "myOtherIndex" );
searchBuilder.term( "foo", "bar" );

var searchResult = getInstance( "Client@SecondaryCluster" ).executeSearch( searchBuilder );
Expand All @@ -100,7 +100,7 @@ var searchResult = getInstance( "Client@SecondaryCluster" ).executeSearch( searc
Document saves, retrievals, and deletions would need to be routed through the client, as well, rather than using the `save()` function:

```js
var newDocument = getInstance( "Document@cbElasticsearch" ).new( { "id" : createUUID(), "foo" : "bar" } );
var newDocument = getInstance( "Document@cbelasticsearch" ).new( { "id" : createUUID(), "foo" : "bar" } );
getInstance( "Client@SecondaryCluster" ).save( newDocument );


Expand Down
2 changes: 1 addition & 1 deletion docs/Indices/Aliases.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ variables.client.applyAliases(
The client's `getAliases` method allows you to retrieve a map containing information on aliases in use in the connected cluster.

```js
var aliasMap = getInstance( "Client@cbElasticsearch" ).getAliases();
var aliasMap = getInstance( "Client@cbelasticsearch" ).getAliases();
```

The corresponding object will have two keys: `aliases` and `unassigned`. The former is a map of aliases with their corresponding index, the latter is an array of indexes which are unassigned to any alias.
2 changes: 1 addition & 1 deletion docs/Indices/Data-Streams.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Now we can create our data stream in one of two ways. We can either send data to
Create a data stream manually without data:

```js
getInstance( "Client@cbElasticsearch" ).ensureDataStream( "my-index-foo" );
getInstance( "Client@cbelasticsearch" ).ensureDataStream( "my-index-foo" );
```

This will create the data stream and backing indices for the data stream named `my-index-foo`.
Expand Down
Loading

0 comments on commit 3d7f83a

Please sign in to comment.