Skip to content

Commit e07e5d6

Browse files
committed
Make reindex and lang-javascript compatible
Fixes two issues: 1. lang-javascript doesn't support `executable` with a `null` `vars` parameters. The parameter is quite nullable. 2. reindex didn't support script engines who's `unwrap` method wasn't a noop. This didn't come up for lang-groovy or lang-painless because both of those `unwrap`s were noops. lang-javascript copys all maps that it `unwrap`s. This adds fairly low level unit tests for these fixes but dosen't add an integration test that makes sure that reindex and lang-javascript play well together. That'd make backporting this difficult and would add a fairly significant amount of time to the build for a fairly rare interaction. Hopefully the unit tests will be enough.
1 parent 2904562 commit e07e5d6

File tree

4 files changed

+37
-14
lines changed

4 files changed

+37
-14
lines changed

modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkIndexByScrollAction.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,8 @@ public RequestWrapper<?> apply(RequestWrapper<?> request, ScrollableHitSource.Hi
450450
}
451451
if (context == null) {
452452
context = new HashMap<>();
453+
} else {
454+
context.clear();
453455
}
454456

455457
context.put(IndexFieldMapper.NAME, doc.getIndex());
@@ -485,50 +487,50 @@ public RequestWrapper<?> apply(RequestWrapper<?> request, ScrollableHitSource.Hi
485487
*/
486488
request.setSource((Map<String, Object>) resultCtx.remove(SourceFieldMapper.NAME));
487489

488-
Object newValue = context.remove(IndexFieldMapper.NAME);
490+
Object newValue = resultCtx.remove(IndexFieldMapper.NAME);
489491
if (false == doc.getIndex().equals(newValue)) {
490492
scriptChangedIndex(request, newValue);
491493
}
492-
newValue = context.remove(TypeFieldMapper.NAME);
494+
newValue = resultCtx.remove(TypeFieldMapper.NAME);
493495
if (false == doc.getType().equals(newValue)) {
494496
scriptChangedType(request, newValue);
495497
}
496-
newValue = context.remove(IdFieldMapper.NAME);
498+
newValue = resultCtx.remove(IdFieldMapper.NAME);
497499
if (false == doc.getId().equals(newValue)) {
498500
scriptChangedId(request, newValue);
499501
}
500-
newValue = context.remove(VersionFieldMapper.NAME);
502+
newValue = resultCtx.remove(VersionFieldMapper.NAME);
501503
if (false == Objects.equals(oldVersion, newValue)) {
502504
scriptChangedVersion(request, newValue);
503505
}
504-
newValue = context.remove(ParentFieldMapper.NAME);
506+
newValue = resultCtx.remove(ParentFieldMapper.NAME);
505507
if (false == Objects.equals(oldParent, newValue)) {
506508
scriptChangedParent(request, newValue);
507509
}
508510
/*
509511
* Its important that routing comes after parent in case you want to
510512
* change them both.
511513
*/
512-
newValue = context.remove(RoutingFieldMapper.NAME);
514+
newValue = resultCtx.remove(RoutingFieldMapper.NAME);
513515
if (false == Objects.equals(oldRouting, newValue)) {
514516
scriptChangedRouting(request, newValue);
515517
}
516-
newValue = context.remove(TimestampFieldMapper.NAME);
518+
newValue = resultCtx.remove(TimestampFieldMapper.NAME);
517519
if (false == Objects.equals(oldTimestamp, newValue)) {
518520
scriptChangedTimestamp(request, newValue);
519521
}
520-
newValue = context.remove(TTLFieldMapper.NAME);
522+
newValue = resultCtx.remove(TTLFieldMapper.NAME);
521523
if (false == Objects.equals(oldTTL, newValue)) {
522524
scriptChangedTTL(request, newValue);
523525
}
524526

525527
OpType newOpType = OpType.fromString(newOp);
526-
if (newOpType != oldOpType) {
528+
if (newOpType != oldOpType) {
527529
return scriptChangedOpType(request, oldOpType, newOpType);
528530
}
529531

530-
if (false == context.isEmpty()) {
531-
throw new IllegalArgumentException("Invalid fields added to context [" + String.join(",", context.keySet()) + ']');
532+
if (false == resultCtx.isEmpty()) {
533+
throw new IllegalArgumentException("Invalid fields added to context [" + String.join(",", resultCtx.keySet()) + ']');
532534
}
533535
return request;
534536
}

modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121

2222
import org.elasticsearch.script.ExecutableScript;
2323

24+
import java.util.HashMap;
2425
import java.util.Map;
2526
import java.util.function.Consumer;
2627

28+
import static org.elasticsearch.test.ESTestCase.randomBoolean;
29+
2730
public class SimpleExecutableScript implements ExecutableScript {
2831
private final Consumer<Map<String, Object>> script;
2932
private Map<String, Object> ctx;
@@ -50,6 +53,13 @@ public void setNextVar(String name, Object value) {
5053

5154
@Override
5255
public Object unwrap(Object value) {
56+
// Some script engines (javascript) copy any maps they unwrap
57+
if (randomBoolean()) {
58+
if (value instanceof Map) {
59+
return new HashMap<>((Map<?, ?>) value);
60+
}
61+
}
62+
// Others just return the objects plain (groovy, painless)
5363
return value;
5464
}
5565
}

plugins/lang-javascript/src/main/java/org/elasticsearch/script/javascript/JavaScriptScriptEngineService.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,16 @@ public Object compile(String scriptName, String scriptSource, Map<String, String
172172
}
173173

174174
@Override
175-
public ExecutableScript executable(CompiledScript compiledScript, Map<String, Object> vars) {
175+
public ExecutableScript executable(CompiledScript compiledScript, @Nullable Map<String, Object> vars) {
176176
Context ctx = Context.enter();
177177
try {
178178
Scriptable scope = ctx.newObject(globalScope);
179179
scope.setPrototype(globalScope);
180180
scope.setParentScope(null);
181-
for (Map.Entry<String, Object> entry : vars.entrySet()) {
182-
ScriptableObject.putProperty(scope, entry.getKey(), entry.getValue());
181+
if (vars != null) {
182+
for (Map.Entry<String, Object> entry : vars.entrySet()) {
183+
ScriptableObject.putProperty(scope, entry.getKey(), entry.getValue());
184+
}
183185
}
184186

185187
return new JavaScriptExecutableScript((Script) compiledScript.compiled(), scope);

plugins/lang-javascript/src/test/java/org/elasticsearch/script/javascript/JavaScriptScriptEngineTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.util.List;
2626
import java.util.Map;
2727

28+
import static java.util.Collections.emptyMap;
29+
2830
import org.elasticsearch.common.collect.MapBuilder;
2931
import org.elasticsearch.common.settings.Settings;
3032
import org.elasticsearch.script.CompiledScript;
@@ -59,6 +61,13 @@ public void testSimpleEquation() {
5961
assertThat(((Number) o).intValue(), equalTo(3));
6062
}
6163

64+
public void testNullVars() {
65+
CompiledScript script = new CompiledScript(ScriptService.ScriptType.INLINE, "testSimpleEquation", "js",
66+
se.compile(null, "1 + 2", emptyMap()));
67+
Object o = se.executable(script, null).run();
68+
assertThat(((Number) o).intValue(), equalTo(3));
69+
}
70+
6271
@SuppressWarnings("unchecked")
6372
public void testMapAccess() {
6473
Map<String, Object> vars = new HashMap<String, Object>();

0 commit comments

Comments
 (0)