This is an automated email from the ASF dual-hosted git repository. nfilotto pushed a commit to branch CAMEL-19752/add-document-only-mode-3.20 in repository https://gitbox.apache.org/repos/asf/camel.git
commit f9f076b515c5561e394be71f7036dc98d45085fa Author: Nicolas Filotto <nfilo...@talend.com> AuthorDate: Fri Sep 1 16:13:14 2023 +0200 CAMEL-19752: camel-elasticsearch - Add a doc-only mode to ease migration --- .../camel/catalog/components/elasticsearch.json | 5 +- .../es/ElasticsearchComponentConfigurer.java | 6 + .../es/ElasticsearchEndpointConfigurer.java | 6 + .../es/ElasticsearchEndpointUriFactory.java | 3 +- .../apache/camel/component/es/elasticsearch.json | 5 +- .../src/main/docs/elasticsearch-component.adoc | 12 +- .../camel/component/es/ElasticsearchComponent.java | 18 +++ .../component/es/ElasticsearchConfiguration.java | 17 +++ .../camel/component/es/ElasticsearchConstants.java | 2 + .../camel/component/es/ElasticsearchProducer.java | 5 + .../ElasticsearchActionRequestConverter.java | 41 +++++- .../es/integration/ElasticsearchBulkIT.java | 7 +- ...ElasticsearchGetSearchDeleteExistsUpdateIT.java | 161 ++++++++++++++++++--- .../dsl/ElasticsearchComponentBuilderFactory.java | 20 +++ .../dsl/ElasticsearchEndpointBuilderFactory.java | 53 +++++++ 15 files changed, 327 insertions(+), 34 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/elasticsearch.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/elasticsearch.json index c3bdd2e6188..81a02fe7d28 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/elasticsearch.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/elasticsearch.json @@ -23,6 +23,7 @@ }, "componentProperties": { "connectionTimeout": { "kind": "property", "displayName": "Connection Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The time in ms to wait before connection will timeout." }, + "enableDocumentOnlyMode": { "kind": "property", "displayName": "Enable Document Only Mode", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether the body of the message contains only documents. By default, it is set to false to be able to do the same requests as what the Document API supports (see https:\/\/www.elastic.co\/guide\ [...] "hostAddresses": { "kind": "property", "displayName": "Host Addresses", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list with ip:port formatted remote transport addresses to use. The ip and port options must be left blank for hostAddresses to be considered instead." }, "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...] "maxRetryTimeout": { "kind": "property", "displayName": "Max Retry Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The time in ms before retry" }, @@ -46,12 +47,14 @@ "scrollKeepAliveMs": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SCROLL_KEEP_ALIVE_MS" }, "useScroll": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Set to true to enable scroll usage", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SCROLL" }, "size": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SIZE" }, - "from": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_FROM" } + "from": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_FROM" }, + "enableDocumentOnlyMode": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Indicates whether the body of the message contains only documents.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_DOCUMENT_MODE" } }, "properties": { "clusterName": { "kind": "path", "displayName": "Cluster Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Name of the cluster" }, "connectionTimeout": { "kind": "parameter", "displayName": "Connection Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "The time in ms to wait before connection will timeout." }, "disconnect": { "kind": "parameter", "displayName": "Disconnect", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Disconnect after it finish calling the producer" }, + "enableDocumentOnlyMode": { "kind": "parameter", "displayName": "Enable Document Only Mode", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Indicates whether the body of the message contains only documents. By default, it [...] "from": { "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Starting index of the response." }, "hostAddresses": { "kind": "parameter", "displayName": "Host Addresses", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Comma separated list with ip:port formatted remote transport addresses to use." }, "indexName": { "kind": "parameter", "displayName": "Index Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "The name of the index to act against" }, diff --git a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchComponentConfigurer.java b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchComponentConfigurer.java index 8b3a0236c01..3cfb3336d9d 100644 --- a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchComponentConfigurer.java +++ b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchComponentConfigurer.java @@ -28,6 +28,8 @@ public class ElasticsearchComponentConfigurer extends PropertyConfigurerSupport case "client": target.setClient(property(camelContext, org.elasticsearch.client.RestClient.class, value)); return true; case "connectiontimeout": case "connectionTimeout": target.setConnectionTimeout(property(camelContext, int.class, value)); return true; + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": target.setEnableDocumentOnlyMode(property(camelContext, boolean.class, value)); return true; case "enablessl": case "enableSSL": target.setEnableSSL(property(camelContext, boolean.class, value)); return true; case "enablesniffer": @@ -65,6 +67,8 @@ public class ElasticsearchComponentConfigurer extends PropertyConfigurerSupport case "client": return org.elasticsearch.client.RestClient.class; case "connectiontimeout": case "connectionTimeout": return int.class; + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": return boolean.class; case "enablessl": case "enableSSL": return boolean.class; case "enablesniffer": @@ -98,6 +102,8 @@ public class ElasticsearchComponentConfigurer extends PropertyConfigurerSupport case "client": return target.getClient(); case "connectiontimeout": case "connectionTimeout": return target.getConnectionTimeout(); + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": return target.isEnableDocumentOnlyMode(); case "enablessl": case "enableSSL": return target.isEnableSSL(); case "enablesniffer": diff --git a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointConfigurer.java b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointConfigurer.java index 02657933b8d..0efbe912840 100644 --- a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointConfigurer.java +++ b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointConfigurer.java @@ -28,6 +28,8 @@ public class ElasticsearchEndpointConfigurer extends PropertyConfigurerSupport i case "disconnect": target.getConfiguration().setDisconnect(property(camelContext, boolean.class, value)); return true; case "documentclass": case "documentClass": target.getConfiguration().setDocumentClass(property(camelContext, java.lang.Class.class, value)); return true; + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": target.getConfiguration().setEnableDocumentOnlyMode(property(camelContext, boolean.class, value)); return true; case "enablessl": case "enableSSL": target.getConfiguration().setEnableSSL(property(camelContext, boolean.class, value)); return true; case "enablesniffer": @@ -69,6 +71,8 @@ public class ElasticsearchEndpointConfigurer extends PropertyConfigurerSupport i case "disconnect": return boolean.class; case "documentclass": case "documentClass": return java.lang.Class.class; + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": return boolean.class; case "enablessl": case "enableSSL": return boolean.class; case "enablesniffer": @@ -111,6 +115,8 @@ public class ElasticsearchEndpointConfigurer extends PropertyConfigurerSupport i case "disconnect": return target.getConfiguration().isDisconnect(); case "documentclass": case "documentClass": return target.getConfiguration().getDocumentClass(); + case "enabledocumentonlymode": + case "enableDocumentOnlyMode": return target.getConfiguration().isEnableDocumentOnlyMode(); case "enablessl": case "enableSSL": return target.getConfiguration().isEnableSSL(); case "enablesniffer": diff --git a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointUriFactory.java b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointUriFactory.java index 44fa48f13a9..b192be6c41d 100644 --- a/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointUriFactory.java +++ b/components/camel-elasticsearch/src/generated/java/org/apache/camel/component/es/ElasticsearchEndpointUriFactory.java @@ -21,12 +21,13 @@ public class ElasticsearchEndpointUriFactory extends org.apache.camel.support.co private static final Set<String> SECRET_PROPERTY_NAMES; private static final Set<String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(20); + Set<String> props = new HashSet<>(21); props.add("certificatePath"); props.add("clusterName"); props.add("connectionTimeout"); props.add("disconnect"); props.add("documentClass"); + props.add("enableDocumentOnlyMode"); props.add("enableSSL"); props.add("enableSniffer"); props.add("from"); diff --git a/components/camel-elasticsearch/src/generated/resources/org/apache/camel/component/es/elasticsearch.json b/components/camel-elasticsearch/src/generated/resources/org/apache/camel/component/es/elasticsearch.json index c3bdd2e6188..81a02fe7d28 100644 --- a/components/camel-elasticsearch/src/generated/resources/org/apache/camel/component/es/elasticsearch.json +++ b/components/camel-elasticsearch/src/generated/resources/org/apache/camel/component/es/elasticsearch.json @@ -23,6 +23,7 @@ }, "componentProperties": { "connectionTimeout": { "kind": "property", "displayName": "Connection Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The time in ms to wait before connection will timeout." }, + "enableDocumentOnlyMode": { "kind": "property", "displayName": "Enable Document Only Mode", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Indicates whether the body of the message contains only documents. By default, it is set to false to be able to do the same requests as what the Document API supports (see https:\/\/www.elastic.co\/guide\ [...] "hostAddresses": { "kind": "property", "displayName": "Host Addresses", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Comma separated list with ip:port formatted remote transport addresses to use. The ip and port options must be left blank for hostAddresses to be considered instead." }, "lazyStartProducer": { "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during star [...] "maxRetryTimeout": { "kind": "property", "displayName": "Max Retry Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "description": "The time in ms before retry" }, @@ -46,12 +47,14 @@ "scrollKeepAliveMs": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SCROLL_KEEP_ALIVE_MS" }, "useScroll": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Set to true to enable scroll usage", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SCROLL" }, "size": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The size of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_SIZE" }, - "from": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_FROM" } + "from": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The starting index of the response.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_FROM" }, + "enableDocumentOnlyMode": { "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "false", "description": "Indicates whether the body of the message contains only documents.", "constantName": "org.apache.camel.component.es.ElasticsearchConstants#PARAM_DOCUMENT_MODE" } }, "properties": { "clusterName": { "kind": "path", "displayName": "Cluster Name", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Name of the cluster" }, "connectionTimeout": { "kind": "parameter", "displayName": "Connection Timeout", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "The time in ms to wait before connection will timeout." }, "disconnect": { "kind": "parameter", "displayName": "Disconnect", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Disconnect after it finish calling the producer" }, + "enableDocumentOnlyMode": { "kind": "parameter", "displayName": "Enable Document Only Mode", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Indicates whether the body of the message contains only documents. By default, it [...] "from": { "kind": "parameter", "displayName": "From", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Starting index of the response." }, "hostAddresses": { "kind": "parameter", "displayName": "Host Addresses", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "Comma separated list with ip:port formatted remote transport addresses to use." }, "indexName": { "kind": "parameter", "displayName": "Index Name", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.es.ElasticsearchConfiguration", "configurationField": "configuration", "description": "The name of the index to act against" }, diff --git a/components/camel-elasticsearch/src/main/docs/elasticsearch-component.adoc b/components/camel-elasticsearch/src/main/docs/elasticsearch-component.adoc index 9d1b0cd25e6..421bd45b91e 100644 --- a/components/camel-elasticsearch/src/main/docs/elasticsearch-component.adoc +++ b/components/camel-elasticsearch/src/main/docs/elasticsearch-component.adoc @@ -101,8 +101,10 @@ You must set the name of the target index by setting the message header with the |Update |*byte[]*, *InputStream*, *String*, *Reader*, *Map* or any document type content to update |Updates content to an index and returns the content's indexId in the body. You can set the name of the target index by setting the message header with the key "indexName". You can set the indexId by setting the message header with -the key "indexId". - +the key "indexId". Be aware of the fact that unlike the component _camel-elasticsearch-rest_, by default, the expected content of +an update request must be the same as what the https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html[Update API expects], consequently +if you want to update one part of an existing document, you need to embed the content to update into a "doc" object. To change the default behavior, it is possible +to configure it globally at the component level thanks to the option _enableDocumentOnlyMode_ or by request by setting the header _ElasticsearchConstants.PARAM_DOCUMENT_MODE_ to true. |Ping |None |Pings the Elasticsearch cluster and returns true if the ping succeeded, false otherwise |=== @@ -183,7 +185,7 @@ from("direct:search") [source,java] ---- -String query = "{\"query\":{\"match\":{\"doc.content\":\"new release of ApacheCamel\"}}}"; +String query = "{\"query\":{\"match\":{\"content\":\"new release of ApacheCamel\"}}}"; HitsMetadata<?> response = template.requestBody("direct:search", query, HitsMetadata.class); ---- @@ -193,7 +195,7 @@ Search on specific field(s) using Map. [source,java] ---- Map<String, Object> actualQuery = new HashMap<>(); -actualQuery.put("doc.content", "new release of ApacheCamel"); +actualQuery.put("content", "new release of ApacheCamel"); Map<String, Object> match = new HashMap<>(); match.put("match", actualQuery); @@ -222,7 +224,7 @@ from("direct:search") [source,java] ---- -String query = "{\"query\":{\"match\":{\"doc.content\":\"new release of ApacheCamel\"}}}"; +String query = "{\"query\":{\"match\":{\"content\":\"new release of ApacheCamel\"}}}"; try (ElasticsearchScrollRequestIterator response = template.requestBody("direct:search", query, ElasticsearchScrollRequestIterator.class)) { // do something smart with results } diff --git a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchComponent.java b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchComponent.java index f59751d9257..c46ccc0e9d6 100644 --- a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchComponent.java +++ b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchComponent.java @@ -45,6 +45,8 @@ public class ElasticsearchComponent extends DefaultComponent { private int maxRetryTimeout = ElasticsearchConstants.MAX_RETRY_TIMEOUT; @Metadata(defaultValue = "" + ElasticsearchConstants.DEFAULT_CONNECTION_TIMEOUT) private int connectionTimeout = ElasticsearchConstants.DEFAULT_CONNECTION_TIMEOUT; + @Metadata(defaultValue = "false") + private boolean enableDocumentOnlyMode; @Metadata(label = "security", secret = true) private String user; @Metadata(label = "security", secret = true) @@ -84,6 +86,7 @@ public class ElasticsearchComponent extends DefaultComponent { config.setSnifferInterval(this.getSnifferInterval()); config.setSniffAfterFailureDelay(this.getSniffAfterFailureDelay()); config.setClusterName(remaining); + config.setEnableDocumentOnlyMode(this.isEnableDocumentOnlyMode()); Endpoint endpoint = new ElasticsearchEndpoint(uri, this, config, client); setProperties(endpoint, parameters); @@ -202,6 +205,21 @@ public class ElasticsearchComponent extends DefaultComponent { this.certificatePath = certificatePath; } + /** + * Indicates whether the body of the message contains only documents. By default, it is set to false to be able to + * do the same requests as what the Document API supports + * (see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html for more details). + * To ease the migration of routes based on the legacy component camel-elasticsearch-rest, you should consider + * enabling the mode especially if your routes do update operations. + */ + public boolean isEnableDocumentOnlyMode() { + return enableDocumentOnlyMode; + } + + public void setEnableDocumentOnlyMode(boolean enableDocumentOnlyMode) { + this.enableDocumentOnlyMode = enableDocumentOnlyMode; + } + /** * The time in ms before retry */ diff --git a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConfiguration.java b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConfiguration.java index 34b3676ef86..a6e73b2dd3c 100644 --- a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConfiguration.java +++ b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConfiguration.java @@ -53,6 +53,8 @@ public class ElasticsearchConfiguration { private int maxRetryTimeout = ElasticsearchConstants.MAX_RETRY_TIMEOUT; @UriParam(defaultValue = "" + ElasticsearchConstants.DEFAULT_CONNECTION_TIMEOUT) private int connectionTimeout = ElasticsearchConstants.DEFAULT_CONNECTION_TIMEOUT; + @UriParam(defaultValue = "false") + private boolean enableDocumentOnlyMode; @UriParam private boolean disconnect; @UriParam(label = "security") @@ -312,4 +314,19 @@ public class ElasticsearchConfiguration { public void setDocumentClass(Class<?> documentClass) { this.documentClass = documentClass; } + + /** + * Indicates whether the body of the message contains only documents. By default, it is set to false to be able to + * do the same requests as what the Document API supports + * (see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html for more details). + * To ease the migration of routes based on the legacy component camel-elasticsearch-rest, you should consider + * enabling the mode especially if your routes do update operations. + */ + public boolean isEnableDocumentOnlyMode() { + return enableDocumentOnlyMode; + } + + public void setEnableDocumentOnlyMode(boolean enableDocumentOnlyMode) { + this.enableDocumentOnlyMode = enableDocumentOnlyMode; + } } diff --git a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConstants.java b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConstants.java index 40c743ae089..764c1f9a9fb 100644 --- a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConstants.java +++ b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchConstants.java @@ -41,6 +41,8 @@ public interface ElasticsearchConstants { String PARAM_SIZE = "size"; @Metadata(description = "The starting index of the response.", javaType = "Integer") String PARAM_FROM = "from"; + @Metadata(description = "Indicates whether the body of the message contains only documents.", javaType = "Boolean", defaultValue = "false") + String PARAM_DOCUMENT_MODE = "enableDocumentOnlyMode"; String PROPERTY_SCROLL_ES_QUERY_COUNT = "CamelElasticsearchScrollQueryCount"; diff --git a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchProducer.java b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchProducer.java index 5e5ea1a1787..4b32e179cfc 100644 --- a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchProducer.java +++ b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/ElasticsearchProducer.java @@ -176,6 +176,11 @@ class ElasticsearchProducer extends DefaultAsyncProducer { message.setHeader(ElasticsearchConstants.PARAM_FROM, configuration.getFrom()); } + Boolean enableDocumentOnlyMode = message.getHeader(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.class); + if (enableDocumentOnlyMode == null) { + message.setHeader(ElasticsearchConstants.PARAM_DOCUMENT_MODE, configuration.isEnableDocumentOnlyMode()); + } + boolean configWaitForActiveShards = false; Integer waitForActiveShards = message.getHeader(ElasticsearchConstants.PARAM_WAIT_FOR_ACTIVE_SHARDS, Integer.class); if (waitForActiveShards == null) { diff --git a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/converter/ElasticsearchActionRequestConverter.java b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/converter/ElasticsearchActionRequestConverter.java index 19835023e28..4e48992c0c2 100644 --- a/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/converter/ElasticsearchActionRequestConverter.java +++ b/components/camel-elasticsearch/src/main/java/org/apache/camel/component/es/converter/ElasticsearchActionRequestConverter.java @@ -121,17 +121,19 @@ public final class ElasticsearchActionRequestConverter { return builder.id(exchange.getIn().getHeader(ElasticsearchConstants.PARAM_INDEX_ID, String.class)); } UpdateRequest.Builder<?, Object> builder = new UpdateRequest.Builder<>(); + Boolean enableDocumentOnlyMode = exchange.getIn().getHeader(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.FALSE, Boolean.class); + Mode mode = enableDocumentOnlyMode == Boolean.TRUE ? Mode.DOCUMENT_ONLY : Mode.DEFAULT; if (document instanceof byte[]) { - builder.withJson(new ByteArrayInputStream((byte[]) document)); + mode.addDocToUpdateRequestBuilder(builder, new ByteArrayInputStream((byte[]) document)); } else if (document instanceof InputStream) { - builder.withJson((InputStream) document); + mode.addDocToUpdateRequestBuilder(builder, (InputStream) document); } else if (document instanceof String) { - builder.withJson(new StringReader((String) document)); + mode.addDocToUpdateRequestBuilder(builder, new StringReader((String) document)); } else if (document instanceof Reader) { - builder.withJson((Reader) document); + mode.addDocToUpdateRequestBuilder(builder, (Reader) document); } else if (document instanceof Map) { ObjectMapper objectMapper = new ObjectMapper(); - builder.withJson(new StringReader(objectMapper.writeValueAsString(document))); + mode.addDocToUpdateRequestBuilder(builder, new StringReader(objectMapper.writeValueAsString(document))); } else { builder.doc(document); } @@ -293,4 +295,33 @@ public final class ElasticsearchActionRequestConverter { } return null; } + + enum Mode { + DEFAULT { + @Override + protected void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, InputStream in) { + builder.withJson(in); + } + + @Override + protected void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, Reader in) { + builder.withJson(in); + } + + }, + DOCUMENT_ONLY { + @Override + protected void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, InputStream in) { + builder.doc(JsonData.from(in)); + } + + @Override + protected void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, Reader in) { + builder.doc(JsonData.from(in)); + } + }; + + protected abstract void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, InputStream in); + protected abstract void addDocToUpdateRequestBuilder(UpdateRequest.Builder<?, Object> builder, Reader in); + } } diff --git a/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchBulkIT.java b/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchBulkIT.java index bd5ab8f5bac..a5bc39e2cbe 100644 --- a/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchBulkIT.java +++ b/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchBulkIT.java @@ -35,6 +35,7 @@ import co.elastic.clients.elasticsearch.core.bulk.DeleteOperation; import co.elastic.clients.elasticsearch.core.bulk.IndexOperation; import co.elastic.clients.elasticsearch.core.bulk.UpdateAction; import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation; +import co.elastic.clients.json.JsonData; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.camel.builder.RouteBuilder; import org.junit.jupiter.api.Test; @@ -220,10 +221,10 @@ class ElasticsearchBulkIT extends ElasticsearchTestSupport { .index("twitter").id(indexId) .action( new UpdateAction.Builder<>() - .withJson( + .doc(JsonData.from( new StringReader( - String.format("{ \"doc\": {\"%skey2\": \"%svalue2\"}}", - createPrefix(), createPrefix()))) + String.format("{\"%skey2\": \"%svalue2\"}", + createPrefix(), createPrefix())))) .build()); @SuppressWarnings("unchecked") List<BulkResponseItem> response = template.requestBody("direct:bulk", List.of(builder), List.class); diff --git a/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchGetSearchDeleteExistsUpdateIT.java b/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchGetSearchDeleteExistsUpdateIT.java index a6673bf931b..c79fd106865 100644 --- a/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchGetSearchDeleteExistsUpdateIT.java +++ b/components/camel-elasticsearch/src/test/java/org/apache/camel/component/es/integration/ElasticsearchGetSearchDeleteExistsUpdateIT.java @@ -277,12 +277,11 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor Map<String, Object> headers = new HashMap<>(); headers.put(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.Bulk); headers.put(ElasticsearchConstants.PARAM_INDEX_NAME, "twitter"); - template.requestBodyAndHeaders("direct:start", List.of(Map.of("doc", map1), Map.of("doc", map2)), headers, - String.class); + template.requestBodyAndHeaders("direct:start", List.of(map1, map2), headers, String.class); // No match Map<String, Object> actualQuery = new HashMap<>(); - actualQuery.put("doc.testSearchWithMapQuery1", "bar"); + actualQuery.put("testSearchWithMapQuery1", "bar"); Map<String, Object> match = new HashMap<>(); match.put("match", actualQuery); Map<String, Object> query = new HashMap<>(); @@ -293,7 +292,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals(0, response.total().value(), "response hits should be == 0"); // Match - actualQuery.put("doc.testSearchWithMapQuery1", "foo"); + actualQuery.put("testSearchWithMapQuery1", "foo"); // the result may see stale data so use Awaitility Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { HitsMetadata<?> resp = template.requestBody("direct:search", query, HitsMetadata.class); @@ -303,8 +302,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals(1, resp.hits().size(), "response hits should be == 1"); Object result = resp.hits().get(0).source(); assertInstanceOf(ObjectNode.class, result); - assertTrue(((ObjectNode) result).has("doc")); - JsonNode node = ((ObjectNode) result).get("doc"); + JsonNode node = (ObjectNode) result; assertTrue(node.has("testSearchWithMapQuery1")); assertEquals("foo", node.get("testSearchWithMapQuery1").asText()); }); @@ -318,12 +316,11 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor Map<String, Object> headers = new HashMap<>(); headers.put(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.Bulk); headers.put(ElasticsearchConstants.PARAM_INDEX_NAME, "twitter"); - template.requestBodyAndHeaders("direct:start", List.of(Map.of("doc", map1), Map.of("doc", map2)), headers, - String.class); + template.requestBodyAndHeaders("direct:start", List.of(map1, map2), headers, String.class); // No match String query = "{\n" - + " \"query\" : { \"match\" : { \"doc.testSearchWithStringQuery1\" : \"bar\" }}\n" + + " \"query\" : { \"match\" : { \"testSearchWithStringQuery1\" : \"bar\" }}\n" + "}\n"; HitsMetadata<?> response = template.requestBody("direct:search", query, HitsMetadata.class); @@ -333,7 +330,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor // Match String q = "{\n" - + " \"query\" : { \"match\" : { \"doc.testSearchWithStringQuery1\" : \"foo\" }}\n" + + " \"query\" : { \"match\" : { \"testSearchWithStringQuery1\" : \"foo\" }}\n" + "}\n"; // the result may see stale data so use Awaitility Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { @@ -344,8 +341,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals(1, resp.hits().size(), "response hits should be == 1"); Object result = resp.hits().get(0).source(); assertInstanceOf(ObjectNode.class, result); - assertTrue(((ObjectNode) result).has("doc")); - JsonNode node = ((ObjectNode) result).get("doc"); + JsonNode node = (ObjectNode) result; assertTrue(node.has("testSearchWithStringQuery1")); assertEquals("foo", node.get("testSearchWithStringQuery1").asText()); }); @@ -359,13 +355,13 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor Map<String, Object> headers = new HashMap<>(); headers.put(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.Bulk); headers.put(ElasticsearchConstants.PARAM_INDEX_NAME, "twitter"); - template.requestBodyAndHeaders("direct:start", List.of(Map.of("doc", map1), Map.of("doc", map2)), headers, + template.requestBodyAndHeaders("direct:start", List.of(map1, map2), headers, String.class); // No match SearchRequest.Builder builder = new SearchRequest.Builder() .query(new Query.Builder() - .match(new MatchQuery.Builder().field("doc.testSearchWithBuilder1").query("bar").build()).build()); + .match(new MatchQuery.Builder().field("testSearchWithBuilder1").query("bar").build()).build()); HitsMetadata<?> response = template.requestBody("direct:search", builder, HitsMetadata.class); assertNotNull(response, "response should not be null"); assertNotNull(response.total()); @@ -376,7 +372,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor Awaitility.await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { SearchRequest.Builder b = new SearchRequest.Builder() .query(new Query.Builder() - .match(new MatchQuery.Builder().field("doc.testSearchWithBuilder1").query("foo").build()).build()); + .match(new MatchQuery.Builder().field("testSearchWithBuilder1").query("foo").build()).build()); HitsMetadata<?> resp = template.requestBody("direct:search", b, HitsMetadata.class); assertNotNull(resp, "response should not be null"); @@ -385,8 +381,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals(1, resp.hits().size(), "response hits should be == 1"); Object result = resp.hits().get(0).source(); assertInstanceOf(ObjectNode.class, result); - assertTrue(((ObjectNode) result).has("doc")); - JsonNode node = ((ObjectNode) result).get("doc"); + JsonNode node = (ObjectNode) result; assertTrue(node.has("testSearchWithBuilder1")); assertEquals("foo", node.get("testSearchWithBuilder1").asText()); }); @@ -415,7 +410,7 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor // No match SearchRequest.Builder builder = new SearchRequest.Builder() - .query(new Query.Builder().match(new MatchQuery.Builder().field("doc.id").query("bar").build()).build()); + .query(new Query.Builder().match(new MatchQuery.Builder().field("id").query("bar").build()).build()); HitsMetadata<?> response = template.requestBodyAndHeader( "direct:search", builder, ElasticsearchConstants.PARAM_DOCUMENT_CLASS, Product.class, HitsMetadata.class); assertNotNull(response, "response should not be null"); @@ -534,6 +529,29 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals(createPrefix() + "value2", ((ObjectNode) response.source()).get(createPrefix() + "key2").asText()); } + @Test + void testUpdateWithMapInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + String indexId = template.requestBody("direct:index", map, String.class); + assertNotNull(indexId, "indexId should be set"); + + Map<String, String> newMap = new HashMap<>(); + newMap.put(createPrefix() + "key2", createPrefix() + "value2"); + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, indexId); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + indexId = template.requestBodyAndHeaders("direct:update", newMap, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + + //now, verify GET succeeded + GetResponse<?> response = template.requestBody("direct:get", indexId, GetResponse.class); + assertNotNull(response, "response should not be null"); + assertNotNull(response.source(), "response source should not be null"); + assertInstanceOf(ObjectNode.class, response.source(), "response source should be a ObjectNode"); + assertTrue(((ObjectNode) response.source()).has(createPrefix() + "key2")); + assertEquals(createPrefix() + "value2", ((ObjectNode) response.source()).get(createPrefix() + "key2").asText()); + } + @Test void testGetWithHeaders() { //first, Index a value @@ -627,6 +645,26 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertEquals("123", indexId, "indexId should be equals to the provided id"); } + @Test + void testUpdateWithIDInHeaderInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.Index); + headers.put(ElasticsearchConstants.PARAM_INDEX_NAME, "twitter"); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, "123"); + + String indexId = template.requestBodyAndHeaders("direct:start", map, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + assertEquals("123", indexId, "indexId should be equals to the provided id"); + + headers.put(ElasticsearchConstants.PARAM_OPERATION, ElasticsearchOperation.Update); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + + indexId = template.requestBodyAndHeaders("direct:start", map, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + assertEquals("123", indexId, "indexId should be equals to the provided id"); + } + @Test void testGetRequestBody() { String prefix = createPrefix(); @@ -703,6 +741,27 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertThat(node.get(key).asText(), equalTo("testUpdateWithString-updated")); } + @Test + void testUpdateWithStringInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + String indexId = template.requestBody("direct:index", map, String.class); + assertNotNull(indexId, "indexId should be set"); + String key = map.keySet().iterator().next(); + Object body = String.format("{\"%s\" : \"testUpdateWithString-updated\"}", key); + + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, indexId); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + indexId = template.requestBodyAndHeaders("direct:update", body, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + + GetResponse<?> response = template.requestBody("direct:get", indexId, GetResponse.class); + assertThat(response.source(), notNullValue()); + ObjectNode node = (ObjectNode) response.source(); + assertThat(node.has(key), equalTo(true)); + assertThat(node.get(key).asText(), equalTo("testUpdateWithString-updated")); + } + @Test void testUpdateWithReader() { Map<String, String> map = createIndexedData(); @@ -723,6 +782,27 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertThat(node.get(key).asText(), equalTo("testUpdateWithReader-updated")); } + @Test + void testUpdateWithReaderInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + String indexId = template.requestBody("direct:index", map, String.class); + assertNotNull(indexId, "indexId should be set"); + String key = map.keySet().iterator().next(); + Object body = new StringReader(String.format("{\"%s\" : \"testUpdateWithReader-updated\"}", key)); + + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, indexId); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + indexId = template.requestBodyAndHeaders("direct:update", body, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + + GetResponse<?> response = template.requestBody("direct:get", indexId, GetResponse.class); + assertThat(response.source(), notNullValue()); + ObjectNode node = (ObjectNode) response.source(); + assertThat(node.has(key), equalTo(true)); + assertThat(node.get(key).asText(), equalTo("testUpdateWithReader-updated")); + } + @Test void testUpdateWithBytes() { Map<String, String> map = createIndexedData(); @@ -744,6 +824,28 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertThat(node.get(key).asText(), equalTo("testUpdateWithBytes-updated")); } + @Test + void testUpdateWithBytesInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + String indexId = template.requestBody("direct:index", map, String.class); + assertNotNull(indexId, "indexId should be set"); + String key = map.keySet().iterator().next(); + Object body + = String.format("{\"%s\" : \"testUpdateWithBytes-updated\"}", key).getBytes(StandardCharsets.UTF_8); + + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, indexId); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + indexId = template.requestBodyAndHeaders("direct:update", body, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + + GetResponse<?> response = template.requestBody("direct:get", indexId, GetResponse.class); + assertThat(response.source(), notNullValue()); + ObjectNode node = (ObjectNode) response.source(); + assertThat(node.has(key), equalTo(true)); + assertThat(node.get(key).asText(), equalTo("testUpdateWithBytes-updated")); + } + @Test void testUpdateWithInputStream() { Map<String, String> map = createIndexedData(); @@ -766,6 +868,29 @@ class ElasticsearchGetSearchDeleteExistsUpdateIT extends ElasticsearchTestSuppor assertThat(node.get(key).asText(), equalTo("testUpdateWithInputStream-updated")); } + @Test + void testUpdateWithInputStreamInDocumentOnlyMode() { + Map<String, String> map = createIndexedData(); + String indexId = template.requestBody("direct:index", map, String.class); + assertNotNull(indexId, "indexId should be set"); + String key = map.keySet().iterator().next(); + Object body = new ByteArrayInputStream( + String.format("{\"%s\" : \"testUpdateWithInputStream-updated\"}", key) + .getBytes(StandardCharsets.UTF_8)); + + Map<String, Object> headers = new HashMap<>(); + headers.put(ElasticsearchConstants.PARAM_INDEX_ID, indexId); + headers.put(ElasticsearchConstants.PARAM_DOCUMENT_MODE, Boolean.TRUE); + indexId = template.requestBodyAndHeaders("direct:update", body, headers, String.class); + assertNotNull(indexId, "indexId should be set"); + + GetResponse<?> response = template.requestBody("direct:get", indexId, GetResponse.class); + assertThat(response.source(), notNullValue()); + ObjectNode node = (ObjectNode) response.source(); + assertThat(node.has(key), equalTo(true)); + assertThat(node.get(key).asText(), equalTo("testUpdateWithInputStream-updated")); + } + @Test void testUpdateWithDocumentType() { Product product = new Product(); diff --git a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ElasticsearchComponentBuilderFactory.java b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ElasticsearchComponentBuilderFactory.java index df50af5eb70..cb4ff5a6e4c 100644 --- a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ElasticsearchComponentBuilderFactory.java +++ b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ElasticsearchComponentBuilderFactory.java @@ -66,6 +66,25 @@ public interface ElasticsearchComponentBuilderFactory { doSetProperty("connectionTimeout", connectionTimeout); return this; } + /** + * Indicates whether the body of the message contains only documents. By + * default, it is set to false to be able to do the same requests as + * what the Document API supports (see + * https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html for more details). To ease the migration of routes based on the legacy component camel-elasticsearch-rest, you should consider enabling the mode especially if your routes do update operations. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: producer + * + * @param enableDocumentOnlyMode the value to set + * @return the dsl builder + */ + default ElasticsearchComponentBuilder enableDocumentOnlyMode( + boolean enableDocumentOnlyMode) { + doSetProperty("enableDocumentOnlyMode", enableDocumentOnlyMode); + return this; + } /** * Comma separated list with ip:port formatted remote transport * addresses to use. The ip and port options must be left blank for @@ -308,6 +327,7 @@ public interface ElasticsearchComponentBuilderFactory { Object value) { switch (name) { case "connectionTimeout": ((ElasticsearchComponent) component).setConnectionTimeout((int) value); return true; + case "enableDocumentOnlyMode": ((ElasticsearchComponent) component).setEnableDocumentOnlyMode((boolean) value); return true; case "hostAddresses": ((ElasticsearchComponent) component).setHostAddresses((java.lang.String) value); return true; case "lazyStartProducer": ((ElasticsearchComponent) component).setLazyStartProducer((boolean) value); return true; case "maxRetryTimeout": ((ElasticsearchComponent) component).setMaxRetryTimeout((int) value); return true; diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ElasticsearchEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ElasticsearchEndpointBuilderFactory.java index 82e50eb4a3d..636c6a61cc2 100644 --- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ElasticsearchEndpointBuilderFactory.java +++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ElasticsearchEndpointBuilderFactory.java @@ -106,6 +106,45 @@ public interface ElasticsearchEndpointBuilderFactory { doSetProperty("disconnect", disconnect); return this; } + /** + * Indicates whether the body of the message contains only documents. By + * default, it is set to false to be able to do the same requests as + * what the Document API supports (see + * https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html for more details). To ease the migration of routes based on the legacy component camel-elasticsearch-rest, you should consider enabling the mode especially if your routes do update operations. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: producer + * + * @param enableDocumentOnlyMode the value to set + * @return the dsl builder + */ + default ElasticsearchEndpointBuilder enableDocumentOnlyMode( + boolean enableDocumentOnlyMode) { + doSetProperty("enableDocumentOnlyMode", enableDocumentOnlyMode); + return this; + } + /** + * Indicates whether the body of the message contains only documents. By + * default, it is set to false to be able to do the same requests as + * what the Document API supports (see + * https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html for more details). To ease the migration of routes based on the legacy component camel-elasticsearch-rest, you should consider enabling the mode especially if your routes do update operations. + * + * The option will be converted to a <code>boolean</code> + * type. + * + * Default: false + * Group: producer + * + * @param enableDocumentOnlyMode the value to set + * @return the dsl builder + */ + default ElasticsearchEndpointBuilder enableDocumentOnlyMode( + String enableDocumentOnlyMode) { + doSetProperty("enableDocumentOnlyMode", enableDocumentOnlyMode); + return this; + } /** * Starting index of the response. * @@ -821,6 +860,20 @@ public interface ElasticsearchEndpointBuilderFactory { public String from() { return "from"; } + + /** + * Indicates whether the body of the message contains only documents. + * + * The option is a: {@code Boolean} type. + * + * Default: false + * Group: producer + * + * @return the name of the header {@code enableDocumentOnlyMode}. + */ + public String enableDocumentOnlyMode() { + return "enableDocumentOnlyMode"; + } } static ElasticsearchEndpointBuilder endpointBuilder( String componentName,