This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new 0abebdc27 TIKA-3855 -- enable upsert for OpenSearchEmitter
     new 6e1e6b724 Merge remote-tracking branch 'origin/main'
0abebdc27 is described below

commit 0abebdc27dbfc9eb34abc3619e13ef816af9e331
Author: tallison <[email protected]>
AuthorDate: Thu Sep 15 12:22:10 2022 -0400

    TIKA-3855 -- enable upsert for OpenSearchEmitter
---
 CHANGES.txt                                        |  13 +-
 .../opensearch/tests/TikaPipesOpenSearchTest.java  |   3 +-
 .../pipes/xsearch/tests/TikaPipesXSearchBase.java  | 122 +++++++++--
 .../pipes/xsearch/tests/XSearchTestClient.java     |  11 +-
 .../opensearch/tika-config-opensearch.xml          |   2 -
 .../pipes/emitter/opensearch/OpenSearchClient.java | 240 +++++++++++++++------
 .../emitter/opensearch/OpenSearchEmitter.java      |  27 ++-
 .../emitter/opensearch/OpenSearchClientTest.java   |   4 +-
 8 files changed, 318 insertions(+), 104 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index cda84e564..9583c56cd 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,4 @@
-Release 2.4.2 - ???
+Release 2.5.0 - ???
 
    * Improved extraction of PDF subset info for PDF/UA, PDF/VT, and PDF/X.
      NOTE: we no longer append PDF/A information, e.g. 'version="A-1b"'
@@ -6,6 +6,17 @@ Release 2.4.2 - ???
      'pdfa:PDFVersion' key or from 'pdfaid:conformance'
      and 'pdfaid:part' (TIKA-3844).
 
+   * Avoid infinite loop in bookmark extraction from PDFs (TIKA-3832).
+
+   * Added upsert option for the OpenSearch emitter (TIKA-3855).
+
+   * Extract PDF signature information at the document level
+     into the metadata (TIKA-3852).
+
+   * Enable configuration of digests via AutoDetectParserConfig (TIKA-3853).
+
+   * Use commons-io byte array streams via PJ Fanning (TIKA-3843).
+
    * Add extraction of ODF version from ODF files (TIKA-3840).
 
    * tika-parser-html-commons (BoilerPipeHandler) is no longer a
diff --git 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/opensearch/tests/TikaPipesOpenSearchTest.java
 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/opensearch/tests/TikaPipesOpenSearchTest.java
index 3bb2d5c4a..cb2646ba8 100644
--- 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/opensearch/tests/TikaPipesOpenSearchTest.java
+++ 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/opensearch/tests/TikaPipesOpenSearchTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tika.pipes.opensearch.tests;
 
+
 import java.time.Duration;
 import java.time.temporal.ChronoUnit;
 
