package com.netflix.suro.sink.elasticsearch; import com.fasterxml.jackson.annotation.JacksonInject; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.collect.Maps; import com.netflix.suro.message.Message; import com.netflix.suro.sink.DataConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; public class DefaultIndexInfoBuilder implements IndexInfoBuilder { private static final Logger log = LoggerFactory.getLogger(DefaultIndexInfoBuilder.class); private final static TypeReference<Map<String, Object>> type = new TypeReference<Map<String, Object>>() {}; private final ObjectMapper jsonMapper; private final Map<String, String> indexMap; private final Map<String, String> typeMap; private final Map<String, List<String>> idFieldsMap; private final TimestampField timestampField; private final IndexSuffixFormatter indexSuffixFormatter; private final DataConverter dataConverter; @JsonCreator public DefaultIndexInfoBuilder( @JsonProperty("indexTypeMap") Map<String, String> indexTypeMap, @JsonProperty("idFields") Map<String, List<String>> idFieldsMap, @JsonProperty("timestamp") TimestampField timestampField, @JsonProperty("indexSuffixFormatter") IndexSuffixFormatter indexSuffixFormatter, @JacksonInject DataConverter dataConverter, @JacksonInject ObjectMapper jsonMapper ) { if (indexTypeMap != null) { indexMap = Maps.newHashMap(); typeMap = Maps.newHashMap(); for (Map.Entry<String, String> entry : indexTypeMap.entrySet()) { String[] index_type = entry.getValue().split(":"); indexMap.put(entry.getKey(), index_type[0]); if (index_type.length > 1) { typeMap.put(entry.getKey(), index_type[1]); } } } else { this.indexMap = Maps.newHashMap(); this.typeMap = Maps.newHashMap(); } this.idFieldsMap = idFieldsMap; this.indexSuffixFormatter = indexSuffixFormatter == null ? new IndexSuffixFormatter(null, null) : indexSuffixFormatter; this.jsonMapper = jsonMapper; this.timestampField = timestampField; this.dataConverter = dataConverter; } @Override public IndexInfo create(final Message msg) { try { final Map<String, Object> msgMap; if (dataConverter != null) { msgMap = dataConverter.convert((Map<String, Object>) jsonMapper.readValue(msg.getPayload(), type)); } else { msgMap = jsonMapper.readValue(msg.getPayload(), type); } return new IndexInfo() { private long ts = 0; //timestamp caching @Override public String getIndex() { String index = indexMap.get(msg.getRoutingKey()); if (index == null) { index = msg.getRoutingKey(); } return index + indexSuffixFormatter.format(this); } @Override public String getType() { String type = typeMap.get(msg.getRoutingKey()); return type == null ? "default" : type; } @Override public Object getSource() { if (dataConverter != null) { return msgMap; } else { return new String(msg.getPayload()); } } @Override public String getId() { if (idFieldsMap == null || !idFieldsMap.containsKey(msg.getRoutingKey())) { return null; } else { StringBuilder sb = new StringBuilder(); for (String id : idFieldsMap.get(msg.getRoutingKey())) { if (id.startsWith("ts_")) { sb.append(TimestampSlice.valueOf(id).get(getTimestamp())); } else { sb.append(msgMap.get(id)); } } return sb.toString(); } } @Override public long getTimestamp() { if (ts == 0 && timestampField != null) { ts = timestampField.get(msgMap); } return ts; } }; } catch (Exception e) { log.error("Exception on parsing message", e); return null; } } @Override public String getActionMetadata(IndexInfo info) { if (!Strings.isNullOrEmpty(info.getId())) { return String.format( "{ \"create\" : { \"_index\" : \"%s\", \"_type\" : \"%s\", \"_id\" : \"%s\" } }", info.getIndex(), info.getType(), info.getId()); } else { return String.format( "{ \"create\" : { \"_index\" : \"%s\", \"_type\" : \"%s\"} }", info.getIndex(), info.getType()); } } @Override public String getSource(IndexInfo info) throws JsonProcessingException { if (info.getSource() instanceof Map) { return jsonMapper.writeValueAsString(info.getSource()); } else { return info.getSource().toString(); } } @Override public String getIndexUri(IndexInfo info) { return info.getId() != null ? String.format( "/%s/%s/%s", info.getIndex(), info.getType(), info.getId()) : String.format( "/%s/%s/", info.getIndex(), info.getType()); } @Override public String getCommand() { return "create"; } }