IngestDocument to support accessing and modifying list items

When reading, through #getFieldValue and #hasField, and a list is encountered, the next element in the path is treated as the index of the item that the path points to (e.g. `list.0.key`). If the index is not a number or out of bounds, an exception gets thrown.

Added #appendFieldValue method that has the same behaviour as setFieldValue, but when a list is the last element in the path, instead of replacing the whole list it will simply add a new element to the existing list. This method is currently unused, we have to decide whether the set processor or a new processor should use it.

A few other changes made:
- Renamed hasFieldValue to hasField, as this method is not really about values but only keys. It will return true if a key is there but its value is null, while it returns false only when a field is not there at all.
- Changed null semantic in getFieldValue. null gets returned only when it was an actual value in the source, an exception is thrown otherwise when trying to access a non existing field, so that null != field not present.
- Made remove stricter about non existing fields. Throws error when trying to remove a non existing field. This is more consistent with the other methods in IngestDocument which are strict about fields that are not present.

Relates to #14324
This commit is contained in:
javanna 2015-11-28 12:25:12 +01:00 committed by Luca Cavanna
parent 467a47670c
commit 43b861b076
12 changed files with 616 additions and 133 deletions

View File

@ -67,29 +67,28 @@ public final class IngestDocument {
* @param path The path within the document in dot-notation
* @param clazz The expected class of the field value
* @return the value for the provided path if existing, null otherwise
* @throws IllegalArgumentException if the field is present but is not of the type provided as argument.
* @throws IllegalArgumentException if the field is null, empty, or if the source contains a field within the path
* which is not of the expected type
*/
public <T> T getFieldValue(String path, Class<T> clazz) {
if (path == null || path.length() == 0) {
return null;
if (Strings.isEmpty(path)) {
throw new IllegalArgumentException("path cannot be null nor empty");
}
String[] pathElements = Strings.splitStringToArray(path, '.');
assert pathElements.length > 0;
Map<String, Object> innerMap = getParent(pathElements);
if (innerMap == null) {
return null;
Object context = source;
for (String pathElement : pathElements) {
context = resolve(pathElement, path, context);
}
String leafKey = pathElements[pathElements.length - 1];
Object fieldValue = innerMap.get(leafKey);
if (fieldValue == null) {
if (context == null) {
return null;
}
if (clazz.isInstance(fieldValue)) {
return clazz.cast(fieldValue);
if (clazz.isInstance(context)) {
return clazz.cast(context);
}
throw new IllegalArgumentException("field [" + path + "] of type [" + fieldValue.getClass().getName() + "] cannot be cast to [" + clazz.getName() + "]");
throw new IllegalArgumentException("field [" + path + "] of type [" + context.getClass().getName() + "] cannot be cast to [" + clazz.getName() + "]");
}
/**
@ -97,18 +96,58 @@ public final class IngestDocument {
* @param path The path within the document in dot-notation
* @return true if the document contains a value for the field, false otherwise
*/
public boolean hasFieldValue(String path) {
if (path == null || path.length() == 0) {
public boolean hasField(String path) {
if (Strings.isEmpty(path)) {
return false;
}
String[] pathElements = Strings.splitStringToArray(path, '.');
assert pathElements.length > 0;
Map<String, Object> innerMap = getParent(pathElements);
if (innerMap == null) {
Object context = source;
for (int i = 0; i < pathElements.length - 1; i++) {
String pathElement = pathElements[i];
if (context == null) {
return false;
}
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) context;
context = map.get(pathElement);
} else if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
try {
int index = Integer.parseInt(pathElement);
if (index < 0 || index >= list.size()) {
return false;
}
context = list.get(index);
} catch (NumberFormatException e) {
return false;
}
} else {
return false;
}
}
String leafKey = pathElements[pathElements.length - 1];
return innerMap.containsKey(leafKey);
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) context;
return map.containsKey(leafKey);
}
if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
try {
int index = Integer.parseInt(leafKey);
return index >= 0 && index < list.size();
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
/**
@ -116,73 +155,182 @@ public final class IngestDocument {
* @param path the path of the field to be removed
*/
public void removeField(String path) {
if (path == null || path.length() == 0) {
return;
if (Strings.isEmpty(path)) {
throw new IllegalArgumentException("path cannot be null nor empty");
}
String[] pathElements = Strings.splitStringToArray(path, '.');
assert pathElements.length > 0;
Map<String, Object> parent = getParent(pathElements);
if (parent != null) {
String leafKey = pathElements[pathElements.length - 1];
if (parent.containsKey(leafKey)) {
sourceModified = true;
parent.remove(leafKey);
}
}
Object context = source;
for (int i = 0; i < pathElements.length - 1; i++) {
context = resolve(pathElements[i], path, context);
}
private Map<String, Object> getParent(String[] pathElements) {
Map<String, Object> innerMap = source;
for (int i = 0; i < pathElements.length - 1; i++) {
Object obj = innerMap.get(pathElements[i]);
if (obj instanceof Map) {
String leafKey = pathElements[pathElements.length - 1];
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> stringObjectMap = (Map<String, Object>) obj;
innerMap = stringObjectMap;
} else {
return null;
Map<String, Object> map = (Map<String, Object>) context;
if (map.containsKey(leafKey)) {
map.remove(leafKey);
this.sourceModified = true;
return;
}
throw new IllegalArgumentException("field [" + leafKey + "] not present as part of path [" + path + "]");
}
return innerMap;
if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
int index;
try {
index = Integer.parseInt(leafKey);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("[" + leafKey + "] is not an integer, cannot be used as an index as part of path [" + path + "]", e);
}
if (index < 0 || index >= list.size()) {
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" + list.size() + "] as part of path [" + path + "]");
}
list.remove(index);
this.sourceModified = true;
return;
}
if (context == null) {
throw new IllegalArgumentException("cannot remove [" + leafKey + "] from null as part of path [" + path + "]");
}
throw new IllegalArgumentException("cannot remove [" + leafKey + "] from object of type [" + context.getClass().getName() + "] as part of path [" + path + "]");
}
private static Object resolve(String pathElement, String fullPath, Object context) {
if (context == null) {
throw new IllegalArgumentException("cannot resolve [" + pathElement + "] from null as part of path [" + fullPath + "]");
}
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) context;
if (map.containsKey(pathElement)) {
return map.get(pathElement);
}
throw new IllegalArgumentException("field [" + pathElement + "] not present as part of path [" + fullPath + "]");
}
if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
int index;
try {
index = Integer.parseInt(pathElement);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("[" + pathElement + "] is not an integer, cannot be used as an index as part of path [" + fullPath + "]", e);
}
if (index < 0 || index >= list.size()) {
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" + list.size() + "] as part of path [" + fullPath + "]");
}
return list.get(index);
}
throw new IllegalArgumentException("cannot resolve [" + pathElement + "] from object of type [" + context.getClass().getName() + "] as part of path [" + fullPath + "]");
}
/**
* Appends the provided value to the provided path in the document.
* Any non existing path element will be created. Same as {@link #setFieldValue(String, Object)}
* but if the last element is a list, the value will be appended to the existing list.
* @param path The path within the document in dot-notation
* @param value The value to put in for the path key
*/
public void appendFieldValue(String path, Object value) {
setFieldValue(path, value, true);
}
/**
* Sets the provided value to the provided path in the document.
* Any non existing path element will be created.
* Any non existing path element will be created. If the last element is a list,
* the value will replace the existing list.
* @param path The path within the document in dot-notation
* @param value The value to put in for the path key
*/
public void setFieldValue(String path, Object value) {
if (path == null || path.length() == 0) {
throw new IllegalArgumentException("cannot add null or empty field");
setFieldValue(path, value, false);
}
private void setFieldValue(String path, Object value, boolean append) {
if (Strings.isEmpty(path)) {
throw new IllegalArgumentException("path cannot be null nor empty");
}
String[] pathElements = Strings.splitStringToArray(path, '.');
assert pathElements.length > 0;
Map<String, Object> inner = source;
Object context = source;
for (int i = 0; i < pathElements.length - 1; i++) {
String pathElement = pathElements[i];
if (inner.containsKey(pathElement)) {
Object object = inner.get(pathElement);
if (object instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> stringObjectMap = (Map<String, Object>) object;
inner = stringObjectMap;
} else if (object == null ) {
throw new IllegalArgumentException("cannot add field to null parent, [" + Map.class.getName() + "] expected instead.");
} else {
throw new IllegalArgumentException("cannot add field to parent [" + pathElement + "] of type [" + object.getClass().getName() + "], [" + Map.class.getName() + "] expected instead.");
if (context == null) {
throw new IllegalArgumentException("cannot resolve [" + pathElement + "] from null as part of path [" + path + "]");
}
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) context;
if (map.containsKey(pathElement)) {
context = map.get(pathElement);
} else {
Map<String, Object> newInnerMap = new HashMap<>();
inner.put(pathElement, newInnerMap);
inner = newInnerMap;
HashMap<Object, Object> newMap = new HashMap<>();
map.put(pathElement, newMap);
sourceModified = true;
context = newMap;
}
} else if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
int index;
try {
index = Integer.parseInt(pathElement);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("[" + pathElement + "] is not an integer, cannot be used as an index as part of path [" + path + "]", e);
}
if (index < 0 || index >= list.size()) {
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" + list.size() + "] as part of path [" + path + "]");
}
context = list.get(index);
} else {
throw new IllegalArgumentException("cannot resolve [" + pathElement + "] from object of type [" + context.getClass().getName() + "] as part of path [" + path + "]");
}
}
String leafKey = pathElements[pathElements.length - 1];
inner.put(leafKey, value);
if (context == null) {
throw new IllegalArgumentException("cannot set [" + leafKey + "] with null parent as part of path [" + path + "]");
}
if (context instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) context;
if (append) {
if (map.containsKey(leafKey)) {
Object object = map.get(leafKey);
if (object instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) object;
list.add(value);
sourceModified = true;
return;
}
}
}
map.put(leafKey, value);
sourceModified = true;
} else if (context instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) context;
int index;
try {
index = Integer.parseInt(leafKey);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("[" + leafKey + "] is not an integer, cannot be used as an index as part of path [" + path + "]", e);
}
if (index < 0 || index >= list.size()) {
throw new IllegalArgumentException("[" + index + "] is out of bounds for array with length [" + list.size() + "] as part of path [" + path + "]");
}
list.add(index, value);
this.sourceModified = true;
} else {
throw new IllegalArgumentException("cannot set [" + leafKey + "] with parent object of type [" + context.getClass().getName() + "] as part of path [" + path + "]");
}
}
public String getMetadata(MetaData metaData) {
@ -195,7 +343,7 @@ public final class IngestDocument {
/**
* Returns the document. Should be used only for reading. Any change made to the returned map will
* not be reflected to the modified flag. Modify the document instead using {@link #setFieldValue(String, Object)}
* not be reflected to the sourceModified flag. Modify the document instead using {@link #setFieldValue(String, Object)}
* and {@link #removeField(String)}
*/
public Map<String, Object> getSource() {

View File

@ -47,8 +47,8 @@ public class RenameProcessor implements Processor {
@Override
public void execute(IngestDocument document) {
for(Map.Entry<String, String> entry : fields.entrySet()) {
if (document.hasFieldValue(entry.getKey())) {
if (document.hasFieldValue(entry.getKey()) == false) {
if (document.hasField(entry.getKey())) {
if (document.hasField(entry.getKey()) == false) {
throw new IllegalArgumentException("field [" + entry.getKey() + "] doesn't exist");
}
Object oldValue = document.getFieldValue(entry.getKey(), Object.class);

View File

@ -45,7 +45,6 @@ import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.ESIntegTestCase;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -53,7 +52,6 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
public class IngestClientIT extends ESIntegTestCase {
@ -115,8 +113,37 @@ public class IngestClientIT extends ESIntegTestCase {
assertThat(response.getResults().size(), equalTo(1));
assertThat(response.getResults().get(0), instanceOf(SimulateDocumentSimpleResult.class));
SimulateDocumentSimpleResult simulateDocumentSimpleResult = (SimulateDocumentSimpleResult) response.getResults().get(0);
IngestDocument expectedIngestDocument = new IngestDocument("index", "type", "id", Collections.singletonMap("foo", "bar"));
assertThat(simulateDocumentSimpleResult.getIngestDocument(), equalTo(expectedIngestDocument));
assertThat(simulateDocumentSimpleResult.getIngestDocument(), nullValue());
assertThat(simulateDocumentSimpleResult.getFailure(), notNullValue());
response = new SimulatePipelineRequestBuilder(client(), SimulatePipelineAction.INSTANCE)
.setId("_id")
.setSource(jsonBuilder().startObject()
.startArray("docs")
.startObject()
.field("_index", "index")
.field("_type", "type")
.field("_id", "id")
.startObject("_source")
.field("field1", "123.42 400 <foo>")
.endObject()
.endObject()
.endArray()
.endObject().bytes())
.get();
assertThat(response.isVerbose(), equalTo(false));
assertThat(response.getPipelineId(), equalTo("_id"));
assertThat(response.getResults().size(), equalTo(1));
assertThat(response.getResults().get(0), instanceOf(SimulateDocumentSimpleResult.class));
simulateDocumentSimpleResult = (SimulateDocumentSimpleResult) response.getResults().get(0);
Map<String, Object> source = new HashMap<>();
source.put("field1", "123.42 400 <foo>");
source.put("val", 123.42f);
source.put("status", 400);
source.put("msg", "foo");
IngestDocument ingestDocument = new IngestDocument("index", "type", "id", source);
assertThat(simulateDocumentSimpleResult.getIngestDocument().getSource(), equalTo(ingestDocument.getSource()));
assertThat(simulateDocumentSimpleResult.getFailure(), nullValue());
}

View File

@ -22,9 +22,7 @@ package org.elasticsearch.ingest;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import static org.hamcrest.Matchers.*;
@ -40,8 +38,16 @@ public class IngestDocumentTests extends ESTestCase {
Map<String, Object> innerObject = new HashMap<>();
innerObject.put("buzz", "hello world");
innerObject.put("foo_null", null);
innerObject.put("1", "bar");
document.put("fizz", innerObject);
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> value = new HashMap<>();
value.put("field", "value");
list.add(value);
list.add(null);
document.put("list", list);
ingestDocument = new IngestDocument("index", "type", "id", document);
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
public void testSimpleGetFieldValue() {
@ -71,46 +77,127 @@ public class IngestDocumentTests extends ESTestCase {
public void testNestedGetFieldValue() {
assertThat(ingestDocument.getFieldValue("fizz.buzz", String.class), equalTo("hello world"));
assertThat(ingestDocument.getFieldValue("fizz.1", String.class), equalTo("bar"));
}
public void testNestedGetFieldValueTypeMismatch() {
try {
ingestDocument.getFieldValue("foo.foo.bar", String.class);
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot resolve [foo] from object of type [java.lang.String] as part of path [foo.foo.bar]"));
}
}
public void testListGetFieldValue() {
assertThat(ingestDocument.getFieldValue("list.0.field", String.class), equalTo("value"));
}
public void testListGetFieldValueNull() {
assertThat(ingestDocument.getFieldValue("list.1", String.class), nullValue());
}
public void testListGetFieldValueIndexNotNumeric() {
try {
ingestDocument.getFieldValue("list.test.field", String.class);
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[test] is not an integer, cannot be used as an index as part of path [list.test.field]"));
}
}
public void testListGetFieldValueIndexOutOfBounds() {
try {
ingestDocument.getFieldValue("list.10.field", String.class);
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[10] is out of bounds for array with length [2] as part of path [list.10.field]"));
}
}
public void testGetFieldValueNotFound() {
assertThat(ingestDocument.getFieldValue("not.here", String.class), nullValue());
try {
ingestDocument.getFieldValue("not.here", String.class);
fail("get field value should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [not] not present as part of path [not.here]"));
}
}
public void testGetFieldValueNotFoundNullParent() {
try {
ingestDocument.getFieldValue("fizz.foo_null.not_there", String.class);
fail("get field value should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot resolve [not_there] from null as part of path [fizz.foo_null.not_there]"));
}
}
public void testGetFieldValueNull() {
assertNull(ingestDocument.getFieldValue(null, String.class));
try {
ingestDocument.getFieldValue(null, String.class);
fail("get field value should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
}
}
public void testGetFieldValueEmpty() {
assertNull(ingestDocument.getFieldValue("", String.class));
try {
ingestDocument.getFieldValue("", String.class);
fail("get field value should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
}
}
public void testHasFieldValue() {
assertTrue(ingestDocument.hasFieldValue("fizz"));
public void testHasField() {
assertTrue(ingestDocument.hasField("fizz"));
}
public void testHasFieldValueNested() {
assertTrue(ingestDocument.hasFieldValue("fizz.buzz"));
public void testHasFieldNested() {
assertTrue(ingestDocument.hasField("fizz.buzz"));
}
public void testHasFieldValueNotFound() {
assertFalse(ingestDocument.hasFieldValue("doesnotexist"));
public void testListHasField() {
assertTrue(ingestDocument.hasField("list.0.field"));
}
public void testHasFieldValueNestedNotFound() {
assertFalse(ingestDocument.hasFieldValue("fizz.doesnotexist"));
public void testListHasFieldNull() {
assertTrue(ingestDocument.hasField("list.1"));
}
public void testHasFieldValueNull() {
assertFalse(ingestDocument.hasFieldValue(null));
public void testListHasFieldIndexOutOfBounds() {
assertFalse(ingestDocument.hasField("list.10"));
}
public void testHasFieldValueNullValue() {
assertTrue(ingestDocument.hasFieldValue("fizz.foo_null"));
public void testListHasFieldIndexNotNumeric() {
assertFalse(ingestDocument.hasField("list.test"));
}
public void testHasFieldValueEmpty() {
assertFalse(ingestDocument.hasFieldValue(""));
public void testNestedHasFieldTypeMismatch() {
assertFalse(ingestDocument.hasField("foo.foo.bar"));
}
public void testHasFieldNotFound() {
assertFalse(ingestDocument.hasField("not.here"));
}
public void testHasFieldNotFoundNullParent() {
assertFalse(ingestDocument.hasField("fizz.foo_null.not_there"));
}
public void testHasFieldNestedNotFound() {
assertFalse(ingestDocument.hasField("fizz.doesnotexist"));
}
public void testHasFieldNull() {
assertFalse(ingestDocument.hasField(null));
}
public void testHasFieldNullValue() {
assertTrue(ingestDocument.hasField("fizz.foo_null"));
}
public void testHasFieldEmpty() {
assertFalse(ingestDocument.hasField(""));
}
public void testSimpleSetFieldValue() {
@ -162,7 +249,7 @@ public class IngestDocumentTests extends ESTestCase {
ingestDocument.setFieldValue("fizz.buzz.new", "bar");
fail("add field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot add field to parent [buzz] of type [java.lang.String], [java.util.Map] expected instead."));
assertThat(e.getMessage(), equalTo("cannot set [new] with parent object of type [java.lang.String] as part of path [fizz.buzz.new]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
@ -172,7 +259,7 @@ public class IngestDocumentTests extends ESTestCase {
ingestDocument.setFieldValue("fizz.foo_null.test", "bar");
fail("add field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot add field to null parent, [java.util.Map] expected instead."));
assertThat(e.getMessage(), equalTo("cannot set [test] with null parent as part of path [fizz.foo_null.test]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
@ -182,7 +269,85 @@ public class IngestDocumentTests extends ESTestCase {
ingestDocument.setFieldValue(null, "bar");
fail("add field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot add null or empty field"));
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testListSetFieldValueNoIndexProvided() {
ingestDocument.setFieldValue("list", "value");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
Object object = ingestDocument.getSource().get("list");
assertThat(object, instanceOf(String.class));
assertThat(object, equalTo("value"));
}
public void testListAppendFieldValue() {
ingestDocument.appendFieldValue("list", "new_value");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
Object object = ingestDocument.getSource().get("list");
assertThat(object, instanceOf(List.class));
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) object;
assertThat(list.size(), equalTo(3));
assertThat(list.get(0), equalTo(Collections.singletonMap("field", "value")));
assertThat(list.get(1), nullValue());
assertThat(list.get(2), equalTo("new_value"));
}
public void testListSetFieldValueIndexProvided() {
ingestDocument.setFieldValue("list.1", "value");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
Object object = ingestDocument.getSource().get("list");
assertThat(object, instanceOf(List.class));
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) object;
assertThat(list.size(), equalTo(3));
assertThat(list.get(0), equalTo(Collections.singletonMap("field", "value")));
assertThat(list.get(1), equalTo("value"));
assertThat(list.get(2), nullValue());
}
public void testSetFieldValueListAsPartOfPath() {
ingestDocument.setFieldValue("list.0.field", "new_value");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
Object object = ingestDocument.getSource().get("list");
assertThat(object, instanceOf(List.class));
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) object;
assertThat(list.size(), equalTo(2));
assertThat(list.get(0), equalTo(Collections.singletonMap("field", "new_value")));
assertThat(list.get(1), nullValue());
}
public void testListSetFieldValueIndexNotNumeric() {
try {
ingestDocument.setFieldValue("list.test", "value");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[test] is not an integer, cannot be used as an index as part of path [list.test]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
try {
ingestDocument.setFieldValue("list.test.field", "new_value");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[test] is not an integer, cannot be used as an index as part of path [list.test.field]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testListSetFieldValueIndexOutOfBounds() {
try {
ingestDocument.setFieldValue("list.10", "value");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[10] is out of bounds for array with length [2] as part of path [list.10]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
try {
ingestDocument.setFieldValue("list.10.field", "value");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[10] is out of bounds for array with length [2] as part of path [list.10.field]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
@ -192,7 +357,7 @@ public class IngestDocumentTests extends ESTestCase {
ingestDocument.setFieldValue("", "bar");
fail("add field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot add null or empty field"));
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
@ -200,48 +365,127 @@ public class IngestDocumentTests extends ESTestCase {
public void testRemoveField() {
ingestDocument.removeField("foo");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
assertThat(ingestDocument.getSource().size(), equalTo(2));
assertThat(ingestDocument.getSource().size(), equalTo(3));
assertThat(ingestDocument.getSource().containsKey("foo"), equalTo(false));
}
public void testRemoveInnerField() {
ingestDocument.removeField("fizz.buzz");
assertThat(ingestDocument.getSource().size(), equalTo(3));
assertThat(ingestDocument.getSource().size(), equalTo(4));
assertThat(ingestDocument.getSource().get("fizz"), instanceOf(Map.class));
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) ingestDocument.getSource().get("fizz");
assertThat(map.size(), equalTo(1));
assertThat(map.size(), equalTo(2));
assertThat(map.containsKey("buzz"), equalTo(false));
ingestDocument.removeField("fizz.foo_null");
assertThat(map.size(), equalTo(1));
assertThat(ingestDocument.getSource().size(), equalTo(4));
assertThat(ingestDocument.getSource().containsKey("fizz"), equalTo(true));
assertThat(ingestDocument.isSourceModified(), equalTo(true));
ingestDocument.removeField("fizz.1");
assertThat(map.size(), equalTo(0));
assertThat(ingestDocument.getSource().size(), equalTo(3));
assertThat(ingestDocument.getSource().size(), equalTo(4));
assertThat(ingestDocument.getSource().containsKey("fizz"), equalTo(true));
assertThat(ingestDocument.isSourceModified(), equalTo(true));
}
public void testRemoveNonExistingField() {
try {
ingestDocument.removeField("does_not_exist");
fail("remove field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [does_not_exist] not present as part of path [does_not_exist]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
assertThat(ingestDocument.getSource().size(), equalTo(3));
}
}
public void testRemoveExistingParentTypeMismatch() {
ingestDocument.removeField("foo.test");
try {
ingestDocument.removeField("foo.foo.bar");
fail("remove field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot resolve [foo] from object of type [java.lang.String] as part of path [foo.foo.bar]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
assertThat(ingestDocument.getSource().size(), equalTo(3));
}
}
public void testListRemoveField() {
ingestDocument.removeField("list.0.field");
assertThat(ingestDocument.isSourceModified(), equalTo(true));
assertThat(ingestDocument.getSource().size(), equalTo(4));
assertThat(ingestDocument.getSource().containsKey("list"), equalTo(true));
Object object = ingestDocument.getSource().get("list");
assertThat(object, instanceOf(List.class));
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) object;
assertThat(list.size(), equalTo(2));
object = list.get(0);
assertThat(object, instanceOf(Map.class));
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) object;
assertThat(map.size(), equalTo(0));
ingestDocument.removeField("list.0");
assertThat(list.size(), equalTo(1));
assertThat(list.get(0), nullValue());
}
public void testRemoveFieldValueNotFoundNullParent() {
try {
ingestDocument.removeField("fizz.foo_null.not_there");
fail("get field value should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot remove [not_there] from null as part of path [fizz.foo_null.not_there]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testNestedRemoveFieldTypeMismatch() {
try {
ingestDocument.removeField("fizz.1.bar");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot remove [bar] from object of type [java.lang.String] as part of path [fizz.1.bar]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testListRemoveFieldIndexNotNumeric() {
try {
ingestDocument.removeField("list.test");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[test] is not an integer, cannot be used as an index as part of path [list.test]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testListRemoveFieldIndexOutOfBounds() {
try {
ingestDocument.removeField("list.10");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("[10] is out of bounds for array with length [2] as part of path [list.10]"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
}
}
public void testRemoveNullField() {
try {
ingestDocument.removeField(null);
fail("remove field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
assertThat(ingestDocument.getSource().size(), equalTo(3));
}
}
public void testRemoveEmptyField() {
try {
ingestDocument.removeField("");
fail("remove field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("path cannot be null nor empty"));
assertThat(ingestDocument.isSourceModified(), equalTo(false));
assertThat(ingestDocument.getSource().size(), equalTo(3));
}
}
public void testEqualsAndHashcode() throws Exception {
@ -289,9 +533,11 @@ public class IngestDocumentTests extends ESTestCase {
} else {
assertThat(ingestDocument, equalTo(otherIngestDocument));
assertThat(otherIngestDocument, equalTo(ingestDocument));
assertThat(ingestDocument.hashCode(), equalTo(otherIngestDocument.hashCode()));
IngestDocument thirdIngestDocument = new IngestDocument(index, type, id, Collections.singletonMap(fieldName, fieldValue));
assertThat(thirdIngestDocument, equalTo(ingestDocument));
assertThat(ingestDocument, equalTo(thirdIngestDocument));
assertThat(ingestDocument.hashCode(), equalTo(thirdIngestDocument.hashCode()));
}
}
}

View File

@ -23,12 +23,12 @@ import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public abstract class AbstractStringProcessorTestCase extends ESTestCase {
@ -57,7 +57,7 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
}
}
public void testNullValue() throws Exception {
public void testFieldNotFound() throws Exception {
String fieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = newProcessor(Collections.singletonList(fieldName));
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
@ -65,7 +65,18 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase {
processor.execute(ingestDocument);
fail("processor should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [" + fieldName + "] is null, cannot process it."));
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testNullValue() throws Exception {
Processor processor = newProcessor(Collections.singletonList("field"));
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
try {
processor.execute(ingestDocument);
fail("processor should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [field] is null, cannot process it."));
}
}

View File

@ -24,8 +24,9 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.Matchers.equalTo;
@ -44,7 +45,7 @@ public class AddProcessorTests extends ESTestCase {
processor.execute(ingestDocument);
for (Map.Entry<String, Object> field : fields.entrySet()) {
assertThat(ingestDocument.hasFieldValue(field.getKey()), equalTo(true));
assertThat(ingestDocument.hasField(field.getKey()), equalTo(true));
assertThat(ingestDocument.getFieldValue(field.getKey(), Object.class), equalTo(field.getValue()));
}
}
@ -63,7 +64,7 @@ public class AddProcessorTests extends ESTestCase {
Processor processor = new AddProcessor(fields);
processor.execute(ingestDocument);
for (Map.Entry<String, Object> field : fields.entrySet()) {
assertThat(ingestDocument.hasFieldValue(field.getKey()), equalTo(true));
assertThat(ingestDocument.hasField(field.getKey()), equalTo(true));
assertThat(ingestDocument.getFieldValue(field.getKey(), Object.class), equalTo(field.getValue()));
}
}
@ -76,7 +77,7 @@ public class AddProcessorTests extends ESTestCase {
processor.execute(ingestDocument);
fail("processor execute should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("cannot add field to parent [field] of type [java.lang.String], [java.util.Map] expected instead."));
assertThat(e.getMessage(), equalTo("cannot set [inner] with parent object of type [java.lang.String] as part of path [field.inner]"));
}
}
}

View File

@ -24,10 +24,10 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.*;
import static org.elasticsearch.ingest.processor.convert.ConvertProcessor.*;
import static org.elasticsearch.ingest.processor.convert.ConvertProcessor.Type;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class ConvertProcessorTests extends ESTestCase {
@ -309,7 +309,7 @@ public class ConvertProcessorTests extends ESTestCase {
}
}
public void testConvertNullField() throws Exception {
public void testConvertNonExistingField() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
Type type = randomFrom(Type.values());
@ -319,7 +319,20 @@ public class ConvertProcessorTests extends ESTestCase {
processor.execute(ingestDocument);
fail("processor execute should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("Field [" + fieldName + "] is null, cannot be converted to type [" + type + "]"));
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testConvertNullField() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
Type type = randomFrom(Type.values());
Map<String, Type> convert = Collections.singletonMap("field", type);
Processor processor = new ConvertProcessor(convert);
try {
processor.execute(ingestDocument);
fail("processor execute should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("Field [field] is null, cannot be converted to type [" + type + "]"));
}
}
}

View File

@ -24,13 +24,13 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class GsubProcessorTests extends ESTestCase {
@ -64,7 +64,7 @@ public class GsubProcessorTests extends ESTestCase {
}
}
public void testGsubNullValue() throws Exception {
public void testGsubFieldNotFound() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
List<GsubExpression> gsubExpressions = Collections.singletonList(new GsubExpression(fieldName, Pattern.compile("\\."), "-"));
@ -73,7 +73,19 @@ public class GsubProcessorTests extends ESTestCase {
processor.execute(ingestDocument);
fail("processor execution should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [" + fieldName + "] is null, cannot match pattern."));
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testGsubNullValue() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
List<GsubExpression> gsubExpressions = Collections.singletonList(new GsubExpression("field", Pattern.compile("\\."), "-"));
Processor processor = new GsubProcessor(gsubExpressions);
try {
processor.execute(ingestDocument);
fail("processor execution should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [field] is null, cannot match pattern."));
}
}
}

View File

@ -24,9 +24,9 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class JoinProcessorTests extends ESTestCase {
@ -111,7 +111,17 @@ public class JoinProcessorTests extends ESTestCase {
try {
processor.execute(ingestDocument);
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [" + fieldName + "] is null, cannot join."));
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testJoinNullValue() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
Processor processor = new JoinProcessor(Collections.singletonMap("field", "-"));
try {
processor.execute(ingestDocument);
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [field] is null, cannot join."));
}
}
}

View File

@ -24,14 +24,13 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.nullValue;
public class RemoveProcessorTests extends ESTestCase {
@ -45,15 +44,19 @@ public class RemoveProcessorTests extends ESTestCase {
Processor processor = new RemoveProcessor(fields);
processor.execute(ingestDocument);
for (String field : fields) {
assertThat(ingestDocument.getFieldValue(field, Object.class), nullValue());
assertThat(ingestDocument.hasFieldValue(field), equalTo(false));
assertThat(ingestDocument.hasField(field), equalTo(false));
}
}
public void testRemoveNonExistingField() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
Processor processor = new RemoveProcessor(Collections.singletonList(RandomDocumentPicks.randomFieldName(random())));
String fieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = new RemoveProcessor(Collections.singletonList(fieldName));
try {
processor.execute(ingestDocument);
assertThat(ingestDocument.getSource().size(), equalTo(0));
fail("remove field should have failed");
} catch(IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
}

View File

@ -70,8 +70,8 @@ public class RenameProcessorTests extends ESTestCase {
String newFieldName = RandomDocumentPicks.randomFieldName(random());
Processor processor = new RenameProcessor(Collections.singletonMap(fieldName, newFieldName));
processor.execute(ingestDocument);
assertThat(ingestDocument.hasFieldValue(fieldName), equalTo(false));
assertThat(ingestDocument.hasFieldValue(newFieldName), equalTo(true));
assertThat(ingestDocument.hasField(fieldName), equalTo(false));
assertThat(ingestDocument.hasField(newFieldName), equalTo(true));
assertThat(ingestDocument.getFieldValue(newFieldName, Object.class), nullValue());
}
}

View File

@ -24,9 +24,9 @@ import org.elasticsearch.ingest.RandomDocumentPicks;
import org.elasticsearch.ingest.processor.Processor;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.*;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class SplitProcessorTests extends ESTestCase {
@ -46,7 +46,7 @@ public class SplitProcessorTests extends ESTestCase {
}
}
public void testSplitNullValue() throws Exception {
public void testSplitFieldNotFound() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String fieldName = RandomDocumentPicks.randomFieldName(random());
Map<String, String> split = Collections.singletonMap(fieldName, "\\.");
@ -55,7 +55,19 @@ public class SplitProcessorTests extends ESTestCase {
processor.execute(ingestDocument);
fail("split processor should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [" + fieldName + "] is null, cannot split."));
assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]"));
}
}
public void testSplitNullValue() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.singletonMap("field", null));
Map<String, String> split = Collections.singletonMap("field", "\\.");
Processor processor = new SplitProcessor(split);
try {
processor.execute(ingestDocument);
fail("split processor should have failed");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo("field [field] is null, cannot split."));
}
}