protected IndexResponse indexItem( DocTableInfo tableInfo, ShardUpsertRequest request, ShardUpsertRequest.Item item, ShardId shardId, boolean tryInsertFirst, int retryCount) throws ElasticsearchException { try { IndexRequest indexRequest; if (tryInsertFirst) { // try insert first without fetching the document try { indexRequest = new IndexRequest(prepareInsert(tableInfo, request, item), request); } catch (IOException e) { throw ExceptionsHelper.convertToElastic(e); } } else { indexRequest = new IndexRequest(prepareUpdate(tableInfo, request, item, shardId), request); } return indexAction.execute(indexRequest).actionGet(); } catch (Throwable t) { if (t instanceof VersionConflictEngineException && retryCount < item.retryOnConflict()) { return indexItem(tableInfo, request, item, shardId, false, retryCount + 1); } else if (tryInsertFirst && item.updateAssignments() != null && t instanceof DocumentAlreadyExistsException) { // insert failed, document already exists, try update return indexItem(tableInfo, request, item, shardId, false, 0); } else { throw t; } } }
/** * Prepares an update request by converting it into an index request. * * <p>TODO: detect a NOOP and return an update response if true */ @SuppressWarnings("unchecked") public IndexRequest prepareUpdate( DocTableInfo tableInfo, ShardUpsertRequest request, ShardUpsertRequest.Item item, ShardId shardId) throws ElasticsearchException { IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); IndexShard indexShard = indexService.shardSafe(shardId.id()); final GetResult getResult = indexShard .getService() .get( request.type(), item.id(), new String[] {RoutingFieldMapper.NAME, ParentFieldMapper.NAME, TTLFieldMapper.NAME}, true, item.version(), VersionType.INTERNAL, FetchSourceContext.FETCH_SOURCE, false); if (!getResult.isExists()) { throw new DocumentMissingException( new ShardId(request.index(), request.shardId()), request.type(), item.id()); } if (getResult.internalSourceRef() == null) { // no source, we can't do nothing, through a failure... throw new DocumentSourceMissingException( new ShardId(request.index(), request.shardId()), request.type(), item.id()); } Tuple<XContentType, Map<String, Object>> sourceAndContent = XContentHelper.convertToMap(getResult.internalSourceRef(), true); 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; updatedSourceAsMap = sourceAndContent.v2(); SymbolToFieldExtractorContext ctx = new SymbolToFieldExtractorContext(functions, item.insertValues()); Map<String, Object> pathsToUpdate = new LinkedHashMap<>(); Map<String, Object> updatedGeneratedColumns = new LinkedHashMap<>(); for (int i = 0; i < request.updateColumns().length; i++) { /** * NOTE: mapping isn't applied. So if an Insert was done using the ES Rest Endpoint the data * might be returned in the wrong format (date as string instead of long) */ String columnPath = request.updateColumns()[i]; Object value = SYMBOL_TO_FIELD_EXTRACTOR.convert(item.updateAssignments()[i], ctx).extract(getResult); ReferenceInfo referenceInfo = tableInfo.getReferenceInfo(ColumnIdent.fromPath(columnPath)); if (referenceInfo instanceof GeneratedReferenceInfo) { updatedGeneratedColumns.put(columnPath, value); } else { pathsToUpdate.put(columnPath, value); } } processGeneratedColumns( tableInfo, pathsToUpdate, updatedGeneratedColumns, request.validateGeneratedColumns(), getResult); updateSourceByPaths(updatedSourceAsMap, pathsToUpdate); final IndexRequest indexRequest = Requests.indexRequest(request.index()) .type(request.type()) .id(item.id()) .routing(routing) .parent(parent) .source(updatedSourceAsMap, updateSourceContentType) .version(getResult.getVersion()); indexRequest.operationThreaded(false); return indexRequest; }