@@ -28,7 +29,7 @@ import 
org.apache.tika.pipes.xsearch.tests.TikaPipesXSearchBase;
 
 public class TikaPipesOpenSearchTest extends TikaPipesXSearchBase {
 
-    private static final String DOCKER_IMAGE_NAME = 
"opensearchproject/opensearch:2.1.0";
+    private static final String DOCKER_IMAGE_NAME = 
"opensearchproject/opensearch:2.3.0";
 
     @ClassRule
     public static GenericContainer<?> OPEN_SEARCH_CONTAINER =
diff --git 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/TikaPipesXSearchBase.java
 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/TikaPipesXSearchBase.java
index 92cddbe6c..1f8378157 100644
--- 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/TikaPipesXSearchBase.java
+++ 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/TikaPipesXSearchBase.java
@@ -21,11 +21,13 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Collections;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -33,31 +35,47 @@ import com.fasterxml.jackson.databind.JsonNode;
 import org.apache.commons.io.IOUtils;
 import org.jetbrains.annotations.NotNull;
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 import org.testcontainers.containers.GenericContainer;
 import org.testcontainers.shaded.org.apache.commons.io.FileUtils;
 
 import org.apache.tika.cli.TikaCLI;
 import org.apache.tika.client.HttpClientFactory;
+import org.apache.tika.metadata.Metadata;
 import org.apache.tika.pipes.HandlerConfig;
+import org.apache.tika.pipes.emitter.Emitter;
+import org.apache.tika.pipes.emitter.EmitterManager;
 import org.apache.tika.pipes.emitter.opensearch.JsonResponse;
 import org.apache.tika.pipes.emitter.opensearch.OpenSearchEmitter;
 
 public abstract class TikaPipesXSearchBase {
 
-    private static final String TEST_INDEX = "tika-pipes-index";
-    private static final File TEST_FILE_FOLDER = new File("target", 
"test-files");
+    //@TempDir -- we can't use this yet because of junit4+junit5
+    private Path pipesDirectory;
+
+    private Path testDocDirectory;
+
+    protected static final String TEST_INDEX = "tika-pipes-index";
     private int numTestDocs = 0;
     private static String OPEN_SEARCH_HOST;
     private static int OPEN_SEARCH_PORT;
     //this includes only the base, not the collection, e.g. 
https://localhost:49213
-    private static String OPEN_SEARCH_ENDPOINT_BASE;
+    protected static String OPEN_SEARCH_ENDPOINT_BASE;
     private static XSearchTestClient client;
 
 
+    @Before
+    public void setUp() throws IOException {
+        pipesDirectory = 
Files.createTempDirectory("tika-opensearch-integration-");
+        testDocDirectory = pipesDirectory.resolve("docs");
+    }
     @After
     public void tearDown() throws Exception {
-        FileUtils.deleteDirectory(TEST_FILE_FOLDER);
+        //we shouldn't have to do this because of @TempDir
+        //there are some timing/order issues because of the joint junit 4 and 5
+        FileUtils.deleteDirectory(pipesDirectory.toFile());
         JsonResponse response = client.deleteIndex(OPEN_SEARCH_ENDPOINT_BASE + 
TEST_INDEX);
         assertEquals(200, response.getStatus());
         assertTrue(response.getJson().get("acknowledged").asBoolean());
@@ -72,6 +90,7 @@ public abstract class TikaPipesXSearchBase {
         sendMappings(endpoint, TEST_INDEX, "opensearch-mappings.json");
 
         runPipes(OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS,
+                OpenSearchEmitter.UpdateStrategy.OVERWRITE,
                 HandlerConfig.PARSE_MODE.CONCATENATE, endpoint);
 
         String query = "{ \"track_total_hits\": true, \"query\": { \"match\": 
{ \"content\": { " +
@@ -98,6 +117,7 @@ public abstract class TikaPipesXSearchBase {
         sendMappings(endpoint, TEST_INDEX, 
"opensearch-parent-child-mappings.json");
 
         runPipes(OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD,
+                OpenSearchEmitter.UpdateStrategy.OVERWRITE,
                 HandlerConfig.PARSE_MODE.RMETA, endpoint);
 
         String query = "{ \"track_total_hits\": true, \"query\": { \"match\": 
{ \"content\": { " +
@@ -161,6 +181,7 @@ public abstract class TikaPipesXSearchBase {
         sendMappings(endpoint, TEST_INDEX, "opensearch-mappings.json");
 
         runPipes(OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS,
+                OpenSearchEmitter.UpdateStrategy.OVERWRITE,
                 HandlerConfig.PARSE_MODE.RMETA, endpoint);
 
         String query = "{ \"track_total_hits\": true, \"query\": { \"match\": 
{ \"content\": { " +
@@ -214,8 +235,42 @@ public abstract class TikaPipesXSearchBase {
         assertEquals(400, results.getStatus());
     }
 
+    @Test
+    public void testUpsert() throws Exception {
+        String endpoint = OPEN_SEARCH_ENDPOINT_BASE + TEST_INDEX;
+        sendMappings(endpoint, TEST_INDEX, "opensearch-mappings.json");
+        Path tikaConfigFile =
+                
getTikaConfigFile(OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS,
+                        OpenSearchEmitter.UpdateStrategy.UPSERT, 
HandlerConfig.PARSE_MODE.RMETA,
+                        endpoint);
+        Emitter emitter = EmitterManager.load(tikaConfigFile).getEmitter();
+        Metadata metadata = new Metadata();
+        metadata.set("mime", "mimeA");
+        metadata.set("title", "titleA");
+        emitter.emit("1", Collections.singletonList(metadata));
+        JsonResponse refresh = client.getJson(endpoint + "/_refresh");
+        metadata.set("title", "titleB");
+        emitter.emit("1", Collections.singletonList(metadata));
+        refresh = client.getJson(endpoint + "/_refresh");
+
+        Metadata metadata2 = new Metadata();
+        metadata2.set("content", "the quick brown fox");
+        emitter.emit("1", Collections.singletonList(metadata2));
+        refresh = client.getJson(endpoint + "/_refresh");
 
-    private void sendMappings(String endpoint, String index, String 
mappingsFile) throws Exception {
+        String query = "{ " +
+                //"\"from\":0, \"size\":1000," +
+                "\"track_total_hits\": true, \"query\": { " +
+                "\"match_all\": {} } }";
+        JsonResponse response = client.postJson(endpoint + "/_search", query);
+        JsonNode doc1 = response.getJson().get("hits").get("hits").get(0).get(
+                "_source");
+        Assertions.assertEquals("mimeA", doc1.get("mime").asText());
+        Assertions.assertEquals("titleB", doc1.get("title").asText());
+        Assertions.assertEquals("the quick brown fox", 
doc1.get("content").asText());
+    }
+
+    protected void sendMappings(String endpoint, String index, String 
mappingsFile) throws Exception {
         //create the collection with mappings
         String mappings = 
IOUtils.toString(TikaPipesXSearchBase.class.getResourceAsStream(
                 "/opensearch/" + mappingsFile), StandardCharsets.UTF_8);
@@ -240,14 +295,30 @@ public abstract class TikaPipesXSearchBase {
     }
 
     private void runPipes(OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                          OpenSearchEmitter.UpdateStrategy updateStrategy,
                           HandlerConfig.PARSE_MODE parseMode, String endpoint) 
throws Exception {
 
-        File tikaConfigFile = new File("target", "ta-opensearch.xml");
-        File log4jPropFile = new File("target", "tmp-log4j2.xml");
+        Path tikaConfigFile = getTikaConfigFile(attachmentStrategy, 
updateStrategy, parseMode,
+                endpoint);
+
+        TikaCLI.main(new String[]{"-a", "--config=" + 
tikaConfigFile.toAbsolutePath().toString()});
+
+        //refresh to make sure the content is searchable
+        JsonResponse refresh = client.getJson(endpoint + "/_refresh");
+
+    }
+
+    private Path getTikaConfigFile(OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                                   OpenSearchEmitter.UpdateStrategy 
updateStrategy,
+                                   HandlerConfig.PARSE_MODE parseMode, String 
endpoint) throws
+            IOException {
+        Path tikaConfigFile = pipesDirectory.resolve("ta-opensearch.xml");
+        Path log4jPropFile = pipesDirectory.resolve("tmp-log4j2.xml");
         try (InputStream is = TikaPipesXSearchBase.class
                 .getResourceAsStream("/pipes-fork-server-custom-log4j2.xml")) {
-            FileUtils.copyInputStreamToFile(is, log4jPropFile);
+            Files.copy(is, log4jPropFile);
         }
+
         String tikaConfigTemplateXml;
         try (InputStream is = TikaPipesXSearchBase.class
                 
.getResourceAsStream("/opensearch/tika-config-opensearch.xml")) {
@@ -256,26 +327,25 @@ public abstract class TikaPipesXSearchBase {
 
         String tikaConfigXml =
                 createTikaConfigXml(tikaConfigFile, log4jPropFile, 
tikaConfigTemplateXml,
-                        attachmentStrategy, parseMode, endpoint);
-        FileUtils.writeStringToFile(tikaConfigFile, tikaConfigXml, 
StandardCharsets.UTF_8);
-
-        TikaCLI.main(new String[]{"-a", "--config=" + 
tikaConfigFile.getAbsolutePath()});
-
-        //refresh to make sure the content is searchable
-        JsonResponse refresh = client.getJson(endpoint + "/_refresh");
+                        attachmentStrategy, updateStrategy, parseMode, 
endpoint);
+        writeStringToPath(tikaConfigFile, tikaConfigXml);
 
+        return tikaConfigFile;
     }
 
     @NotNull
-    private String createTikaConfigXml(File tikaConfigFile, File 
log4jPropFile, String tikaConfigTemplateXml,
+    private String createTikaConfigXml(Path tikaConfigFile, Path log4jPropFile,
+                                       String tikaConfigTemplateXml,
                                        OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                                       OpenSearchEmitter.UpdateStrategy 
updateStrategy,
                                        HandlerConfig.PARSE_MODE parseMode, 
String endpoint) {
         String res =
-                tikaConfigTemplateXml.replace("{TIKA_CONFIG}", 
tikaConfigFile.getAbsolutePath())
+                tikaConfigTemplateXml.replace("{TIKA_CONFIG}", 
tikaConfigFile.toAbsolutePath().toString())
                         .replace("{ATTACHMENT_STRATEGY}", 
attachmentStrategy.toString())
-                        .replace("{LOG4J_PROPERTIES_FILE}", 
log4jPropFile.getAbsolutePath())
+                        .replace("{LOG4J_PROPERTIES_FILE}", 
log4jPropFile.toAbsolutePath().toString())
+                        .replace("{UPDATE_STRATEGY}", 
updateStrategy.toString())
                         .replaceAll("\\{PATH_TO_DOCS\\}", 
-                                
Matcher.quoteReplacement(TEST_FILE_FOLDER.getAbsolutePath()))
+                                
Matcher.quoteReplacement(testDocDirectory.toAbsolutePath().toString()))
                         .replace("{PARSE_MODE}", parseMode.name());
 
         res = res.replace("{OPENSEARCH_CONNECTION}", endpoint);
@@ -295,23 +365,29 @@ public abstract class TikaPipesXSearchBase {
         client = new XSearchTestClient(OPEN_SEARCH_ENDPOINT_BASE,
                 httpClientFactory.build(),
                 OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS,
+                OpenSearchEmitter.UpdateStrategy.OVERWRITE,
                 OpenSearchEmitter.DEFAULT_EMBEDDED_FILE_FIELD_NAME);
     }
 
     private void createTestHtmlFiles(String bodyContent, int numHtmlDocs) 
throws Exception {
-        TEST_FILE_FOLDER.mkdirs();
+        Files.createDirectories(testDocDirectory);
         for (int i = 0; i < numHtmlDocs; ++i) {
-            FileUtils.writeStringToFile(new File(TEST_FILE_FOLDER, "test-" + i 
+ ".html"),
-                    "<html><body>" + bodyContent +  "</body></html>", 
StandardCharsets.UTF_8);
+            String html = "<html><body>" + bodyContent +  "</body></html>";
+            Path p = testDocDirectory.resolve( "test-" + i + ".html");
+            writeStringToPath(p, html);
         }
         File testDocuments =
                 
Paths.get(TikaPipesXSearchBase.class.getResource("/test-documents").toURI()).toFile();
         for (File f : testDocuments.listFiles()) {
-            Path targ = TEST_FILE_FOLDER.toPath().resolve(f.getName());
+            Path targ = testDocDirectory.resolve(f.getName());
             Files.copy(f.toPath(), targ);
             numTestDocs++;
         }
     }
 
+    private static void writeStringToPath(Path path, String string) throws 
IOException {
+        Files.write(path, string.getBytes(StandardCharsets.UTF_8));
+    }
+
 
 }
diff --git 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/XSearchTestClient.java
 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/XSearchTestClient.java
index ec8191f3c..8f2c580f1 100644
--- 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/XSearchTestClient.java
+++ 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/java/org/apache/tika/pipes/xsearch/tests/XSearchTestClient.java
@@ -43,10 +43,11 @@ import 
org.apache.tika.pipes.emitter.opensearch.OpenSearchEmitter;
  */
 public class XSearchTestClient extends OpenSearchClient {
 
-    protected XSearchTestClient(String openSearchUrl, HttpClient httpClient,
-                                OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
-                                String embeddedFileFieldName) {
-        super(openSearchUrl, httpClient, attachmentStrategy, 
embeddedFileFieldName);
+    public XSearchTestClient(String openSearchUrl, HttpClient httpClient,
+                             OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                             OpenSearchEmitter.UpdateStrategy updateStrategy,
+                             String embeddedFileFieldName) {
+        super(openSearchUrl, httpClient, attachmentStrategy, updateStrategy, 
embeddedFileFieldName);
     }
 
     protected JsonResponse putJson(String url, String json) throws IOException 
{
@@ -86,7 +87,7 @@ public class XSearchTestClient extends OpenSearchClient {
         }
     }
 
-    protected JsonResponse getJson(String url) throws IOException {
+    public JsonResponse getJson(String url) throws IOException {
         HttpGet httpRequest = new HttpGet(url);
         httpRequest.setHeader("Accept", "application/json");
         httpRequest.setHeader("Content-type", "application/json; 
charset=utf-8");
diff --git 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/resources/opensearch/tika-config-opensearch.xml
 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/resources/opensearch/tika-config-opensearch.xml
index df9452a2f..ee727b1ad 100644
--- 
a/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/resources/opensearch/tika-config-opensearch.xml
+++ 
b/tika-integration-tests/tika-pipes-opensearch-integration-tests/src/test/resources/opensearch/tika-config-opensearch.xml
@@ -93,9 +93,7 @@
       <params>
         <name>ose</name>
         <openSearchUrl>{OPENSEARCH_CONNECTION}</openSearchUrl>
-<!--    TODO: implement this
         <updateStrategy>{UPDATE_STRATEGY}</updateStrategy>
-        -->
         <attachmentStrategy>{ATTACHMENT_STRATEGY}</attachmentStrategy>
         <commitWithin>10</commitWithin>
         <idField>_id</idField>
diff --git 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClient.java
 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClient.java
index 3eff65997..3b8061a95 100644
--- 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClient.java
+++ 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClient.java
@@ -52,13 +52,18 @@ public class OpenSearchClient {
     protected final String openSearchUrl;
     protected final HttpClient httpClient;
     private final OpenSearchEmitter.AttachmentStrategy attachmentStrategy;
+
+    private final MetadataToJsonWriter metadataToJsonWriter;
     private final String embeddedFileFieldName;
     protected OpenSearchClient(String openSearchUrl, HttpClient httpClient,
                                OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                               OpenSearchEmitter.UpdateStrategy updateStrategy,
                                String embeddedFileFieldName) {
         this.openSearchUrl = openSearchUrl;
         this.httpClient = httpClient;
         this.attachmentStrategy = attachmentStrategy;
+        this.metadataToJsonWriter = (updateStrategy == 
OpenSearchEmitter.UpdateStrategy.OVERWRITE) ?
+                new InsertMetadataToJsonWriter() : new 
UpsertMetadataToJsonWriter();
         this.embeddedFileFieldName = embeddedFileFieldName;
     }
 
@@ -73,7 +78,6 @@ public class OpenSearchClient {
 
     private void emitJson(StringBuilder json) throws IOException, 
TikaClientException {
         String requestUrl = openSearchUrl + "/_bulk";
-
         JsonResponse response = postJson(requestUrl, json.toString());
         if (response.getStatus() != 200) {
             throw new TikaClientException(response.getMsg());
@@ -107,12 +111,12 @@ public class OpenSearchClient {
             if (i > 0) {
                 id.append("-").append(UUID.randomUUID());
             }
-            String indexJson = getBulkIndexJson(id.toString(), routing);
+            String indexJson = metadataToJsonWriter.getBulkJson(id.toString(), 
routing);
             json.append(indexJson).append("\n");
             if (i == 0) {
-                json.append(metadataToJsonContainer(metadata, 
attachmentStrategy));
+                json.append(metadataToJsonWriter.writeContainer(metadata, 
attachmentStrategy));
             } else {
-                json.append(metadataToJsonEmbedded(metadata, 
attachmentStrategy, emitKey,
+                json.append(metadataToJsonWriter.writeEmbedded(metadata, 
attachmentStrategy, emitKey,
                         embeddedFileFieldName));
             }
             json.append("\n");
@@ -120,75 +124,20 @@ public class OpenSearchClient {
         }
     }
 
-    protected static String metadataToJsonEmbedded(Metadata metadata,
-                                                   
OpenSearchEmitter.AttachmentStrategy attachmentStrategy,
-                                                   String emitKey, String 
embeddedFileFieldName) throws IOException {
-        StringWriter writer = new StringWriter();
-        try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
-            jsonGenerator.writeStartObject();
-
-            writeMetadata(metadata, jsonGenerator);
-            if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
-                jsonGenerator.writeObjectFieldStart("relation_type");
-                jsonGenerator.writeStringField("name", embeddedFileFieldName);
-                jsonGenerator.writeStringField("parent", emitKey);
-                //end the relation type object
-                jsonGenerator.writeEndObject();
-            } else if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS) {
-                jsonGenerator.writeStringField("parent", emitKey);
-            }
-            //end the metadata object
-            jsonGenerator.writeEndObject();
-        }
-        return writer.toString();
-    }
-
-    protected static String metadataToJsonContainer(Metadata metadata,
+    //Only here for testing. These may disappear without notice in the future.
+    protected static String metadataToJsonContainerInsert(Metadata metadata,
                                                     
OpenSearchEmitter.AttachmentStrategy attachmentStrategy)
             throws IOException {
-        StringWriter writer = new StringWriter();
-        try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
-            jsonGenerator.writeStartObject();
-            writeMetadata(metadata, jsonGenerator);
-            if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
-                jsonGenerator.writeStringField("relation_type", "container");
-            }
-            jsonGenerator.writeEndObject();
-        }
-        return writer.toString();
+        return new InsertMetadataToJsonWriter().writeContainer(metadata, 
attachmentStrategy);
     }
 
-    private static void writeMetadata(Metadata metadata, JsonGenerator 
jsonGenerator) throws IOException {
-        //writes the metadata without the start { or the end }
-        //to allow for other fields to be added
-        for (String n : metadata.names()) {
-            String[] vals = metadata.getValues(n);
-            if (vals.length == 1) {
-                jsonGenerator.writeStringField(n, vals[0]);
-            } else {
-                jsonGenerator.writeArrayFieldStart(n);
-                for (String v : vals) {
-                    jsonGenerator.writeString(v);
-                }
-                jsonGenerator.writeEndArray();
-            }
-        }
-    }
-
-    private String getBulkIndexJson(String id, String routing) throws 
IOException {
-        StringWriter writer = new StringWriter();
-        try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
-            jsonGenerator.writeStartObject();
-            jsonGenerator.writeObjectFieldStart("index");
-            jsonGenerator.writeStringField("_id", id);
-            if (!StringUtils.isEmpty(routing)) {
-                jsonGenerator.writeStringField("routing", routing);
-            }
-
-            jsonGenerator.writeEndObject();
-            jsonGenerator.writeEndObject();
-        }
-        return writer.toString();
+    //Only here for testing. These may disappear without notice in the future.
+    protected static String metadataToJsonEmbeddedInsert(Metadata metadata,
+                                                         
OpenSearchEmitter.AttachmentStrategy attachmentStrategy,
+                                                         String emitKey, 
String embeddedFileFieldName)
+            throws IOException {
+        return new InsertMetadataToJsonWriter().writeEmbedded(metadata,
+                attachmentStrategy, emitKey, embeddedFileFieldName);
     }
 
     public JsonResponse postJson(String url, String json) throws IOException {
@@ -230,4 +179,157 @@ public class OpenSearchClient {
             httpRequest.releaseConnection();
         }
     }
+
+    private interface MetadataToJsonWriter {
+        String writeContainer(Metadata metadata, 
OpenSearchEmitter.AttachmentStrategy attachmentStrategy)
+                throws IOException;
+
+        String writeEmbedded(Metadata metadata, 
OpenSearchEmitter.AttachmentStrategy attachmentStrategy,
+                             String emitKey, String embeddedFileFieldName) 
throws IOException;
+
+        String getBulkJson(String id, String routing) throws IOException;
+    }
+
+    private static class InsertMetadataToJsonWriter implements 
MetadataToJsonWriter {
+
+        @Override
+        public String writeContainer(Metadata metadata,
+                                     OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy)
+                throws IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+                writeMetadata(metadata, jsonGenerator);
+                if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
+                    jsonGenerator.writeStringField("relation_type", 
"container");
+                }
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+
+        @Override
+        public String writeEmbedded(Metadata metadata,
+                                    OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                                    String emitKey, String 
embeddedFileFieldName)
+                throws IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+
+                writeMetadata(metadata, jsonGenerator);
+                if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
+                    jsonGenerator.writeObjectFieldStart("relation_type");
+                    jsonGenerator.writeStringField("name", 
embeddedFileFieldName);
+                    jsonGenerator.writeStringField("parent", emitKey);
+                    //end the relation type object
+                    jsonGenerator.writeEndObject();
+                } else if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS) {
+                    jsonGenerator.writeStringField("parent", emitKey);
+                }
+                //end the metadata object
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+
+        @Override
+        public String getBulkJson(String id, String routing) throws 
IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+                jsonGenerator.writeObjectFieldStart("index");
+                jsonGenerator.writeStringField("_id", id);
+                if (!StringUtils.isEmpty(routing)) {
+                    jsonGenerator.writeStringField("routing", routing);
+                }
+                jsonGenerator.writeEndObject();
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+    }
+
+    private static class UpsertMetadataToJsonWriter implements 
MetadataToJsonWriter {
+
+        @Override
+        public String writeContainer(Metadata metadata,
+                                     OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy)
+                throws IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+                jsonGenerator.writeObjectFieldStart("doc");
+                writeMetadata(metadata, jsonGenerator);
+                if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
+                    jsonGenerator.writeStringField("relation_type", 
"container");
+                }
+                jsonGenerator.writeEndObject();
+                jsonGenerator.writeBooleanField("doc_as_upsert", true);
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+
+        @Override
+        public String writeEmbedded(Metadata metadata,
+                                    OpenSearchEmitter.AttachmentStrategy 
attachmentStrategy,
+                                    String emitKey, String 
embeddedFileFieldName)
+                throws IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+                jsonGenerator.writeObjectFieldStart("doc");
+                writeMetadata(metadata, jsonGenerator);
+                if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.PARENT_CHILD) {
+                    jsonGenerator.writeObjectFieldStart("relation_type");
+                    jsonGenerator.writeStringField("name", 
embeddedFileFieldName);
+                    jsonGenerator.writeStringField("parent", emitKey);
+                    //end the relation type object
+                    jsonGenerator.writeEndObject();
+                } else if (attachmentStrategy == 
OpenSearchEmitter.AttachmentStrategy.SEPARATE_DOCUMENTS) {
+                    jsonGenerator.writeStringField("parent", emitKey);
+                }
+                //end the "doc"
+                jsonGenerator.writeEndObject();
+                //end the metadata object
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+
+        @Override
+        public String getBulkJson(String id, String routing) throws 
IOException {
+            StringWriter writer = new StringWriter();
+            try (JsonGenerator jsonGenerator = new 
JsonFactory().createGenerator(writer)) {
+                jsonGenerator.writeStartObject();
+                jsonGenerator.writeObjectFieldStart("update");
+                jsonGenerator.writeStringField("_id", id);
+                if (!StringUtils.isEmpty(routing)) {
+                    jsonGenerator.writeStringField("routing", routing);
+                }
+                jsonGenerator.writeNumberField("retry_on_conflict", 3);
+                jsonGenerator.writeEndObject();
+                jsonGenerator.writeEndObject();
+            }
+            return writer.toString();
+        }
+    }
+
+    private static void writeMetadata(Metadata metadata, JsonGenerator 
jsonGenerator) throws IOException {
+        //writes the metadata without the start { or the end }
+        //to allow for other fields to be added
+        for (String n : metadata.names()) {
+            String[] vals = metadata.getValues(n);
+            if (vals.length == 1) {
+                jsonGenerator.writeStringField(n, vals[0]);
+            } else {
+                jsonGenerator.writeArrayFieldStart(n);
+                for (String v : vals) {
+                    jsonGenerator.writeString(v);
+                }
+                jsonGenerator.writeEndArray();
+            }
+        }
+    }
 }
diff --git 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchEmitter.java
 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchEmitter.java
index 2e94221f1..bc010cf46 100644
--- 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchEmitter.java
+++ 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/main/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchEmitter.java
@@ -20,6 +20,7 @@ import static 
org.apache.tika.config.TikaConfig.mustNotBeEmpty;
 
 import java.io.IOException;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import org.slf4j.Logger;
@@ -47,10 +48,16 @@ public class OpenSearchEmitter extends AbstractEmitter 
implements Initializable
         //anything else?
     }
 
+    public enum UpdateStrategy {
+        OVERWRITE, UPSERT
+        //others?
+    }
+
     public static String DEFAULT_EMBEDDED_FILE_FIELD_NAME = "embedded";
     private static final Logger LOG = 
LoggerFactory.getLogger(OpenSearchEmitter.class);
     private AttachmentStrategy attachmentStrategy = 
AttachmentStrategy.PARENT_CHILD;
 
+    private UpdateStrategy updateStrategy = UpdateStrategy.OVERWRITE;
     private String openSearchUrl = null;
     private String idField = "_id";
     private int commitWithin = 1000;
@@ -179,6 +186,24 @@ public class OpenSearchEmitter extends AbstractEmitter 
implements Initializable
         httpClientFactory.setProxyPort(proxyPort);
     }
 
+    public void setUpdateStrategy(UpdateStrategy updateStrategy) {
+        this.updateStrategy = updateStrategy;
+    }
+
+    public void setUpdateStrategy(String strategy) throws TikaConfigException {
+        switch (strategy.toLowerCase(Locale.US)) {
+            case "overwrite" :
+                setUpdateStrategy(UpdateStrategy.OVERWRITE);
+                break;
+            case "upsert" :
+                setUpdateStrategy(UpdateStrategy.UPSERT);
+                break;
+            default :
+                throw new TikaConfigException("'overwrite' and 'upsert' are 
the two options so " +
+                        "far. I regret I don't understand: " + strategy);
+        }
+    }
+
     /**
      * If using the {@link AttachmentStrategy#PARENT_CHILD}, this is the field 
name
      * used to store the child documents.  Note that we artificially flatten 
all embedded
@@ -200,7 +225,7 @@ public class OpenSearchEmitter extends AbstractEmitter 
implements Initializable
         } else {
             openSearchClient =
                     new OpenSearchClient(openSearchUrl,
-                            httpClientFactory.build(), attachmentStrategy,
+                            httpClientFactory.build(), attachmentStrategy, 
updateStrategy,
                             embeddedFileFieldName);
         }
     }
diff --git 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/test/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClientTest.java
 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/test/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClientTest.java
index 638533c33..994ff0044 100644
--- 
a/tika-pipes/tika-emitters/tika-emitter-opensearch/src/test/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClientTest.java
+++ 
b/tika-pipes/tika-emitters/tika-emitter-opensearch/src/test/java/org/apache/tika/pipes/emitter/opensearch/OpenSearchClientTest.java
@@ -31,7 +31,7 @@ public class OpenSearchClientTest extends TikaTest {
         metadata.add("title", "title1");
         for (OpenSearchEmitter.AttachmentStrategy strategy :
                 OpenSearchEmitter.AttachmentStrategy.values()) {
-            String json = OpenSearchClient.metadataToJsonContainer(metadata,
+            String json = 
OpenSearchClient.metadataToJsonContainerInsert(metadata,
                     strategy);
             assertContains("author1", json);
             assertContains("author2", json);
@@ -40,7 +40,7 @@ public class OpenSearchClientTest extends TikaTest {
         }
         for (OpenSearchEmitter.AttachmentStrategy strategy :
                 OpenSearchEmitter.AttachmentStrategy.values()) {
-            String json = OpenSearchClient.metadataToJsonEmbedded(metadata, 
strategy,
+            String json = 
OpenSearchClient.metadataToJsonEmbeddedInsert(metadata, strategy,
                     "myEmitKey", 
OpenSearchEmitter.DEFAULT_EMBEDDED_FILE_FIELD_NAME);
             assertContains("author1", json);
             assertContains("author2", json);

Reply via email to