Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5cbbd5e
Add option for Append Processor to skip/allow empty values
limotova Feb 14, 2024
d624991
Change allow_empty_values to ignore_empty_values
limotova Mar 9, 2024
c961392
Update docs/changelog/105718.yaml
limotova Jan 30, 2025
4e50ac7
Merge branch 'main' into add-allow-empty-values-append-processor
joegallo Sep 5, 2025
27c4e9a
Handle nulls as well
joegallo Sep 5, 2025
661766e
Rewrite setFieldValue in terms of valueNotEmpty
joegallo Sep 5, 2025
bea6446
Rewrite another setFieldValue arity, too
joegallo Sep 5, 2025
1675b1f
Verify an invariant
joegallo Sep 5, 2025
15123c9
This test can't handle nulls anymore
joegallo Sep 5, 2025
d46e6bc
Fix a typo
joegallo Sep 5, 2025
920fe67
Declare these once
joegallo Sep 5, 2025
6225a8b
Add some empty values into this test
joegallo Sep 5, 2025
fd0e386
Merge branch 'main' into add-allow-empty-values-append-processor
joegallo Sep 9, 2025
7eaaa20
Merge branch 'main' into add-allow-empty-values-append-processor
joegallo Sep 29, 2025
c103a7b
Ignore empty values with getFieldValue and copy_from
joegallo Sep 29, 2025
e2d5d76
Re-add the ignore_empty_values docs
joegallo Sep 29, 2025
a71ac87
It seems conventional to just label these '9.2'
joegallo Sep 29, 2025
d9d5753
Add an example
joegallo Sep 29, 2025
3161607
Better quoting
joegallo Sep 29, 2025
5ba1426
Add a test-only feature
joegallo Sep 29, 2025
6361a9e
Add some rest tests
joegallo Sep 29, 2025
f0b86a7
Fix typos
joegallo Sep 30, 2025
29a566a
Merge branch 'main' into add-allow-empty-values-append-processor
joegallo Sep 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/reference/ingest/processors/append.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Accepts a single value or an array of values.
| `value` | yes | - | The value to be appended. Supports <<template-snippets,template snippets>>.
| `allow_duplicates` | no | true | If `false`, the processor does not append
values already present in the field.
| `allow_empty_values` | no | true | If `false`, the processor does not append
values that resolve to empty strings.
| `media_type` | no | `application/json` | The media type for encoding `value`. Applies only when `value` is a
<<template-snippets,template snippet>>. Must be one of `application/json`, `text/plain`, or
`application/x-www-form-urlencoded`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@ public final class AppendProcessor extends AbstractProcessor {
private final TemplateScript.Factory field;
private final ValueSource value;
private final boolean allowDuplicates;
private final boolean allowEmptyValues;

AppendProcessor(String tag, String description, TemplateScript.Factory field, ValueSource value, boolean allowDuplicates) {
AppendProcessor(
String tag,
String description,
TemplateScript.Factory field,
ValueSource value,
boolean allowDuplicates,
boolean allowEmptyValues
) {
super(tag, description);
this.field = field;
this.value = value;
this.allowDuplicates = allowDuplicates;
this.allowEmptyValues = allowEmptyValues;
}

public TemplateScript.Factory getField() {
Expand All @@ -50,7 +59,7 @@ public ValueSource getValue() {
@Override
public IngestDocument execute(IngestDocument document) throws Exception {
String path = document.renderTemplate(field);
document.appendFieldValue(path, value, allowDuplicates);
document.appendFieldValue(path, value, allowDuplicates, allowEmptyValues);
return document;
}

Expand All @@ -77,14 +86,16 @@ public AppendProcessor create(
String field = ConfigurationUtils.readStringProperty(TYPE, processorTag, config, "field");
Object value = ConfigurationUtils.readObject(TYPE, processorTag, config, "value");
boolean allowDuplicates = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "allow_duplicates", true);
boolean allowEmptyValues = ConfigurationUtils.readBooleanProperty(TYPE, processorTag, config, "allow_empty_values", true);
TemplateScript.Factory compiledTemplate = ConfigurationUtils.compileTemplate(TYPE, processorTag, "field", field, scriptService);
String mediaType = ConfigurationUtils.readMediaTypeProperty(TYPE, processorTag, config, "media_type", "application/json");
return new AppendProcessor(
processorTag,
description,
compiledTemplate,
ValueSource.wrap(value, scriptService, Map.of(Script.CONTENT_TYPE_OPTION, mediaType)),
allowDuplicates
allowDuplicates,
allowEmptyValues
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ public void testAppendValuesToExistingList() throws Exception {
if (randomBoolean()) {
Object value = scalar.randomValue();
values.add(value);
appendProcessor = createAppendProcessor(field, value, true);
appendProcessor = createAppendProcessor(field, value, true, true);
} else {
int valuesSize = randomIntBetween(0, 10);
for (int i = 0; i < valuesSize; i++) {
values.add(scalar.randomValue());
}
appendProcessor = createAppendProcessor(field, values, true);
appendProcessor = createAppendProcessor(field, values, true, true);
}
appendProcessor.execute(ingestDocument);
Object fieldValue = ingestDocument.getFieldValue(field, Object.class);
Expand All @@ -79,13 +79,13 @@ public void testAppendValuesToNonExistingList() throws Exception {
if (randomBoolean()) {
Object value = scalar.randomValue();
values.add(value);
appendProcessor = createAppendProcessor(field, value, true);
appendProcessor = createAppendProcessor(field, value, true, true);
} else {
int valuesSize = randomIntBetween(0, 10);
for (int i = 0; i < valuesSize; i++) {
values.add(scalar.randomValue());
}
appendProcessor = createAppendProcessor(field, values, true);
appendProcessor = createAppendProcessor(field, values, true, true);
}
appendProcessor.execute(ingestDocument);
List<?> list = ingestDocument.getFieldValue(field, List.class);
Expand All @@ -103,13 +103,13 @@ public void testConvertScalarToList() throws Exception {
if (randomBoolean()) {
Object value = scalar.randomValue();
values.add(value);
appendProcessor = createAppendProcessor(field, value, true);
appendProcessor = createAppendProcessor(field, value, true, true);
} else {
int valuesSize = randomIntBetween(0, 10);
for (int i = 0; i < valuesSize; i++) {
values.add(scalar.randomValue());
}
appendProcessor = createAppendProcessor(field, values, true);
appendProcessor = createAppendProcessor(field, values, true, true);
}
appendProcessor.execute(ingestDocument);
List<?> fieldValue = ingestDocument.getFieldValue(field, List.class);
Expand All @@ -127,7 +127,7 @@ public void testAppendingDuplicateValueToScalarDoesNotModifyDocument() throws Ex

List<Object> valuesToAppend = new ArrayList<>();
valuesToAppend.add(originalValue);
Processor appendProcessor = createAppendProcessor(field, valuesToAppend, false);
Processor appendProcessor = createAppendProcessor(field, valuesToAppend, false, true);
appendProcessor.execute(ingestDocument);
Object fieldValue = ingestDocument.getFieldValue(field, Object.class);
assertThat(fieldValue, not(instanceOf(List.class)));
Expand All @@ -142,7 +142,7 @@ public void testAppendingUniqueValueToScalar() throws Exception {
List<Object> valuesToAppend = new ArrayList<>();
String newValue = randomValueOtherThan(originalValue, () -> randomAlphaOfLengthBetween(1, 10));
valuesToAppend.add(newValue);
Processor appendProcessor = createAppendProcessor(field, valuesToAppend, false);
Processor appendProcessor = createAppendProcessor(field, valuesToAppend, false, true);
appendProcessor.execute(ingestDocument);
List<?> list = ingestDocument.getFieldValue(field, List.class);
assertThat(list.size(), equalTo(2));
Expand Down Expand Up @@ -171,20 +171,140 @@ public void testAppendingToListWithDuplicatesDisallowed() throws Exception {
Collections.sort(valuesToAppend);

// attempt to append both new and existing values
Processor appendProcessor = createAppendProcessor(originalField, valuesToAppend, false);
Processor appendProcessor = createAppendProcessor(originalField, valuesToAppend, false, true);
appendProcessor.execute(ingestDocument);
List<?> fieldValue = ingestDocument.getFieldValue(originalField, List.class);
assertThat(fieldValue, sameInstance(list));
assertThat(fieldValue, containsInAnyOrder(expectedValues.toArray()));
}

private static Processor createAppendProcessor(String fieldName, Object fieldValue, boolean allowDuplicates) {
public void testAppendingToListWithNoEmptyValuesAndEmptyValuesDisallowed() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
Scalar scalar = randomFrom(Scalar.values());
List<Object> list = new ArrayList<>();
int size = randomIntBetween(0, 10);
for (int i = 0; i < size; i++) {
list.add(scalar.randomValue());
}
List<Object> checkList = new ArrayList<>(list);
String field = RandomDocumentPicks.addRandomField(random(), ingestDocument, list);
List<Object> values = new ArrayList<>();
Processor appendProcessor;
if (randomBoolean()) {
Object value = scalar.randomValue();
values.add(value);
appendProcessor = createAppendProcessor(field, value, true, false);
} else {
int valuesSize = randomIntBetween(0, 10);
for (int i = 0; i < valuesSize; i++) {
values.add(scalar.randomValue());
}
appendProcessor = createAppendProcessor(field, values, true, false);
}
appendProcessor.execute(ingestDocument);
Object fieldValue = ingestDocument.getFieldValue(field, Object.class);
assertThat(fieldValue, sameInstance(list));
assertThat(list.size(), equalTo(size + values.size()));
for (int i = 0; i < size; i++) {
assertThat(list.get(i), equalTo(checkList.get(i)));
}
for (int i = size; i < size + values.size(); i++) {
assertThat(list.get(i), equalTo(values.get(i - size)));
}
}

public void testAppendingToListEmptyStringAndEmptyValuesDisallowed() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random());
Scalar scalar = Scalar.STRING;
List<Object> list = new ArrayList<>();
int size = randomIntBetween(0, 10);
for (int i = 0; i < size; i++) {
list.add(scalar.randomValue());
}
List<Object> checkList = new ArrayList<>(list);
String field = RandomDocumentPicks.addRandomField(random(), ingestDocument, list);
List<Object> values = new ArrayList<>();
Processor appendProcessor;
if (randomBoolean()) {
Object value;
if (randomBoolean()) {
value = "";
} else {
value = scalar.randomValue();
values.add(value);
}
appendProcessor = createAppendProcessor(field, value, true, false);
} else {
int valuesSize = randomIntBetween(0, 10);
List<Object> allValues = new ArrayList<>();
for (int i = 0; i < valuesSize; i++) {
Object value;
if (randomBoolean()) {
value = "";
} else {
value = scalar.randomValue();
values.add(value);
}
allValues.add(value);
}
appendProcessor = createAppendProcessor(field, allValues, true, false);
}
appendProcessor.execute(ingestDocument);
Object fieldValue = ingestDocument.getFieldValue(field, Object.class);
assertThat(fieldValue, sameInstance(list));
assertThat(list.size(), equalTo(size + values.size()));
for (int i = 0; i < size; i++) {
assertThat(list.get(i), equalTo(checkList.get(i)));
}
for (int i = size; i < size + values.size(); i++) {
assertThat(list.get(i), equalTo(values.get(i - size)));
}
}

public void testAppendingToNonExistingListEmptyStringAndEmpyValuesDisallowed() throws Exception {
IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>());
String field = RandomDocumentPicks.randomFieldName(random());
Scalar scalar = Scalar.STRING;
List<Object> values = new ArrayList<>();
Processor appendProcessor;
if (randomBoolean()) {
Object value;
if (randomBoolean()) {
value = "";
} else {
value = scalar.randomValue();
values.add(value);
}
appendProcessor = createAppendProcessor(field, value, true, false);
} else {
List<Object> allValues = new ArrayList<>();
int valuesSize = randomIntBetween(0, 10);
for (int i = 0; i < valuesSize; i++) {
Object value;
if (randomBoolean()) {
value = "";
} else {
value = scalar.randomValue();
values.add(value);
}
allValues.add(value);
}
appendProcessor = createAppendProcessor(field, allValues, true, false);
}
appendProcessor.execute(ingestDocument);
List<?> list = ingestDocument.getFieldValue(field, List.class);
assertThat(list, not(sameInstance(values)));
assertThat(list, equalTo(values));
}

private static Processor createAppendProcessor(String fieldName, Object fieldValue, boolean allowDuplicates, boolean allowEmptyValues) {
return new AppendProcessor(
randomAlphaOfLength(10),
null,
new TestTemplateService.MockTemplateScript.Factory(fieldName),
ValueSource.wrap(fieldValue, TestTemplateService.instance()),
allowDuplicates
allowDuplicates,
allowEmptyValues
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public void testModifyFieldsOutsideArray() {
new CompoundProcessor(
false,
List.of(new UppercaseProcessor("_tag_upper", null, "_ingest._value", false, "_ingest._value")),
List.of(new AppendProcessor("_tag", null, template, (model) -> (List.of("added")), true))
List.of(new AppendProcessor("_tag", null, template, (model) -> (List.of("added")), true, true))
),
false
);
Expand Down
Loading