Browse Source

Add target_field parameter to gsub, join, lowercase, sort, split, trim, uppercase (#24133)

Closes #23682 #23228
Alexander Kazakov 8 years ago
parent
commit
a7dafdaa05
26 changed files with 475 additions and 292 deletions
  1. 18 10
      docs/reference/ingest/ingest-node.asciidoc
  2. 13 4
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java
  3. 14 28
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java
  4. 10 3
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JoinProcessor.java
  5. 6 4
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java
  6. 10 3
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/SortProcessor.java
  7. 11 4
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/SplitProcessor.java
  8. 7 4
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java
  9. 6 5
      modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java
  10. 103 0
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java
  11. 18 8
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java
  12. 2 2
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ForEachProcessorTests.java
  13. 14 23
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java
  14. 10 50
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java
  15. 16 0
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java
  16. 26 5
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorTests.java
  17. 4 43
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorFactoryTests.java
  18. 2 2
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java
  19. 108 0
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorFactoryTests.java
  20. 34 12
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorTests.java
  21. 16 0
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SplitProcessorFactoryTests.java
  22. 15 6
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SplitProcessorTests.java
  23. 4 43
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorFactoryTests.java
  24. 2 2
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java
  25. 4 29
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorFactoryTests.java
  26. 2 2
      modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java

+ 18 - 10
docs/reference/ingest/ingest-node.asciidoc

@@ -1489,10 +1489,12 @@ If the field is not a string, the processor will throw an exception.
 .Gsub Options
 [options="header"]
 |======
-| Name          | Required  | Default  | Description
-| `field`       | yes       | -        | The field to apply the replacement to
-| `pattern`     | yes       | -        | The pattern to be replaced
-| `replacement` | yes       | -        | The string to replace the matching patterns with
+| Name              | Required  | Default  | Description
+| `field`           | yes       | -        | The field to apply the replacement to
+| `pattern`         | yes       | -        | The pattern to be replaced
+| `replacement`     | yes       | -        | The string to replace the matching patterns with
+| `target_field`    | no        | `field`  | The field to assign the converted value to, by default `field` is updated in-place
+| `ignore_missing`  | no        | `false`  | If `true` and `field` does not exist or is `null`, the processor quietly exits without modifying the document
 |======
 
 [source,js]
@@ -1516,9 +1518,10 @@ Throws an error when the field is not an array.
 .Join Options
 [options="header"]
 |======
-| Name          | Required  | Default  | Description
-| `field`       | yes       | -        | The field to be separated
-| `separator`   | yes       | -        | The separator character
+| Name              | Required  | Default  | Description
+| `field`           | yes       | -        | The field to be separated
+| `separator`       | yes       | -        | The separator character
+| `target_field`    | no        | `field`  | The field to assign the joined value to, by default `field` is updated in-place
 |======
 
 [source,js]
@@ -1662,6 +1665,7 @@ Converts a string to its lowercase equivalent.
 |======
 | Name             | Required  | Default  | Description
 | `field`          | yes       | -        | The field to make lowercase
+| `target_field`   | no        | `field`  | The field to assign the converted value to, by default `field` is updated in-place
 | `ignore_missing` | no        | `false`  | If `true` and `field` does not exist or is `null`, the processor quietly exits without modifying the document
 |======
 
@@ -1874,6 +1878,7 @@ Splits a field into an array using a separator character. Only works on string f
 | Name              | Required  | Default  | Description
 | `field`           | yes       | -        | The field to split
 | `separator`       | yes       | -        | A regex which matches the separator, eg `,` or `\s+`
+| `target_field`    | no        | `field`  | The field to assign the split value to, by default `field` is updated in-place
 | `ignore_missing`  | no        | `false`  | If `true` and `field` does not exist, the processor quietly exits without modifying the document
 |======
 
@@ -1899,9 +1904,10 @@ Throws an error when the field is not an array.
 .Sort Options
 [options="header"]
 |======
-| Name    | Required  | Default  | Description
-| `field` | yes       | -        | The field to be sorted
-| `order` | no        | `"asc"`  | The sort order to use. Accepts `"asc"` or `"desc"`.
+| Name              | Required  | Default  | Description
+| `field`           | yes       | -        | The field to be sorted
+| `order`           | no        | `"asc"`  | The sort order to use. Accepts `"asc"` or `"desc"`.
+| `target_field`    | no        | `field`  | The field to assign the sorted value to, by default `field` is updated in-place
 |======
 
 [source,js]
@@ -1927,6 +1933,7 @@ NOTE: This only works on leading and trailing whitespace.
 |======
 | Name              | Required  | Default  | Description
 | `field`           | yes       | -        | The string-valued field to trim whitespace from
+| `target_field`    | no        | `field`  | The field to assign the trimmed value to, by default `field` is updated in-place
 | `ignore_missing`  | no        | `false`  | If `true` and `field` does not exist, the processor quietly exits without modifying the document
 |======
 
@@ -1950,6 +1957,7 @@ Converts a string to its uppercase equivalent.
 |======
 | Name             | Required  | Default  | Description
 | `field`          | yes       | -        | The field to make uppercase
+| `target_field`   | no        | `field`  | The field to assign the converted value to, by default `field` is updated in-place
 | `ignore_missing` | no        | `false`  | If `true` and `field` does not exist or is `null`, the processor quietly exits without modifying the document
 |======
 

+ 13 - 4
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java

@@ -33,11 +33,13 @@ import java.util.Map;
 abstract class AbstractStringProcessor extends AbstractProcessor {
     private final String field;
     private final boolean ignoreMissing;
+    private final String targetField;
 
-    AbstractStringProcessor(String tag, String field, boolean ignoreMissing) {
+    AbstractStringProcessor(String tag, String field, boolean ignoreMissing, String targetField) {
         super(tag);
         this.field = field;
         this.ignoreMissing = ignoreMissing;
+        this.targetField = targetField;
     }
 
     public String getField() {
@@ -48,6 +50,10 @@ abstract class AbstractStringProcessor extends AbstractProcessor {
         return ignoreMissing;
     }
 
+    String getTargetField() {
+        return targetField;
+    }
+
     @Override
     public final void execute(IngestDocument document) {
         String val = document.getFieldValue(field, String.class, ignoreMissing);
@@ -58,7 +64,7 @@ abstract class AbstractStringProcessor extends AbstractProcessor {
             throw new IllegalArgumentException("field [" + field + "] is null, cannot process it.");
         }
 
-        document.setFieldValue(field, process(val));
+        document.setFieldValue(targetField, process(val));
     }
 
     protected abstract String process(String value);
@@ -75,9 +81,12 @@ abstract class AbstractStringProcessor extends AbstractProcessor {
                                               Map<String, Object> config) throws Exception {
             String field = ConfigurationUtils.readStringProperty(processorType, tag, config, "field");
             boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(processorType, tag, config, "ignore_missing", false);
-            return newProcessor(tag, field, ignoreMissing);
+            String targetField = ConfigurationUtils.readStringProperty(processorType, tag, config, "target_field", field);
+
+            return newProcessor(tag, config, field, ignoreMissing, targetField);
         }
 
-        protected abstract AbstractStringProcessor newProcessor(String processorTag, String field, boolean ignoreMissing);
+        protected abstract AbstractStringProcessor newProcessor(String processorTag, Map<String, Object> config, String field,
+                                                                boolean ignoreMissing, String targetField);
     }
 }

+ 14 - 28
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java

@@ -19,12 +19,7 @@
 
 package org.elasticsearch.ingest.common;
 
-import org.elasticsearch.ingest.AbstractProcessor;
-import org.elasticsearch.ingest.IngestDocument;
-import org.elasticsearch.ingest.Processor;
-
 import java.util.Map;
-import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import static org.elasticsearch.ingest.ConfigurationUtils.newConfigurationException;
@@ -34,25 +29,19 @@ import static org.elasticsearch.ingest.ConfigurationUtils.readStringProperty;
  * Processor that allows to search for patterns in field content and replace them with corresponding string replacement.
  * Support fields of string type only, throws exception if a field is of a different type.
  */
-public final class GsubProcessor extends AbstractProcessor {
+public final class GsubProcessor extends AbstractStringProcessor {
 
     public static final String TYPE = "gsub";
 
-    private final String field;
     private final Pattern pattern;
     private final String replacement;
 
-    GsubProcessor(String tag, String field, Pattern pattern, String replacement) {
-        super(tag);
-        this.field = field;
+    GsubProcessor(String tag, String field, Pattern pattern, String replacement, boolean ignoreMissing, String targetField) {
+        super(tag, field, ignoreMissing, targetField);
         this.pattern = pattern;
         this.replacement = replacement;
     }
 
-    String getField() {
-        return field;
-    }
-
     Pattern getPattern() {
         return pattern;
     }
@@ -61,16 +50,9 @@ public final class GsubProcessor extends AbstractProcessor {
         return replacement;
     }
 
-
     @Override
-    public void execute(IngestDocument document) {
-        String oldVal = document.getFieldValue(field, String.class);
-        if (oldVal == null) {
-            throw new IllegalArgumentException("field [" + field + "] is null, cannot match pattern.");
-        }
-        Matcher matcher = pattern.matcher(oldVal);
-        String newVal = matcher.replaceAll(replacement);
-        document.setFieldValue(field, newVal);
+    protected String process(String value) {
+        return pattern.matcher(value).replaceAll(replacement);
     }
 
     @Override
@@ -78,11 +60,15 @@ public final class GsubProcessor extends AbstractProcessor {
         return TYPE;
     }
 
-    public static final class Factory implements Processor.Factory {
+    public static final class Factory extends AbstractStringProcessor.Factory {
+
+        public Factory() {
+            super(TYPE);
+        }
+
         @Override
-        public GsubProcessor create(Map<String, Processor.Factory> registry, String processorTag,
-                                    Map<String, Object> config) throws Exception {
-            String field = readStringProperty(TYPE, processorTag, config, "field");
+        protected AbstractStringProcessor newProcessor(String processorTag, Map<String, Object> config, String field,
+                                                       boolean ignoreMissing, String targetField) {
             String pattern = readStringProperty(TYPE, processorTag, config, "pattern");
             String replacement = readStringProperty(TYPE, processorTag, config, "replacement");
             Pattern searchPattern;
@@ -91,7 +77,7 @@ public final class GsubProcessor extends AbstractProcessor {
             } catch (Exception e) {
                 throw newConfigurationException(TYPE, processorTag, "pattern", "Invalid regex pattern. " + e.getMessage());
             }
-            return new GsubProcessor(processorTag, field, searchPattern, replacement);
+            return new GsubProcessor(processorTag, field, searchPattern, replacement, ignoreMissing, targetField);
         }
     }
 }

+ 10 - 3
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JoinProcessor.java

@@ -38,11 +38,13 @@ public final class JoinProcessor extends AbstractProcessor {
 
     private final String field;
     private final String separator;
+    private final String targetField;
 
-    JoinProcessor(String tag, String field, String separator) {
+    JoinProcessor(String tag, String field, String separator, String targetField) {
         super(tag);
         this.field = field;
         this.separator = separator;
+        this.targetField = targetField;
     }
 
     String getField() {
@@ -53,6 +55,10 @@ public final class JoinProcessor extends AbstractProcessor {
         return separator;
     }
 
+    String getTargetField() {
+        return targetField;
+    }
+
     @Override
     public void execute(IngestDocument document) {
         List<?> list = document.getFieldValue(field, List.class);
@@ -62,7 +68,7 @@ public final class JoinProcessor extends AbstractProcessor {
         String joined = list.stream()
                 .map(Object::toString)
                 .collect(Collectors.joining(separator));
-        document.setFieldValue(field, joined);
+        document.setFieldValue(targetField, joined);
     }
 
     @Override
@@ -76,7 +82,8 @@ public final class JoinProcessor extends AbstractProcessor {
                                     Map<String, Object> config) throws Exception {
             String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "field");
             String separator = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "separator");
-            return new JoinProcessor(processorTag, field, separator);
+            String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field", field);
+            return new JoinProcessor(processorTag, field, separator, targetField);
         }
     }
 }

+ 6 - 4
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.ingest.common;
 
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Processor that converts the content of string fields to lowercase.
@@ -30,8 +31,8 @@ public final class LowercaseProcessor extends AbstractStringProcessor {
 
     public static final String TYPE = "lowercase";
 
-    LowercaseProcessor(String processorTag, String field, boolean ignoreMissing) {
-        super(processorTag, field, ignoreMissing);
+    LowercaseProcessor(String processorTag, String field, boolean ignoreMissing, String targetField) {
+        super(processorTag, field, ignoreMissing, targetField);
     }
 
     @Override
@@ -51,8 +52,9 @@ public final class LowercaseProcessor extends AbstractStringProcessor {
         }
 
         @Override
-        protected LowercaseProcessor newProcessor(String tag, String field, boolean ignoreMissing) {
-            return new LowercaseProcessor(tag, field, ignoreMissing);
+        protected LowercaseProcessor newProcessor(String tag, Map<String, Object> config, String field,
+                                                  boolean ignoreMissing, String targetField) {
+            return new LowercaseProcessor(tag, field, ignoreMissing, targetField);
         }
     }
 }

+ 10 - 3
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/SortProcessor.java

@@ -69,11 +69,13 @@ public final class SortProcessor extends AbstractProcessor {
 
     private final String field;
     private final SortOrder order;
+    private final String targetField;
 
-    SortProcessor(String tag, String field, SortOrder order) {
+    SortProcessor(String tag, String field, SortOrder order, String targetField) {
         super(tag);
         this.field = field;
         this.order = order;
+        this.targetField = targetField;
     }
 
     String getField() {
@@ -84,6 +86,10 @@ public final class SortProcessor extends AbstractProcessor {
         return order;
     }
 
+    String getTargetField() {
+        return targetField;
+    }
+
     @Override
     @SuppressWarnings("unchecked")
     public void execute(IngestDocument document) {
@@ -103,7 +109,7 @@ public final class SortProcessor extends AbstractProcessor {
             Collections.sort(list, Collections.reverseOrder());
         }
 
-        document.setFieldValue(field, list);
+        document.setFieldValue(targetField, list);
     }
 
     @Override
@@ -117,6 +123,7 @@ public final class SortProcessor extends AbstractProcessor {
         public SortProcessor create(Map<String, Processor.Factory> registry, String processorTag,
                                     Map<String, Object> config) throws Exception {
             String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, FIELD);
+            String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field", field);
             try {
                 SortOrder direction = SortOrder.fromString(
                     ConfigurationUtils.readStringProperty(
@@ -125,7 +132,7 @@ public final class SortProcessor extends AbstractProcessor {
                         config,
                         ORDER,
                         DEFAULT_ORDER));
-                return new SortProcessor(processorTag, field, direction);
+                return new SortProcessor(processorTag, field, direction, targetField);
             } catch (IllegalArgumentException e) {
                 throw ConfigurationUtils.newConfigurationException(TYPE, processorTag, ORDER, e.getMessage());
             }

+ 11 - 4
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/SplitProcessor.java

@@ -32,7 +32,7 @@ import java.util.Map;
 /**
  * Processor that splits fields content into different items based on the occurrence of a specified separator.
  * New field value will be an array containing all of the different extracted items.
- * Throws exception if the field is null or a type other than string.
+ * Support fields of string type only, throws exception if a field is of a different type.
  */
 public final class SplitProcessor extends AbstractProcessor {
 
@@ -41,12 +41,14 @@ public final class SplitProcessor extends AbstractProcessor {
     private final String field;
     private final String separator;
     private final boolean ignoreMissing;
+    private final String targetField;
 
-    SplitProcessor(String tag, String field, String separator, boolean ignoreMissing) {
+    SplitProcessor(String tag, String field, String separator, boolean ignoreMissing, String targetField) {
         super(tag);
         this.field = field;
         this.separator = separator;
         this.ignoreMissing = ignoreMissing;
+        this.targetField = targetField;
     }
 
     String getField() {
@@ -61,6 +63,10 @@ public final class SplitProcessor extends AbstractProcessor {
         return ignoreMissing;
     }
 
+    String getTargetField() {
+        return targetField;
+    }
+
     @Override
     public void execute(IngestDocument document) {
         String oldVal = document.getFieldValue(field, String.class, ignoreMissing);
@@ -74,7 +80,7 @@ public final class SplitProcessor extends AbstractProcessor {
         String[] strings = oldVal.split(separator);
         List<String> splitList = new ArrayList<>(strings.length);
         Collections.addAll(splitList, strings);
-        document.setFieldValue(field, splitList);
+        document.setFieldValue(targetField, splitList);
     }
 
     @Override
@@ -88,8 +94,9 @@ public final class SplitProcessor extends AbstractProcessor {
                                      Map<String, Object> config) throws Exception {
             String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "field");
             boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "ignore_missing", false);
+            String targetField = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "target_field", field);
             return new SplitProcessor(processorTag, field,
-                ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "separator"), ignoreMissing);
+                ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "separator"), ignoreMissing, targetField);
         }
     }
 }

+ 7 - 4
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java

@@ -19,6 +19,8 @@
 
 package org.elasticsearch.ingest.common;
 
+import java.util.Map;
+
 /**
  * Processor that trims the content of string fields.
  * Throws exception is the field is not of type string.
@@ -27,8 +29,8 @@ public final class TrimProcessor extends AbstractStringProcessor {
 
     public static final String TYPE = "trim";
 
-    TrimProcessor(String processorTag, String field, boolean ignoreMissing) {
-        super(processorTag, field, ignoreMissing);
+    TrimProcessor(String processorTag, String field, boolean ignoreMissing, String targetField) {
+        super(processorTag, field, ignoreMissing, targetField);
     }
 
     @Override
@@ -48,8 +50,9 @@ public final class TrimProcessor extends AbstractStringProcessor {
         }
 
         @Override
-        protected TrimProcessor newProcessor(String tag, String field, boolean ignoreMissing) {
-            return new TrimProcessor(tag, field, ignoreMissing);
+        protected TrimProcessor newProcessor(String tag, Map<String, Object> config, String field,
+                                             boolean ignoreMissing, String targetField) {
+            return new TrimProcessor(tag, field, ignoreMissing, targetField);
         }
     }
 }

+ 6 - 5
modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.ingest.common;
 
 import java.util.Locale;
+import java.util.Map;
 
 /**
  * Processor that converts the content of string fields to uppercase.
@@ -29,8 +30,8 @@ public final class UppercaseProcessor extends AbstractStringProcessor {
 
     public static final String TYPE = "uppercase";
 
-    UppercaseProcessor(String processorTag, String field, boolean ignoreMissing) {
-        super(processorTag, field, ignoreMissing);
+    UppercaseProcessor(String processorTag, String field, boolean ignoreMissing, String targetField) {
+        super(processorTag, field, ignoreMissing, targetField);
     }
 
     @Override
@@ -50,9 +51,9 @@ public final class UppercaseProcessor extends AbstractStringProcessor {
         }
 
         @Override
-        protected UppercaseProcessor newProcessor(String tag, String field, boolean ignoreMissing) {
-            return new UppercaseProcessor(tag, field, ignoreMissing);
+        protected UppercaseProcessor newProcessor(String tag, Map<String, Object> config, String field,
+                                                  boolean ignoreMissing, String targetField) {
+            return new UppercaseProcessor(tag, field, ignoreMissing, targetField);
         }
     }
 }
-

+ 103 - 0
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java

@@ -0,0 +1,103 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.ingest.common;
+
+import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.ingest.RandomDocumentPicks;
+import org.elasticsearch.test.ESTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+public abstract class AbstractStringProcessorFactoryTestCase extends ESTestCase {
+
+    protected abstract AbstractStringProcessor.Factory newFactory();
+
+    protected Map<String, Object> modifyConfig(Map<String, Object> config) {
+        return config;
+    }
+
+    protected void assertProcessor(AbstractStringProcessor processor) {}
+
+    public void testCreate() throws Exception {
+        AbstractStringProcessor.Factory factory = newFactory();
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+        String processorTag = randomAlphaOfLength(10);
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+
+        AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config));
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.isIgnoreMissing(), is(false));
+        assertThat(processor.getTargetField(), equalTo(fieldName));
+        assertProcessor(processor);
+    }
+
+    public void testCreateWithIgnoreMissing() throws Exception {
+        AbstractStringProcessor.Factory factory = newFactory();
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+        String processorTag = randomAlphaOfLength(10);
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+        config.put("ignore_missing", true);
+
+        AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config));
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.isIgnoreMissing(), is(true));
+        assertThat(processor.getTargetField(), equalTo(fieldName));
+        assertProcessor(processor);
+    }
+
+    public void testCreateWithTargetField() throws Exception {
+        AbstractStringProcessor.Factory factory = newFactory();
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+        String processorTag = randomAlphaOfLength(10);
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+        config.put("target_field", targetFieldName);
+
+        AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config));
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.isIgnoreMissing(), is(false));
+        assertThat(processor.getTargetField(), equalTo(targetFieldName));
+        assertProcessor(processor);
+    }
+
+    public void testCreateMissingField() throws Exception {
+        AbstractStringProcessor.Factory factory = newFactory();
+        Map<String, Object> config = new HashMap<>();
+        try {
+            factory.create(null, null, config);
+            fail("factory create should have failed");
+        } catch(ElasticsearchParseException e) {
+            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
+        }
+    }
+}

+ 18 - 8
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java

@@ -33,7 +33,7 @@ import static org.hamcrest.Matchers.equalTo;
 
 public abstract class AbstractStringProcessorTestCase extends ESTestCase {
 
-    protected abstract AbstractStringProcessor newProcessor(String field, boolean ignoreMissing);
+    protected abstract AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField);
 
     protected String modifyInput(String input) {
         return input;
@@ -45,14 +45,14 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
         String fieldValue = RandomDocumentPicks.randomString(random());
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, modifyInput(fieldValue));
-        Processor processor = newProcessor(fieldName, randomBoolean());
+        Processor processor = newProcessor(fieldName, randomBoolean(), fieldName);
         processor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue(fieldName, String.class), equalTo(expectedResult(fieldValue)));
     }
 
     public void testFieldNotFound() throws Exception {
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = newProcessor(fieldName, false);
+        Processor processor = newProcessor(fieldName, false, fieldName);
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         Exception e = expectThrows(Exception.class, () -> processor.execute(ingestDocument));
         assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
@@ -60,7 +60,7 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
 
     public void testFieldNotFoundWithIgnoreMissing() throws Exception {
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = newProcessor(fieldName, true);
+        Processor processor = newProcessor(fieldName, true, fieldName);
         IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
         processor.execute(ingestDocument);
@@ -68,14 +68,14 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
     }
 
     public void testNullValue() throws Exception {
-        Processor processor = newProcessor("field", false);
+        Processor processor = newProcessor("field", false, "field");
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
         Exception e = expectThrows(Exception.class, () -> processor.execute(ingestDocument));
         assertThat(e.getMessage(), equalTo("field [field] is null, cannot process it."));
     }
 
     public void testNullValueWithIgnoreMissing() throws Exception {
-        Processor processor = newProcessor("field", true);
+        Processor processor = newProcessor("field", true, "field");
         IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
         IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
         processor.execute(ingestDocument);
@@ -84,7 +84,7 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
 
     public void testNonStringValue() throws Exception {
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = newProcessor(fieldName, false);
+        Processor processor = newProcessor(fieldName, false, fieldName);
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(fieldName, randomInt());
         Exception e = expectThrows(Exception.class, () -> processor.execute(ingestDocument));
@@ -94,11 +94,21 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
 
     public void testNonStringValueWithIgnoreMissing() throws Exception {
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = newProcessor(fieldName, true);
+        Processor processor = newProcessor(fieldName, true, fieldName);
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         ingestDocument.setFieldValue(fieldName, randomInt());
         Exception e = expectThrows(Exception.class, () -> processor.execute(ingestDocument));
         assertThat(e.getMessage(), equalTo("field [" + fieldName +
             "] of type [java.lang.Integer] cannot be cast to [java.lang.String]"));
     }
+
+    public void testTargetField() throws Exception {
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
+        String fieldValue = RandomDocumentPicks.randomString(random());
+        String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, modifyInput(fieldValue));
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+        Processor processor = newProcessor(fieldName, randomBoolean(), targetFieldName);
+        processor.execute(ingestDocument);
+        assertThat(ingestDocument.getFieldValue(targetFieldName, String.class), equalTo(expectedResult(fieldValue)));
+    }
 }

+ 2 - 2
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ForEachProcessorTests.java

@@ -49,7 +49,7 @@ public class ForEachProcessorTests extends ESTestCase {
         );
 
         ForEachProcessor processor = new ForEachProcessor(
-            "_tag", "values", new UppercaseProcessor("_tag", "_ingest._value", false)
+            "_tag", "values", new UppercaseProcessor("_tag", "_ingest._value", false, "_ingest._value")
         );
         processor.execute(ingestDocument);
 
@@ -197,7 +197,7 @@ public class ForEachProcessorTests extends ESTestCase {
 
         ForEachProcessor processor = new ForEachProcessor(
                 "_tag", "values", new CompoundProcessor(false,
-                Collections.singletonList(new UppercaseProcessor("_tag_upper", "_ingest._value", false)),
+                Collections.singletonList(new UppercaseProcessor("_tag_upper", "_ingest._value", false, "_ingest._value")),
                 Collections.singletonList(new AppendProcessor("_tag", template, (model) -> (Collections.singletonList("added"))))
         ));
         processor.execute(ingestDocument);

+ 14 - 23
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java

@@ -20,7 +20,6 @@
 package org.elasticsearch.ingest.common;
 
 import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.test.ESTestCase;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -28,33 +27,25 @@ import java.util.Map;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.Matchers.containsString;
 
-public class GsubProcessorFactoryTests extends ESTestCase {
+public class GsubProcessorFactoryTests extends AbstractStringProcessorFactoryTestCase {
 
-    public void testCreate() throws Exception {
-        GsubProcessor.Factory factory = new GsubProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        config.put("pattern", "\\.");
-        config.put("replacement", "-");
-        String processorTag = randomAlphaOfLength(10);
-        GsubProcessor gsubProcessor = factory.create(null, processorTag, config);
-        assertThat(gsubProcessor.getTag(), equalTo(processorTag));
-        assertThat(gsubProcessor.getField(), equalTo("field1"));
-        assertThat(gsubProcessor.getPattern().toString(), equalTo("\\."));
-        assertThat(gsubProcessor.getReplacement(), equalTo("-"));
+    @Override
+    protected AbstractStringProcessor.Factory newFactory() {
+        return new GsubProcessor.Factory();
     }
 
-    public void testCreateNoFieldPresent() throws Exception {
-        GsubProcessor.Factory factory = new GsubProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
+    @Override
+    protected Map<String, Object> modifyConfig(Map<String, Object> config) {
         config.put("pattern", "\\.");
         config.put("replacement", "-");
-        try {
-            factory.create(null, null, config);
-            fail("factory create should have failed");
-        } catch(ElasticsearchParseException e) {
-            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
-        }
+        return config;
+    }
+
+    @Override
+    protected void assertProcessor(AbstractStringProcessor processor) {
+        GsubProcessor gsubProcessor = (GsubProcessor) processor;
+        assertThat(gsubProcessor.getPattern().toString(), equalTo("\\."));
+        assertThat(gsubProcessor.getReplacement(), equalTo("-"));
     }
 
     public void testCreateNoPatternPresent() throws Exception {

+ 10 - 50
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java

@@ -19,62 +19,22 @@
 
 package org.elasticsearch.ingest.common;
 
-import org.elasticsearch.ingest.IngestDocument;
-import org.elasticsearch.ingest.Processor;
-import org.elasticsearch.ingest.RandomDocumentPicks;
-import org.elasticsearch.test.ESTestCase;
-
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.regex.Pattern;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.equalTo;
-
-public class GsubProcessorTests extends ESTestCase {
-
-    public void testGsub() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
-        String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, "127.0.0.1");
-        Processor processor = new GsubProcessor(randomAlphaOfLength(10), fieldName, Pattern.compile("\\."), "-");
-        processor.execute(ingestDocument);
-        assertThat(ingestDocument.getFieldValue(fieldName, String.class), equalTo("127-0-0-1"));
-    }
+public class GsubProcessorTests extends AbstractStringProcessorTestCase {
 
-    public void testGsubNotAStringValue() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
-        String fieldName = RandomDocumentPicks.randomFieldName(random());
-        ingestDocument.setFieldValue(fieldName, 123);
-        Processor processor = new GsubProcessor(randomAlphaOfLength(10), fieldName, Pattern.compile("\\."), "-");
-        try {
-            processor.execute(ingestDocument);
-            fail("processor execution should have failed");
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), equalTo("field [" + fieldName +
-                    "] of type [java.lang.Integer] cannot be cast to [java.lang.String]"));
-        }
+    @Override
+    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) {
+        return new GsubProcessor(randomAlphaOfLength(10), field, Pattern.compile("\\."), "-", ignoreMissing, targetField);
     }
 
-    public void testGsubFieldNotFound() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
-        String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = new GsubProcessor(randomAlphaOfLength(10), fieldName, Pattern.compile("\\."), "-");
-        try {
-            processor.execute(ingestDocument);
-            fail("processor execution should have failed");
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
-        }
+    @Override
+    protected String modifyInput(String input) {
+        return "127.0.0.1";
     }
 
-    public void testGsubNullValue() throws Exception {
-        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
-        Processor processor = new GsubProcessor(randomAlphaOfLength(10), "field", Pattern.compile("\\."), "-");
-        try {
-            processor.execute(ingestDocument);
-            fail("processor execution should have failed");
-        } catch (IllegalArgumentException e) {
-            assertThat(e.getMessage(), equalTo("field [field] is null, cannot match pattern."));
-        }
+    @Override
+    protected String expectedResult(String input) {
+        return "127-0-0-1";
     }
 }

+ 16 - 0
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java

@@ -20,6 +20,7 @@
 package org.elasticsearch.ingest.common;
 
 import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.ingest.RandomDocumentPicks;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.HashMap;
@@ -39,6 +40,7 @@ public class JoinProcessorFactoryTests extends ESTestCase {
         assertThat(joinProcessor.getTag(), equalTo(processorTag));
         assertThat(joinProcessor.getField(), equalTo("field1"));
         assertThat(joinProcessor.getSeparator(), equalTo("-"));
+        assertThat(joinProcessor.getTargetField(), equalTo("field1"));
     }
 
     public void testCreateNoFieldPresent() throws Exception {
@@ -64,4 +66,18 @@ public class JoinProcessorFactoryTests extends ESTestCase {
             assertThat(e.getMessage(), equalTo("[separator] required property is missing"));
         }
     }
+
+    public void testCreateWithTargetField() throws Exception {
+        JoinProcessor.Factory factory = new JoinProcessor.Factory();
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", "field1");
+        config.put("separator", "-");
+        config.put("target_field", "target");
+        String processorTag = randomAlphaOfLength(10);
+        JoinProcessor joinProcessor = factory.create(null, processorTag, config);
+        assertThat(joinProcessor.getTag(), equalTo(processorTag));
+        assertThat(joinProcessor.getField(), equalTo("field1"));
+        assertThat(joinProcessor.getSeparator(), equalTo("-"));
+        assertThat(joinProcessor.getTargetField(), equalTo("target"));
+    }
 }

+ 26 - 5
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorTests.java

@@ -51,7 +51,7 @@ public class JoinProcessorTests extends ESTestCase {
             }
         }
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, separator);
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, separator, fieldName);
         processor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue(fieldName, String.class), equalTo(expectedResult));
     }
@@ -71,7 +71,7 @@ public class JoinProcessorTests extends ESTestCase {
             }
         }
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, separator);
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, separator, fieldName);
         processor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue(fieldName, String.class), equalTo(expectedResult));
     }
@@ -80,7 +80,7 @@ public class JoinProcessorTests extends ESTestCase {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         String fieldName = RandomDocumentPicks.randomFieldName(random());
         ingestDocument.setFieldValue(fieldName, randomAlphaOfLengthBetween(1, 10));
-        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, "-");
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, "-", fieldName);
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
@@ -91,7 +91,7 @@ public class JoinProcessorTests extends ESTestCase {
     public void testJoinNonExistingField() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, "-");
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, "-", fieldName);
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
@@ -101,11 +101,32 @@ public class JoinProcessorTests extends ESTestCase {
 
     public void testJoinNullValue() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
-        Processor processor = new JoinProcessor(randomAlphaOfLength(10), "field", "-");
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), "field", "-", "field");
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
             assertThat(e.getMessage(), equalTo("field [field] is null, cannot join."));
         }
     }
+
+    public void testJoinWithTargetField() throws Exception {
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
+        int numItems = randomIntBetween(1, 10);
+        String separator = randomFrom(SEPARATORS);
+        List<String> fieldValue = new ArrayList<>(numItems);
+        String expectedResult = "";
+        for (int j = 0; j < numItems; j++) {
+            String value = randomAlphaOfLengthBetween(1, 10);
+            fieldValue.add(value);
+            expectedResult += value;
+            if (j < numItems - 1) {
+                expectedResult += separator;
+            }
+        }
+        String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+        Processor processor = new JoinProcessor(randomAlphaOfLength(10), fieldName, separator, targetFieldName);
+        processor.execute(ingestDocument);
+        assertThat(ingestDocument.getFieldValue(targetFieldName, String.class), equalTo(expectedResult));
+    }
 }

+ 4 - 43
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorFactoryTests.java

@@ -19,48 +19,9 @@
 
 package org.elasticsearch.ingest.common;
 
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.test.ESTestCase;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.Matchers.is;
-
-public class LowercaseProcessorFactoryTests extends ESTestCase {
-
-    public void testCreate() throws Exception {
-        LowercaseProcessor.Factory factory = new LowercaseProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        String processorTag = randomAlphaOfLength(10);
-        LowercaseProcessor uppercaseProcessor = (LowercaseProcessor)factory.create(null, processorTag, config);
-        assertThat(uppercaseProcessor.getTag(), equalTo(processorTag));
-        assertThat(uppercaseProcessor.getField(), equalTo("field1"));
-        assertThat(uppercaseProcessor.isIgnoreMissing(), is(false));
-    }
-
-    public void testCreateWithIgnoreMissing() throws Exception {
-        LowercaseProcessor.Factory factory = new LowercaseProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        config.put("ignore_missing", true);
-        String processorTag = randomAlphaOfLength(10);
-        LowercaseProcessor uppercaseProcessor = (LowercaseProcessor)factory.create(null, processorTag, config);
-        assertThat(uppercaseProcessor.getTag(), equalTo(processorTag));
-        assertThat(uppercaseProcessor.getField(), equalTo("field1"));
-        assertThat(uppercaseProcessor.isIgnoreMissing(), is(true));
-    }
-
-    public void testCreateMissingField() throws Exception {
-        LowercaseProcessor.Factory factory = new LowercaseProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        try {
-            factory.create(null, null, config);
-            fail("factory create should have failed");
-        } catch(ElasticsearchParseException e) {
-            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
-        }
+public class LowercaseProcessorFactoryTests extends AbstractStringProcessorFactoryTestCase {
+    @Override
+    protected AbstractStringProcessor.Factory newFactory() {
+        return new LowercaseProcessor.Factory();
     }
 }

+ 2 - 2
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java

@@ -23,8 +23,8 @@ import java.util.Locale;
 
 public class LowercaseProcessorTests extends AbstractStringProcessorTestCase {
     @Override
-    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing) {
-        return new LowercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing);
+    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) {
+        return new LowercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField);
     }
 
     @Override

+ 108 - 0
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorFactoryTests.java

@@ -0,0 +1,108 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.ingest.common;
+
+import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.ingest.RandomDocumentPicks;
+import org.elasticsearch.test.ESTestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+
+public class SortProcessorFactoryTests extends ESTestCase {
+
+    public void testCreate() throws Exception {
+        String processorTag = randomAlphaOfLength(10);
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+
+        SortProcessor.Factory factory = new SortProcessor.Factory();
+        SortProcessor processor = factory.create(null, processorTag, config);
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.getOrder(), equalTo(SortProcessor.SortOrder.ASCENDING));
+        assertThat(processor.getTargetField(), equalTo(fieldName));
+    }
+
+    public void testCreateWithOrder() throws Exception {
+        String processorTag = randomAlphaOfLength(10);
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+        config.put("order", "desc");
+
+        SortProcessor.Factory factory = new SortProcessor.Factory();
+        SortProcessor processor = factory.create(null, processorTag, config);
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.getOrder(), equalTo(SortProcessor.SortOrder.DESCENDING));
+        assertThat(processor.getTargetField(), equalTo(fieldName));
+    }
+
+    public void testCreateWithTargetField() throws Exception {
+        String processorTag = randomAlphaOfLength(10);
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+        config.put("target_field", targetFieldName);
+
+        SortProcessor.Factory factory = new SortProcessor.Factory();
+        SortProcessor processor = factory.create(null, processorTag, config);
+        assertThat(processor.getTag(), equalTo(processorTag));
+        assertThat(processor.getField(), equalTo(fieldName));
+        assertThat(processor.getOrder(), equalTo(SortProcessor.SortOrder.ASCENDING));
+        assertThat(processor.getTargetField(), equalTo(targetFieldName));
+    }
+
+    public void testCreateWithInvalidOrder() throws Exception {
+        String processorTag = randomAlphaOfLength(10);
+        String fieldName = RandomDocumentPicks.randomFieldName(random());
+
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", fieldName);
+        config.put("order", "invalid");
+
+        SortProcessor.Factory factory = new SortProcessor.Factory();
+        try {
+            factory.create(null, processorTag, config);
+            fail("factory create should have failed");
+        } catch (ElasticsearchParseException e) {
+            assertThat(e.getMessage(), equalTo("[order] Sort direction [invalid] not recognized. Valid values are: [asc, desc]"));
+        }
+    }
+
+    public void testCreateMissingField() throws Exception {
+        SortProcessor.Factory factory = new SortProcessor.Factory();
+        Map<String, Object> config = new HashMap<>();
+        try {
+            factory.create(null, null, config);
+            fail("factory create should have failed");
+        } catch(ElasticsearchParseException e) {
+            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
+        }
+    }
+}

+ 34 - 12
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SortProcessorTests.java

@@ -54,7 +54,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -68,7 +68,7 @@ public class SortProcessorTests extends ESTestCase {
         Collections.shuffle(fieldValue, random());
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, SortOrder.ASCENDING);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, SortOrder.ASCENDING, fieldName);
         processor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue(fieldName, List.class).toArray(), equalTo(expectedResult));
     }
@@ -91,7 +91,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -114,7 +114,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -137,7 +137,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -160,7 +160,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -183,7 +183,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -206,7 +206,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -234,7 +234,7 @@ public class SortProcessorTests extends ESTestCase {
         }
 
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         processor.execute(ingestDocument);
         assertEquals(ingestDocument.getFieldValue(fieldName, List.class), expectedResult);
     }
@@ -244,7 +244,7 @@ public class SortProcessorTests extends ESTestCase {
         String fieldName = RandomDocumentPicks.randomFieldName(random());
         ingestDocument.setFieldValue(fieldName, randomAlphaOfLengthBetween(1, 10));
         SortOrder order = randomBoolean() ? SortOrder.ASCENDING : SortOrder.DESCENDING;
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
@@ -256,7 +256,7 @@ public class SortProcessorTests extends ESTestCase {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         String fieldName = RandomDocumentPicks.randomFieldName(random());
         SortOrder order = randomBoolean() ? SortOrder.ASCENDING : SortOrder.DESCENDING;
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, fieldName);
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
@@ -267,7 +267,7 @@ public class SortProcessorTests extends ESTestCase {
     public void testSortNullValue() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
         SortOrder order = randomBoolean() ? SortOrder.ASCENDING : SortOrder.DESCENDING;
-        Processor processor = new SortProcessor(randomAlphaOfLength(10), "field", order);
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), "field", order, "field");
         try {
             processor.execute(ingestDocument);
         } catch(IllegalArgumentException e) {
@@ -275,6 +275,28 @@ public class SortProcessorTests extends ESTestCase {
         }
     }
 
+    public void testSortWithTargetField() throws Exception {
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
+        int numItems = randomIntBetween(1, 10);
+        List<String> fieldValue = new ArrayList<>(numItems);
+        List<String> expectedResult = new ArrayList<>(numItems);
+        for (int j = 0; j < numItems; j++) {
+            String value = randomAlphaOfLengthBetween(1, 10);
+            fieldValue.add(value);
+            expectedResult.add(value);
+        }
+        Collections.sort(expectedResult);
 
+        SortOrder order = randomBoolean() ? SortOrder.ASCENDING : SortOrder.DESCENDING;
+        if (order.equals(SortOrder.DESCENDING)) {
+            Collections.reverse(expectedResult);
+        }
+
+        String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, fieldValue);
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+        Processor processor = new SortProcessor(randomAlphaOfLength(10), fieldName, order, targetFieldName);
+        processor.execute(ingestDocument);
+        assertEquals(ingestDocument.getFieldValue(targetFieldName, List.class), expectedResult);
+    }
 
 }

+ 16 - 0
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SplitProcessorFactoryTests.java

@@ -40,6 +40,7 @@ public class SplitProcessorFactoryTests extends ESTestCase {
         assertThat(splitProcessor.getField(), equalTo("field1"));
         assertThat(splitProcessor.getSeparator(), equalTo("\\."));
         assertFalse(splitProcessor.isIgnoreMissing());
+        assertThat(splitProcessor.getTargetField(), equalTo("field1"));
     }
 
     public void testCreateNoFieldPresent() throws Exception {
@@ -65,4 +66,19 @@ public class SplitProcessorFactoryTests extends ESTestCase {
             assertThat(e.getMessage(), equalTo("[separator] required property is missing"));
         }
     }
+
+    public void testCreateWithTargetField() throws Exception {
+        SplitProcessor.Factory factory = new SplitProcessor.Factory();
+        Map<String, Object> config = new HashMap<>();
+        config.put("field", "field1");
+        config.put("separator", "\\.");
+        config.put("target_field", "target");
+        String processorTag = randomAlphaOfLength(10);
+        SplitProcessor splitProcessor = factory.create(null, processorTag, config);
+        assertThat(splitProcessor.getTag(), equalTo(processorTag));
+        assertThat(splitProcessor.getField(), equalTo("field1"));
+        assertThat(splitProcessor.getSeparator(), equalTo("\\."));
+        assertFalse(splitProcessor.isIgnoreMissing());
+        assertThat(splitProcessor.getTargetField(), equalTo("target"));
+    }
 }

+ 15 - 6
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SplitProcessorTests.java

@@ -39,7 +39,7 @@ public class SplitProcessorTests extends ESTestCase {
     public void testSplit() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
         String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, "127.0.0.1");
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false, fieldName);
         processor.execute(ingestDocument);
         assertThat(ingestDocument.getFieldValue(fieldName, List.class), equalTo(Arrays.asList("127", "0", "0", "1")));
     }
@@ -47,7 +47,7 @@ public class SplitProcessorTests extends ESTestCase {
     public void testSplitFieldNotFound() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         String fieldName = RandomDocumentPicks.randomFieldName(random());
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false, fieldName);
         try {
             processor.execute(ingestDocument);
             fail("split processor should have failed");
@@ -59,7 +59,7 @@ public class SplitProcessorTests extends ESTestCase {
     public void testSplitNullValue() throws Exception {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(),
             Collections.singletonMap("field", null));
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), "field", "\\.", false);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), "field", "\\.", false, "field");
         try {
             processor.execute(ingestDocument);
             fail("split processor should have failed");
@@ -73,7 +73,7 @@ public class SplitProcessorTests extends ESTestCase {
         IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(),
             Collections.singletonMap(fieldName, null));
         IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", true);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", true, fieldName);
         processor.execute(ingestDocument);
         assertIngestDocument(originalIngestDocument, ingestDocument);
     }
@@ -81,7 +81,7 @@ public class SplitProcessorTests extends ESTestCase {
     public void testSplitNonExistentWithIgnoreMissing() throws Exception {
         IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.emptyMap());
         IngestDocument ingestDocument = new IngestDocument(originalIngestDocument);
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), "field", "\\.", true);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), "field", "\\.", true, "field");
         processor.execute(ingestDocument);
         assertIngestDocument(originalIngestDocument, ingestDocument);
     }
@@ -90,7 +90,7 @@ public class SplitProcessorTests extends ESTestCase {
         IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
         String fieldName = RandomDocumentPicks.randomFieldName(random());
         ingestDocument.setFieldValue(fieldName, randomInt());
-        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false);
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false, fieldName);
         try {
             processor.execute(ingestDocument);
             fail("split processor should have failed");
@@ -116,4 +116,13 @@ public class SplitProcessorTests extends ESTestCase {
         assertThat(ingestDocument.getFieldValue("flags", List.class), equalTo(Arrays.asList("new", "hot", "super",
                 "fun", "interesting", "additional_flag")));
     }
+
+    public void testSplitWithTargetField() throws Exception {
+        IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
+        String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, "127.0.0.1");
+        String targetFieldName = RandomDocumentPicks.randomFieldName(random());
+        Processor processor = new SplitProcessor(randomAlphaOfLength(10), fieldName, "\\.", false, targetFieldName);
+        processor.execute(ingestDocument);
+        assertThat(ingestDocument.getFieldValue(targetFieldName, List.class), equalTo(Arrays.asList("127", "0", "0", "1")));
+    }
 }

+ 4 - 43
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorFactoryTests.java

@@ -19,48 +19,9 @@
 
 package org.elasticsearch.ingest.common;
 
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.test.ESTestCase;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.Matchers.is;
-
-public class TrimProcessorFactoryTests extends ESTestCase {
-
-    public void testCreate() throws Exception {
-        TrimProcessor.Factory factory = new TrimProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        String processorTag = randomAlphaOfLength(10);
-        TrimProcessor uppercaseProcessor = (TrimProcessor)factory.create(null, processorTag, config);
-        assertThat(uppercaseProcessor.getTag(), equalTo(processorTag));
-        assertThat(uppercaseProcessor.getField(), equalTo("field1"));
-        assertThat(uppercaseProcessor.isIgnoreMissing(), is(false));
-    }
-
-    public void testCreateWithIgnoreMissing() throws Exception {
-        TrimProcessor.Factory factory = new TrimProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        config.put("ignore_missing", true);
-        String processorTag = randomAlphaOfLength(10);
-        TrimProcessor uppercaseProcessor = (TrimProcessor)factory.create(null, processorTag, config);
-        assertThat(uppercaseProcessor.getTag(), equalTo(processorTag));
-        assertThat(uppercaseProcessor.getField(), equalTo("field1"));
-        assertThat(uppercaseProcessor.isIgnoreMissing(), is(true));
-    }
-
-    public void testCreateMissingField() throws Exception {
-        TrimProcessor.Factory factory = new TrimProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        try {
-            factory.create(null, null, config);
-            fail("factory create should have failed");
-        } catch(ElasticsearchParseException e) {
-            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
-        }
+public class TrimProcessorFactoryTests extends AbstractStringProcessorFactoryTestCase {
+    @Override
+    protected AbstractStringProcessor.Factory newFactory() {
+        return new TrimProcessor.Factory();
     }
 }

+ 2 - 2
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java

@@ -22,8 +22,8 @@ package org.elasticsearch.ingest.common;
 public class TrimProcessorTests extends AbstractStringProcessorTestCase {
 
     @Override
-    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing) {
-        return new TrimProcessor(randomAlphaOfLength(10), field, ignoreMissing);
+    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) {
+        return new TrimProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField);
     }
 
     @Override

+ 4 - 29
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorFactoryTests.java

@@ -19,34 +19,9 @@
 
 package org.elasticsearch.ingest.common;
 
-import org.elasticsearch.ElasticsearchParseException;
-import org.elasticsearch.test.ESTestCase;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-
-public class UppercaseProcessorFactoryTests extends ESTestCase {
-
-    public void testCreate() throws Exception {
-        UppercaseProcessor.Factory factory = new UppercaseProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        config.put("field", "field1");
-        String processorTag = randomAlphaOfLength(10);
-        UppercaseProcessor uppercaseProcessor = (UppercaseProcessor)factory.create(null, processorTag, config);
-        assertThat(uppercaseProcessor.getTag(), equalTo(processorTag));
-        assertThat(uppercaseProcessor.getField(), equalTo("field1"));
-    }
-
-    public void testCreateMissingField() throws Exception {
-        UppercaseProcessor.Factory factory = new UppercaseProcessor.Factory();
-        Map<String, Object> config = new HashMap<>();
-        try {
-            factory.create(null, null, config);
-            fail("factory create should have failed");
-        } catch(ElasticsearchParseException e) {
-            assertThat(e.getMessage(), equalTo("[field] required property is missing"));
-        }
+public class UppercaseProcessorFactoryTests extends AbstractStringProcessorFactoryTestCase {
+    @Override
+    protected AbstractStringProcessor.Factory newFactory() {
+        return new UppercaseProcessor.Factory();
     }
 }

+ 2 - 2
modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java

@@ -24,8 +24,8 @@ import java.util.Locale;
 public class UppercaseProcessorTests extends AbstractStringProcessorTestCase {
 
     @Override
-    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing) {
-        return new UppercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing);
+    protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) {
+        return new UppercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField);
     }
 
     @Override