Provide more context variables in update scripts
In addition to `_source`, the following variables are available through the `ctx` map: `_index`, `_type`, `_id`, `_version`, `_routing`, `_parent`, `_timestamp`, `_ttl`. Some of these fields are more useful still within the context of an Update By Query, see #1607, #2230, #2231.
This commit is contained in:
parent
936b4c63fc
commit
4d68d3d053
|
@ -234,3 +234,7 @@ It also allows to update the `ttl` of a document using `ctx._ttl` and
|
|||
timestamp using `ctx._timestamp`. Note that if the timestamp is not
|
||||
updated and not extracted from the `_source` it will be set to the
|
||||
update date.
|
||||
|
||||
In addition to `_source`, the following variables are available through
|
||||
the `ctx` map: `_index`, `_type`, `_id`, `_version`, `_routing`,
|
||||
`_parent`, `_timestamp`, `_ttl`.
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.elasticsearch.index.get.GetResult;
|
|||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
||||
import org.elasticsearch.index.mapper.internal.RoutingFieldMapper;
|
||||
import org.elasticsearch.index.mapper.internal.TTLFieldMapper;
|
||||
import org.elasticsearch.index.mapper.internal.TimestampFieldMapper;
|
||||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.index.shard.service.IndexShard;
|
||||
import org.elasticsearch.script.ExecutableScript;
|
||||
|
@ -74,7 +76,7 @@ public class UpdateHelper extends AbstractComponent {
|
|||
public Result prepare(UpdateRequest request, IndexShard indexShard) {
|
||||
long getDate = System.currentTimeMillis();
|
||||
final GetResult getResult = indexShard.getService().get(request.type(), request.id(),
|
||||
new String[]{RoutingFieldMapper.NAME, ParentFieldMapper.NAME, TTLFieldMapper.NAME},
|
||||
new String[]{RoutingFieldMapper.NAME, ParentFieldMapper.NAME, TTLFieldMapper.NAME, TimestampFieldMapper.NAME},
|
||||
true, request.version(), request.versionType(), FetchSourceContext.FETCH_SOURCE, false);
|
||||
|
||||
if (!getResult.isExists()) {
|
||||
|
@ -148,7 +150,7 @@ public class UpdateHelper extends AbstractComponent {
|
|||
|
||||
Tuple<XContentType, Map<String, Object>> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true);
|
||||
String operation = null;
|
||||
String timestamp;
|
||||
String timestamp = null;
|
||||
Long ttl = null;
|
||||
final Map<String, Object> updatedSourceAsMap;
|
||||
final XContentType updateSourceContentType = sourceAndContent.v1();
|
||||
|
@ -176,7 +178,17 @@ public class UpdateHelper extends AbstractComponent {
|
|||
operation = "none";
|
||||
}
|
||||
} else {
|
||||
Map<String, Object> ctx = new HashMap<>(2);
|
||||
Map<String, Object> ctx = new HashMap<>(16);
|
||||
Long originalTtl = getResult.getFields().containsKey(TTLFieldMapper.NAME) ? (Long) getResult.field(TTLFieldMapper.NAME).getValue() : null;
|
||||
Long originalTimestamp = getResult.getFields().containsKey(TimestampFieldMapper.NAME) ? (Long) getResult.field(TimestampFieldMapper.NAME).getValue() : null;
|
||||
ctx.put("_index", getResult.getIndex());
|
||||
ctx.put("_type", getResult.getType());
|
||||
ctx.put("_id", getResult.getId());
|
||||
ctx.put("_version", getResult.getVersion());
|
||||
ctx.put("_routing", routing);
|
||||
ctx.put("_parent", parent);
|
||||
ctx.put("_timestamp", originalTimestamp);
|
||||
ctx.put("_ttl", originalTtl);
|
||||
ctx.put("_source", sourceAndContent.v2());
|
||||
|
||||
try {
|
||||
|
@ -190,7 +202,14 @@ public class UpdateHelper extends AbstractComponent {
|
|||
}
|
||||
|
||||
operation = (String) ctx.get("op");
|
||||
timestamp = (String) ctx.get("_timestamp");
|
||||
|
||||
Object fetchedTimestamp = ctx.get("_timestamp");
|
||||
if (fetchedTimestamp != null) {
|
||||
timestamp = fetchedTimestamp.toString();
|
||||
} else if (originalTimestamp != null) {
|
||||
// No timestamp has been given in the update script, so we keep the previous timestamp if there is one
|
||||
timestamp = originalTimestamp.toString();
|
||||
}
|
||||
|
||||
ttl = getTTLFromScriptContext(ctx);
|
||||
|
||||
|
|
|
@ -461,6 +461,99 @@ public class UpdateTests extends ElasticsearchIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextVariables() throws Exception {
|
||||
createTestIndex();
|
||||
|
||||
// Add child type for testing the _parent context variable
|
||||
client().admin().indices().preparePutMapping("test")
|
||||
.setType("subtype1")
|
||||
.setSource(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("subtype1")
|
||||
.startObject("_parent").field("type", "type1").endObject()
|
||||
.startObject("_timestamp").field("enabled", true).field("store", "yes").endObject()
|
||||
.startObject("_ttl").field("enabled", true).field("store", "yes").endObject()
|
||||
.endObject()
|
||||
.endObject())
|
||||
.execute().actionGet();
|
||||
ensureGreen();
|
||||
|
||||
// Index some documents
|
||||
long timestamp = System.currentTimeMillis();
|
||||
client().prepareIndex()
|
||||
.setIndex("test")
|
||||
.setType("type1")
|
||||
.setId("parentId1")
|
||||
.setTimestamp(String.valueOf(timestamp-1))
|
||||
.setSource("field1", 0, "content", "bar")
|
||||
.execute().actionGet();
|
||||
|
||||
client().prepareIndex()
|
||||
.setIndex("test")
|
||||
.setType("subtype1")
|
||||
.setId("id1")
|
||||
.setParent("parentId1")
|
||||
.setRouting("routing1")
|
||||
.setTimestamp(String.valueOf(timestamp))
|
||||
.setTTL(111211211)
|
||||
.setSource("field1", 1, "content", "foo")
|
||||
.execute().actionGet();
|
||||
long postIndexTs = System.currentTimeMillis();
|
||||
|
||||
// Update the first object and note context variables values
|
||||
Map<String, Object> scriptParams = new HashMap<>();
|
||||
scriptParams.put("delim", "_");
|
||||
UpdateResponse updateResponse = client().prepareUpdate("test", "subtype1", "id1")
|
||||
.setRouting("routing1")
|
||||
.setScript(
|
||||
"assert ctx._index == \"test\" : \"index should be \\\"test\\\"\"\n" +
|
||||
"assert ctx._type == \"subtype1\" : \"type should be \\\"subtype1\\\"\"\n" +
|
||||
"assert ctx._id == \"id1\" : \"id should be \\\"id1\\\"\"\n" +
|
||||
"assert ctx._version == 1 : \"version should be 1\"\n" +
|
||||
"assert ctx._parent == \"parentId1\" : \"parent should be \\\"parentId1\\\"\"\n" +
|
||||
"assert ctx._routing == \"routing1\" : \"routing should be \\\"routing1\\\"\"\n" +
|
||||
"assert ctx._timestamp == " + timestamp + " : \"timestamp should be " + timestamp + "\"\n" +
|
||||
"def now = new Date().getTime()\n" +
|
||||
"assert (111211211 - ctx._ttl) <= (now - " + postIndexTs + ") : \"ttl is not within acceptable range\"\n" +
|
||||
"ctx._source.content = ctx._source.content + delim + ctx._source.content;\n" +
|
||||
"ctx._source.field1 += 1;\n",
|
||||
ScriptService.ScriptType.INLINE)
|
||||
.setScriptParams(scriptParams)
|
||||
.execute().actionGet();
|
||||
|
||||
assertEquals(2, updateResponse.getVersion());
|
||||
|
||||
GetResponse getResponse = client().prepareGet("test", "subtype1", "id1").setRouting("routing1").execute().actionGet();
|
||||
assertEquals(2, getResponse.getSourceAsMap().get("field1"));
|
||||
assertEquals("foo_foo", getResponse.getSourceAsMap().get("content"));
|
||||
|
||||
// Idem with the second object
|
||||
scriptParams = new HashMap<>();
|
||||
scriptParams.put("delim", "_");
|
||||
updateResponse = client().prepareUpdate("test", "type1", "parentId1")
|
||||
.setScript(
|
||||
"assert ctx._index == \"test\" : \"index should be \\\"test\\\"\"\n" +
|
||||
"assert ctx._type == \"type1\" : \"type should be \\\"type1\\\"\"\n" +
|
||||
"assert ctx._id == \"parentId1\" : \"id should be \\\"parentId1\\\"\"\n" +
|
||||
"assert ctx._version == 1 : \"version should be 1\"\n" +
|
||||
"assert ctx._parent == null : \"parent should be null\"\n" +
|
||||
"assert ctx._routing == null : \"routing should be null\"\n" +
|
||||
"assert ctx._timestamp == " + (timestamp - 1) + " : \"timestamp should be " + (timestamp - 1) + "\"\n" +
|
||||
"assert ctx._ttl == null : \"ttl should be null\"\n" +
|
||||
"ctx._source.content = ctx._source.content + delim + ctx._source.content;\n" +
|
||||
"ctx._source.field1 += 1;\n",
|
||||
ScriptService.ScriptType.INLINE)
|
||||
.setScriptParams(scriptParams)
|
||||
.execute().actionGet();
|
||||
|
||||
assertEquals(2, updateResponse.getVersion());
|
||||
|
||||
getResponse = client().prepareGet("test", "type1", "parentId1").execute().actionGet();
|
||||
assertEquals(1, getResponse.getSourceAsMap().get("field1"));
|
||||
assertEquals("bar_bar", getResponse.getSourceAsMap().get("content"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Slow
|
||||
public void testConcurrentUpdateWithRetryOnConflict() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue