package com.github.obourgain.elasticsearch.http.handler.document.bulk; import java.io.IOException; import java.util.List; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.WriteConsistencyLevel; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.VersionType; import com.github.obourgain.elasticsearch.http.handler.document.update.UpdateHelper; import com.google.common.base.Charsets; import rx.Observable; import rx.functions.Func1; public class BulkActionMarshaller { public static final byte[] LINE_BREAK = "\n".getBytes(Charsets.US_ASCII); public static Observable<byte[]> lazyConvertToBytes(List<ActionRequest> actions) { return Observable.from(actions) .flatMap(new Func1<ActionRequest, Observable<byte[]>>() { @Override public Observable<byte[]> call(ActionRequest actionRequest) { try { if (actionRequest instanceof IndexRequest) { IndexRequest indexRequest = (IndexRequest) actionRequest; return Observable.just(buildIndexCommand(indexRequest), LINE_BREAK, indexRequest.source().toBytes(), LINE_BREAK); } else if (actionRequest instanceof DeleteRequest) { DeleteRequest deleteRequest = (DeleteRequest) actionRequest; return Observable.just(buildDeleteCommand(deleteRequest), LINE_BREAK); } else if (actionRequest instanceof UpdateRequest) { UpdateRequest updateRequest = (UpdateRequest) actionRequest; return Observable.just(buildUpdateCommand(updateRequest), LINE_BREAK, UpdateHelper.buildRequestBody(updateRequest), LINE_BREAK); } else { throw new IllegalArgumentException("action type " + actionRequest.getClass().getName() + " not supported"); } } catch (IOException e) { throw new RuntimeException(e); } } }); } private static byte[] buildIndexCommand(IndexRequest indexRequest) throws IOException { try (XContentBuilder builder = XContentFactory.jsonBuilder().startObject()) { addCommonOptions(builder, "index", indexRequest.index(), indexRequest.type(), indexRequest.id(), indexRequest.version(), indexRequest.versionType(), indexRequest.routing(), indexRequest.consistencyLevel(), indexRequest.refresh()); String parent = indexRequest.parent(); String timestamp = indexRequest.timestamp(); long ttl = indexRequest.ttl(); if (parent != null) { builder.field("_parent", parent); } if (timestamp != null) { builder.field("_timestamp", timestamp); } if (ttl != -1) { builder.field("_ttl", ttl); } if (indexRequest.opType() != null) { builder.field("op_type", indexRequest.opType().toString().toLowerCase()); } if (indexRequest.parent() != null) { builder.field("op_type", indexRequest.parent()); } return builder.bytes().toBytes(); } } private static byte[] buildDeleteCommand(DeleteRequest deleteRequest) throws IOException { try(XContentBuilder builder = XContentFactory.jsonBuilder().startObject()) { addCommonOptions(builder, "delete", deleteRequest.index(), deleteRequest.type(), deleteRequest.id(), deleteRequest.version(), deleteRequest.versionType(), deleteRequest.routing(), deleteRequest.consistencyLevel(), deleteRequest.refresh()); return builder.bytes().toBytes(); } } private static byte[] buildUpdateCommand(UpdateRequest updateRequest) throws IOException { try (XContentBuilder builder = XContentFactory.jsonBuilder().startObject()) { addCommonOptions(builder, "update", updateRequest.index(), updateRequest.type(), updateRequest.id(), updateRequest.version(), updateRequest.versionType(), updateRequest.routing(), updateRequest.consistencyLevel(), updateRequest.refresh()); String timestamp = updateRequest.doc() != null ? updateRequest.doc().timestamp() : null; long ttl = updateRequest.doc() != null ? updateRequest.doc().ttl() : -1; if (timestamp != null) { builder.field("_timestamp", timestamp); } if (ttl != -1) { builder.field("_ttl", ttl); } if (updateRequest.retryOnConflict() != -1) { builder.field("_retry_on_conflict", updateRequest.retryOnConflict()); } if (updateRequest.fields() != null && updateRequest.fields().length > 0) { builder.field("_fields", Strings.arrayToCommaDelimitedString(updateRequest.fields())); } if (updateRequest.doc() != null && updateRequest.doc().parent() != null) { builder.field("_parent", updateRequest.doc().parent()); } else if (updateRequest.routing() != null) { builder.field("_parent", updateRequest.routing()); } return builder.bytes().toBytes(); } } private static void addCommonOptions(XContentBuilder builder, String command, String index, String type, String id, @Nullable long version, VersionType versionType, // may be Versions.MATCH_ANY and null @Nullable String routing, @Nullable WriteConsistencyLevel consistencyLevel, boolean refresh ) throws IOException { builder.startObject(command) .field("_index", index) .field("_type", type) .field("_id", id); if (version != Versions.MATCH_ANY) { builder.field("_version", version); builder.field("_version_type", versionType.toString().toLowerCase()); } if (routing != null) { builder.field("_routing", routing); } if (consistencyLevel != WriteConsistencyLevel.DEFAULT) { builder.field("_consistency", consistencyLevel.toString().toLowerCase()); } if (refresh) { builder.field("_refresh", true); } } }