protected void shardOperation( final UpdateRequest request, final ActionListener<UpdateResponse> listener, final int retryCount) throws ElasticSearchException { IndexService indexService = indicesService.indexServiceSafe(request.index()); IndexShard indexShard = indexService.shardSafe(request.shardId()); long getDate = System.currentTimeMillis(); final GetResult getResult = indexShard .getService() .get( request.type(), request.id(), new String[] { SourceFieldMapper.NAME, RoutingFieldMapper.NAME, ParentFieldMapper.NAME, TTLFieldMapper.NAME }, true); // no doc, what to do, what to do... if (!getResult.isExists()) { if (request.upsertRequest() == null) { listener.onFailure( new DocumentMissingException( new ShardId(request.index(), request.shardId()), request.type(), request.id())); return; } final IndexRequest indexRequest = request.upsertRequest(); indexRequest .index(request.index()) .type(request.type()) .id(request.id()) // it has to be a "create!" .create(true) .routing(request.routing()) .percolate(request.percolate()) .refresh(request.refresh()) .replicationType(request.replicationType()) .consistencyLevel(request.consistencyLevel()); indexRequest.operationThreaded(false); // we fetch it from the index request so we don't generate the bytes twice, its already done // in the index request final BytesReference updateSourceBytes = indexRequest.source(); indexAction.execute( indexRequest, new ActionListener<IndexResponse>() { @Override public void onResponse(IndexResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion()); update.setMatches(response.getMatches()); if (request.fields() != null && request.fields().length > 0) { Tuple<XContentType, Map<String, Object>> sourceAndContent = XContentHelper.convertToMap(updateSourceBytes, true); update.setGetResult( extractGetResult( request, response.getVersion(), sourceAndContent.v2(), sourceAndContent.v1(), updateSourceBytes)); } else { update.setGetResult(null); } listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException || e instanceof DocumentAlreadyExistsException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new Runnable() { @Override public void run() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); return; } if (getResult.internalSourceRef() == null) { // no source, we can't do nothing, through a failure... listener.onFailure( new DocumentSourceMissingException( new ShardId(request.index(), request.shardId()), request.type(), request.id())); return; } Tuple<XContentType, Map<String, Object>> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true); String operation = null; String timestamp = null; Long ttl = null; Object fetchedTTL = null; final Map<String, Object> updatedSourceAsMap; final XContentType updateSourceContentType = sourceAndContent.v1(); String routing = getResult.getFields().containsKey(RoutingFieldMapper.NAME) ? getResult.field(RoutingFieldMapper.NAME).getValue().toString() : null; String parent = getResult.getFields().containsKey(ParentFieldMapper.NAME) ? getResult.field(ParentFieldMapper.NAME).getValue().toString() : null; if (request.script() == null && request.doc() != null) { IndexRequest indexRequest = request.doc(); updatedSourceAsMap = sourceAndContent.v2(); if (indexRequest.ttl() > 0) { ttl = indexRequest.ttl(); } timestamp = indexRequest.timestamp(); if (indexRequest.routing() != null) { routing = indexRequest.routing(); } if (indexRequest.parent() != null) { parent = indexRequest.parent(); } XContentHelper.update(updatedSourceAsMap, indexRequest.sourceAsMap()); } else { Map<String, Object> ctx = new HashMap<String, Object>(2); ctx.put("_source", sourceAndContent.v2()); try { ExecutableScript script = scriptService.executable(request.scriptLang, request.script, request.scriptParams); script.setNextVar("ctx", ctx); script.run(); // we need to unwrap the ctx... ctx = (Map<String, Object>) script.unwrap(ctx); } catch (Exception e) { throw new ElasticSearchIllegalArgumentException("failed to execute script", e); } operation = (String) ctx.get("op"); timestamp = (String) ctx.get("_timestamp"); fetchedTTL = ctx.get("_ttl"); if (fetchedTTL != null) { if (fetchedTTL instanceof Number) { ttl = ((Number) fetchedTTL).longValue(); } else { ttl = TimeValue.parseTimeValue((String) fetchedTTL, null).millis(); } } updatedSourceAsMap = (Map<String, Object>) ctx.get("_source"); } // apply script to update the source // No TTL has been given in the update script so we keep previous TTL value if there is one if (ttl == null) { ttl = getResult.getFields().containsKey(TTLFieldMapper.NAME) ? (Long) getResult.field(TTLFieldMapper.NAME).getValue() : null; if (ttl != null) { ttl = ttl - (System.currentTimeMillis() - getDate); // It is an approximation of exact TTL value, could be improved } } // TODO: external version type, does it make sense here? does not seem like it... if (operation == null || "index".equals(operation)) { final IndexRequest indexRequest = Requests.indexRequest(request.index()) .type(request.type()) .id(request.id()) .routing(routing) .parent(parent) .source(updatedSourceAsMap, updateSourceContentType) .version(getResult.getVersion()) .replicationType(request.replicationType()) .consistencyLevel(request.consistencyLevel()) .timestamp(timestamp) .ttl(ttl) .percolate(request.percolate()) .refresh(request.refresh()); indexRequest.operationThreaded(false); // we fetch it from the index request so we don't generate the bytes twice, its already done // in the index request final BytesReference updateSourceBytes = indexRequest.source(); indexAction.execute( indexRequest, new ActionListener<IndexResponse>() { @Override public void onResponse(IndexResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion()); update.setMatches(response.getMatches()); update.setGetResult( extractGetResult( request, response.getVersion(), updatedSourceAsMap, updateSourceContentType, updateSourceBytes)); listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new Runnable() { @Override public void run() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); } else if ("delete".equals(operation)) { DeleteRequest deleteRequest = Requests.deleteRequest(request.index()) .type(request.type()) .id(request.id()) .routing(routing) .parent(parent) .version(getResult.getVersion()) .replicationType(request.replicationType()) .consistencyLevel(request.consistencyLevel()); deleteRequest.operationThreaded(false); deleteAction.execute( deleteRequest, new ActionListener<DeleteResponse>() { @Override public void onResponse(DeleteResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion()); update.setGetResult( extractGetResult( request, response.getVersion(), updatedSourceAsMap, updateSourceContentType, null)); listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new Runnable() { @Override public void run() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); } else if ("none".equals(operation)) { UpdateResponse update = new UpdateResponse( getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion()); update.setGetResult( extractGetResult( request, getResult.getVersion(), updatedSourceAsMap, updateSourceContentType, null)); listener.onResponse(update); } else { logger.warn( "Used update operation [{}] for script [{}], doing nothing...", operation, request.script); listener.onResponse( new UpdateResponse( getResult.getIndex(), getResult.getType(), getResult.getId(), getResult.getVersion())); } }
protected void shardOperation( final UpdateRequest request, final ActionListener<UpdateResponse> listener, final int retryCount) throws ElasticsearchException { final UpdateHelper.Result result = updateHelper.prepare(request); switch (result.operation()) { case UPSERT: IndexRequest upsertRequest = result.action(); // we fetch it from the index request so we don't generate the bytes twice, its already done // in the index request final BytesReference upsertSourceBytes = upsertRequest.source(); indexAction.execute( upsertRequest, new ActionListener<IndexResponse>() { @Override public void onResponse(IndexResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion(), response.isCreated()); if (request.fields() != null && request.fields().length > 0) { Tuple<XContentType, Map<String, Object>> sourceAndContent = XContentHelper.convertToMap(upsertSourceBytes, true); update.setGetResult( updateHelper.extractGetResult( request, response.getVersion(), sourceAndContent.v2(), sourceAndContent.v1(), upsertSourceBytes)); } else { update.setGetResult(null); } listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException || e instanceof DocumentAlreadyExistsException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new ActionRunnable<UpdateResponse>(listener) { @Override protected void doRun() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); break; case INDEX: IndexRequest indexRequest = result.action(); // we fetch it from the index request so we don't generate the bytes twice, its already done // in the index request final BytesReference indexSourceBytes = indexRequest.source(); indexAction.execute( indexRequest, new ActionListener<IndexResponse>() { @Override public void onResponse(IndexResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion(), response.isCreated()); update.setGetResult( updateHelper.extractGetResult( request, response.getVersion(), result.updatedSourceAsMap(), result.updateSourceContentType(), indexSourceBytes)); listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new ActionRunnable<UpdateResponse>(listener) { @Override protected void doRun() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); break; case DELETE: DeleteRequest deleteRequest = result.action(); deleteAction.execute( deleteRequest, new ActionListener<DeleteResponse>() { @Override public void onResponse(DeleteResponse response) { UpdateResponse update = new UpdateResponse( response.getIndex(), response.getType(), response.getId(), response.getVersion(), false); update.setGetResult( updateHelper.extractGetResult( request, response.getVersion(), result.updatedSourceAsMap(), result.updateSourceContentType(), null)); listener.onResponse(update); } @Override public void onFailure(Throwable e) { e = ExceptionsHelper.unwrapCause(e); if (e instanceof VersionConflictEngineException) { if (retryCount < request.retryOnConflict()) { threadPool .executor(executor()) .execute( new ActionRunnable<UpdateResponse>(listener) { @Override protected void doRun() { shardOperation(request, listener, retryCount + 1); } }); return; } } listener.onFailure(e); } }); break; case NONE: UpdateResponse update = result.action(); listener.onResponse(update); indicesService .indexService(request.index()) .shard(request.shardId()) .indexingService() .noopUpdate(request.type()); break; default: throw new ElasticsearchIllegalStateException("Illegal operation " + result.operation()); } }