All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.groovy.client.ClientExtensions.groovy Maven / Gradle / Ivy

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.elasticsearch.groovy.client

import org.elasticsearch.action.ListenableActionFuture
import org.elasticsearch.action.bulk.BulkRequest
import org.elasticsearch.action.bulk.BulkResponse
import org.elasticsearch.action.count.CountRequest
import org.elasticsearch.action.count.CountResponse
import org.elasticsearch.action.delete.DeleteRequest
import org.elasticsearch.action.delete.DeleteResponse
import org.elasticsearch.action.deletebyquery.DeleteByQueryRequest
import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse
import org.elasticsearch.action.explain.ExplainRequest
import org.elasticsearch.action.explain.ExplainResponse
import org.elasticsearch.action.fieldstats.FieldStatsRequest
import org.elasticsearch.action.fieldstats.FieldStatsResponse
import org.elasticsearch.action.get.GetRequest
import org.elasticsearch.action.get.GetResponse
import org.elasticsearch.action.get.MultiGetRequest
import org.elasticsearch.action.get.MultiGetResponse
import org.elasticsearch.action.index.IndexRequest
import org.elasticsearch.action.index.IndexResponse
import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequest
import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptResponse
import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequest
import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse
import org.elasticsearch.action.mlt.MoreLikeThisRequest
import org.elasticsearch.action.percolate.MultiPercolateRequest
import org.elasticsearch.action.percolate.MultiPercolateResponse
import org.elasticsearch.action.percolate.PercolateRequest
import org.elasticsearch.action.percolate.PercolateResponse
import org.elasticsearch.action.search.ClearScrollRequest
import org.elasticsearch.action.search.ClearScrollResponse
import org.elasticsearch.action.search.MultiSearchRequest
import org.elasticsearch.action.search.MultiSearchResponse
import org.elasticsearch.action.search.SearchRequest
import org.elasticsearch.action.search.SearchResponse
import org.elasticsearch.action.search.SearchScrollRequest
import org.elasticsearch.action.suggest.SuggestRequest
import org.elasticsearch.action.suggest.SuggestResponse
import org.elasticsearch.action.termvector.MultiTermVectorsRequest
import org.elasticsearch.action.termvector.MultiTermVectorsResponse
import org.elasticsearch.action.termvector.TermVectorRequest
import org.elasticsearch.action.termvector.TermVectorResponse
import org.elasticsearch.action.update.UpdateRequest
import org.elasticsearch.action.update.UpdateResponse
import org.elasticsearch.client.AdminClient
import org.elasticsearch.client.Client
import org.elasticsearch.client.Requests
import org.elasticsearch.common.settings.Settings

/**
 * {@code ClientExtensions} provides extensions to the Elasticsearch {@link Client} to enable Groovy-friendly
 * requests.
 * 

* This enables support for using {@link Closure}s to configure (and execute) the various action requests. For example: *

 * ListenableActionFuture<IndexResponse> indexResponse = client.index {
 *     index "index-name"
 *     type "type-name"
 *     id "id-value"
 *     source {
 *         name "kimchy"
 *         timestamp = new Date()
 *         nested {
 *             other = 1.23
 *             data {
 *                 count = 1234
 *                 values = ["abc", "def"]
 *             }
 *         }
 *     }
 * }
 * 
* The above code would create an {@link IndexRequest}, call {@link IndexRequest#index(String) index("index-name")}, * {@link IndexRequest#type(String) type("type-name")}, {@link IndexRequest#id(String) id("id-value")}, and {@link * IndexRequest#source source(Closure)}. *

* Note: All requests made by the {@code ClientExtensions} methods are asynchronous and they are invoked immediately. * To block until a response is returned, then call {@link ListenableActionFuture#actionGet()} or one of its overloads * that allow you to provide a timeout. */ class ClientExtensions extends AbstractClientExtensions { /** * Get the admin client that can be used to perform administrative operations. * * @param self The {@code this} reference for the {@link Client} * @return Always {@link Client#admin()}. * @throws NullPointerException if {@code self} is {@code null} */ static AdminClient getAdmin(Client self) { self.admin() } /** * Get the client {@link Settings}. * * @param self The {@code this} reference for the {@link Client} * @return Always {@link Client#settings()}. * @throws NullPointerException if {@code self} is {@code null} */ static Settings getSettings(Client self) { self.settings() } // REQUEST/RESPONSE /** * Index a document associated with a given index and type, then get the future result. *

* The id is optional. If it is not provided, one will be generated automatically. *

     * IndexResponse response = client.index {
     *   index "my-index"
     *   type "my-type"
     *   // optional ID
     *   id "my-id"
     *   source {
     *     user = "kimchy"
     *     postedDate = new Date()
     *     nested {
     *       object {
     *         field = 123
     *       }
     *     }
     *   }
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link IndexRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture index(Client self, Closure requestClosure) { doRequestAsync(self, Requests.indexRequest(), requestClosure, self.&index) } /** * Executes a bulk of index, update, or delete operations. *

* An example usage of the Bulk API would be to bulk index your own data, which may make sense to wrap for your own * convenience: *

     * BulkResponse bulkIndex(String indexName,
     *                        String typeName,
     *                        List<Closure> sources) {
     *     client.bulk {
     *         // Note: This creates a List<IndexRequest>
     *         add sources.collect {
     *             Requests.indexRequest(indexName).type(typeName).source(it)
     *         }
     *     }.actionGet()
     * }
     * 
* Such a method could then be used to build {@code List}s of {@code Closure}s to more clearly bulk index. *
     * // Index three documents
     * BulkResponse response = bulkIndex("my-index", "my-type", [
     *     { user = "kimchy" },
     *     { user = "pickypg" },
     *     { user = "dadoonet" }
     * ])
     * 
* You could build the {@code List} dynamically in a more realistic example: *
     * Closure convertMyObject(MyObject value) {
     *     // return is used explicitly so that the compiler knows this is
     *     //  not an arbitrary code block
     *     return {
     *         user = value.username
     *     }
     * }
     *
     * void indexDocuments(List<MyObject> objects) {
     *     // objects.collect(this.&convertMyObject) returns a List with each item the 1:1 result of calling
     *     //   convertMyObject(objects[i])
     *     bulkIndex("my-index", "my-type", objects.collect(this.&convertMyObject))
     * }
     * 
* If you wanted to mix-and-match indexing, updating, and deletions, then this approach would have to be modified, * but for the common use case of only adding new documents, then this should simplify a lot of bulk insertions. If * you wanted to mix-and-match different indices or types, then a variation of this could be created using the * Groovy-supplied {@code with} method at the expense of complicating each {@code Closure}. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link BulkRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture bulk(Client self, Closure requestClosure) { doRequestAsync(self, new BulkRequest(), requestClosure, self.&bulk) } /** * Updates a document based on a script or given source. *

* For an unscripted example, you could simply replace fields (all or partially) in an existing document: *

     * UpdateResponse response = client.update {
     *     index "my-index"
     *     type "my-type"
     *     id "my-id"
     *     // Add/replace document fields
     *     doc {
     *         new_field = 456.7
     *     }
     * }.actionGet()
     * 
* For a scripted example, you might do something like: *
     * UpdateResponse response = client.update {
     *     index "my-index"
     *     type "my-type"
     *     id "my-id"
     *     script "ctx._source.counter += count"
     *     scriptParams {
     *         count = 1
     *     }
     *     upsert {
     *         some {
     *           other {
     *               info = "indexed if document does not exist"
     *           }
     *         }
     *         counter = 1
     *     }
     * }.actionGet()
     * 
* Note: All updates are really delete-then-index operations and partial updates require that the * document's source be stored (defaults to {@code true}, but changeable in the type's mapping). * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link UpdateRequest}. * @return Never {@code null}. */ static ListenableActionFuture update(Client self, Closure requestClosure) { doRequestAsync(self, new UpdateRequest(), requestClosure, self.&update) } /** * Deletes a document from the index based on the index, type and id. *
     * DeleteResponse response = client.delete {
     *     index "my-index"
     *     type "my-type"
     *     id "my-id"
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link DeleteRequest}. * @return Never {@code null}. */ static ListenableActionFuture delete(Client self, Closure requestClosure) { doRequestAsync(self, new DeleteRequest(), requestClosure, self.&delete) } /** * Deletes all documents from one or more indices based on a query. *
     * DeleteByQueryResponse response = client.deleteByQuery {
     *     indices "my-index"
     *     types "my-type"
     *     source {
     *         query {
     *             range {
     *                 // Note: "value" is the field name
     *                 value {
     *                     gte = 100
     *                 }
     *             }
     *         }
     *     }
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link DeleteByQueryRequest}. * @return Never {@code null}. */ static ListenableActionFuture deleteByQuery(Client self, Closure requestClosure) { doRequestAsync(self, Requests.deleteByQueryRequest(), requestClosure, self.&deleteByQuery) } /** * Gets a document from the index based on the index, type and id. *

* Note: Get retrievals are performed in real time. *

     * GetResponse response = client.get {
     *     index "my-index"
     *     type "my-type"
     *     id "my-id"
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link GetRequest}. * @return Never {@code null}. */ static ListenableActionFuture get(Client self, Closure requestClosure) { // index is expected to be set by the closure doRequestAsync(self, Requests.getRequest(null), requestClosure, self.&get) } /** * Multi-get documents. This provides the mechanism to perform bulk requests (as opposed to bulk indexing) to avoid * unnecessary back-and-forth requests. *
     * MultiGetResponse response = client.multiGet {
     *     // You can still do code constructs in your Closures, like
     *     //  this loop to invoke add multiple times
     *     for (String id : ["my-id1", "my-id2", "my-id3"]) {
     *         add "my-index", "my-type", id
     *     }
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link MultiGetRequest}. * @return Never {@code null}. */ static ListenableActionFuture multiGet(Client self, Closure requestClosure) { doRequestAsync(self, new MultiGetRequest(), requestClosure, self.&multiGet) } /** * Request suggestion matching for a specific query. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link SuggestRequest}. * @return Never {@code null}. */ static ListenableActionFuture suggest(Client self, Closure requestClosure) { doRequestAsync(self, new SuggestRequest(), requestClosure, self.&suggest) } /** * Search across one or more indices and one or more types with a query. *
     * SearchResponse response = client.search {
     *     indices "my-index1", "my-index2"
     *     types "my-types1", "my-types2"
     *     source {
     *         query {
     *             match_all { }
     *         }
     *     }
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link SearchRequest}. * @return Never {@code null}. */ static ListenableActionFuture search(Client self, Closure requestClosure) { doRequestAsync(self, Requests.searchRequest(), requestClosure, self.&search) } /** * Perform multiple search requests similar to multi-get. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link MultiSearchRequest}. * @return Never {@code null}. */ static ListenableActionFuture multiSearch(Client self, Closure requestClosure) { doRequestAsync(self, new MultiSearchRequest(), requestClosure, self.&multiSearch) } /** * Request a count of documents matching a specified query. *
     * CountResponse response = client.count {
     *     indices "my-index1", "my-index2"
     *     types "my-types1", "my-types2"
     *     source {
     *         query {
     *             match_all { }
     *         }
     *     }
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link CountRequest}. * @return Never {@code null}. */ static ListenableActionFuture count(Client self, Closure requestClosure) { doRequestAsync(self, Requests.countRequest(), requestClosure, self.&count) } /** * A search scroll request to continue searching a previous scrollable search request. *
     * // Open the scan
     * SearchResponse searchResponse = client.search {
     *     indices "my-index"
     *     types "my-type"
     *     source {
     *         query {
     *             match_all { }
     *         }
     *         // Note: Size is per shard! 5 shards means 5000 documents per response
     *         size = 1000
     *     }
     *     searchType SearchType.SCAN
     *     // The time that the scroll stays open should be the minimum duration
     *     //  required
     *     scroll "10s"
     * }.actionGet()
     *
     * // Scroll through the results (like a database cursor)
     * SearchResponse response = client.searchScroll {
     *     // Note: next call should use response.scrollId!
     *     scrollId searchResponse.scrollId
     *     // keep the _next_ window open
     *     scroll "10s"
     * }.actionGet()
     * 
* Note: Each {@link SearchResponse} will contain a new ID to use for subsequent requests. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link SearchScrollRequest}. * @return Never {@code null}. */ static ListenableActionFuture searchScroll(Client self, Closure requestClosure) { doRequestAsync(self, new SearchScrollRequest(), requestClosure, self.&searchScroll) } /** * Clears the search contexts associated with specified Scroll IDs. *
     * ClearScrollResponse response = client.clearScroll {
     *     addScrollId lastScrollId
     * }.actionGet()
     * 
* Technically, this is not a necessary action following any scan/scroll action, but you should always do * it to optimistically clean up resources. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link ClearScrollRequest}. * @return Never {@code null}. */ static ListenableActionFuture clearScroll(Client self, Closure requestClosure) { doRequestAsync(self, new ClearScrollRequest(), requestClosure, self.&clearScroll) } /** * An action that is the term vectors for a specific document. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link TermVectorRequest}. * @return Never {@code null}. */ static ListenableActionFuture termVector(Client self, Closure requestClosure) { // index, type and id are expected to be set by the closure doRequestAsync(self, new TermVectorRequest(null, null, null), requestClosure, self.&termVector) } /** * Multi-get term vectors. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link MultiTermVectorsRequest}. * @return Never {@code null}. */ static ListenableActionFuture multiTermVectors(Client self, Closure requestClosure) { doRequestAsync(self, new MultiTermVectorsRequest(), requestClosure, self.&multiTermVectors) } /** * Percolates a requesting the matching documents. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link PercolateRequest}. * @return Never {@code null}. */ static ListenableActionFuture percolate(Client self, Closure requestClosure) { doRequestAsync(self, new PercolateRequest(), requestClosure, self.&percolate) } /** * Performs multiple percolate requests. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link MultiPercolateRequest}. * @return Never {@code null}. */ static ListenableActionFuture multiPercolate(Client self, Closure requestClosure) { doRequestAsync(self, new MultiPercolateRequest(), requestClosure, self.&multiPercolate) } /** * Computes a score explanation for the specified request. * * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link ExplainRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture explain(Client self, Closure requestClosure) { doRequestAsync(self, new ExplainRequest(null, null, null), requestClosure, self.&explain) } /** * Put (set/add) the indexed script to be used by other requests. *
     * PutIndexedScriptResponse response = client.putIndexedScript {
     *     id 'my-script-name'
     *     // NOTE1: This will be the Groovy runtime within Elasticsearch
     *     //        and not the Groovy client (this)!
     *     scriptLang 'groovy'
     *     source {
     *         // NOTE2: The script is [in this case] Groovy, but it must
     *         //        be a string that is interpreted on the server
     *         // NOTE3: "count" is a script parameter that must be filled
     *         //        in by the associated update request that makes
     *         //        use of this script
     *         script = "ctx._source.count += count"
     *     }
     * }.actionGet()
     * 
* Once the above script is added, then you could make use of it by using it with an {@link UpdateRequest}. *
     * UpdateResponse updateResponse = client.update {
     *     index indexName
     *     type typeName
     *     id docId
     *     source {
     *         script_id 'testPutIndexedScriptRequest'
     *         lang 'groovy'
     *         params {
     *             count = 5
     *         }
     *     }
     * }.actionGet()
     *
     * @param self The {@code this} reference for the {@link Client}
     * @param requestClosure The map-like closure that configures the {@link PutIndexedScriptRequest}.
     * @return Never {@code null}.
     * @throws NullPointerException if any parameter is {@code null}
     */
    static ListenableActionFuture putIndexedScript(Client self, Closure requestClosure) {
        doRequestAsync(self, new PutIndexedScriptRequest(), requestClosure, self.&putIndexedScript)
    }

    /**
     * Get an indexed script.
     * 
     * GetIndexedScriptResponse response = client.getIndexedScript {
     *     id 'my-script-name'
     *     scriptLang 'groovy'
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link GetIndexedScriptRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture getIndexedScript(Client self, Closure requestClosure) { doRequestAsync(self, new GetIndexedScriptRequest(), requestClosure, self.&getIndexedScript) } /** * Delete an indexed script. *
     * DeleteIndexedScriptResponse response = client.deleteIndexedScript {
     *     id 'my-script-name'
     *     scriptLang 'groovy'
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link DeleteIndexedScriptRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture deleteIndexedScript(Client self, Closure requestClosure) { doRequestAsync(self, new DeleteIndexedScriptRequest(), requestClosure, self.&deleteIndexedScript) } /** * A more like this action to search for documents that are "like" a specific document. *

* This method may be deprecated in favor of one that does not require the {@code index} to be supplied to * the method in the future. * * @param self The {@code this} reference for the {@link Client} * @param index The index to load the document(s) from * @param requestClosure The map-like closure that configures the {@link MoreLikeThisRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} except {@code index} */ static ListenableActionFuture moreLikeThis(Client self, String index, Closure requestClosure) { // the only one that _requires_ the index as a parameter/constructor arg (no public setter) doRequestAsync(self, Requests.moreLikeThisRequest(index), requestClosure, self.&moreLikeThis) } /** * Collect the field stats at the specified level for the specified fields. *

* The level defaults to "cluster". *

     * FieldStatsResponse response = client.fieldStats {
     *     fields ['x', 'y', 'z']
     * }.actionGet()
     * 
* * @param self The {@code this} reference for the {@link Client} * @param requestClosure The map-like closure that configures the {@link FieldStatsRequest}. * @return Never {@code null}. * @throws NullPointerException if any parameter is {@code null} */ static ListenableActionFuture fieldStats(Client self, Closure requestClosure) { doRequestAsync(self, new FieldStatsRequest(), requestClosure, self.&fieldStats) } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy