package pl.allegro.tech.search.elasticsearch.tools.reindex.process;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.base.Preconditions;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.search.SearchHit;
import pl.allegro.tech.search.elasticsearch.tools.reindex.connection.ElasticDataPointer;
public class IndexingComponent {
private final Client client;
public IndexingComponent(Client client) {
this.client = client;
}
private BulkRequestBuilder createBulkRequestBuilder() {
return client.prepareBulk();
}
public Optional<BulkResult> indexData(ElasticDataPointer targetDataPointer, SearchHit[] hits) {
BulkRequestBuilder bulkRequest = createBulkRequestBuilder();
for (SearchHit hit : hits) {
Map<String, Object> source = hit.getSource();
IndexRequestBuilder requestBuilder = prepareIndex(targetDataPointer.getIndexName(), targetDataPointer
.getTypeName(), hit.getId(), source, hit.getIndex());
if (hit.getFields().get("_ttl") != null) {
requestBuilder.setTTL(hit.getFields().get("_ttl").value());
}
if (hit.getFields().get("_routing") != null) {
requestBuilder.setRouting(hit.getFields().get("_routing").value());
}
requestBuilder.setSource(source);
bulkRequest.add(requestBuilder);
}
return executeBulk(hits.length, bulkRequest);
}
private Optional<BulkResult> executeBulk(int indexedCount, BulkRequestBuilder bulkRequest) {
if (bulkRequest.numberOfActions() > 0) {
BulkResponse bulkItemResponses = bulkRequest.execute().actionGet();
Set<String> failedIds = Stream.of(bulkItemResponses.getItems())
.filter(BulkItemResponse::isFailed)
.map(BulkItemResponse::getId)
.collect(Collectors.toSet());
return Optional.of(new BulkResult(indexedCount, failedIds));
}
return Optional.empty();
}
private static final Pattern INDEX_NAME_REPLACEMENT_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}");
private IndexRequestBuilder prepareIndex(String indexName, String typeName, String id, Map<String, Object> sourceFields, String sourceIndex) {
String newIndexName = computeIndexName(indexName, sourceFields, sourceIndex);
return client.prepareIndex(newIndexName, typeName, id);
}
protected static String computeIndexName(String indexName, Map<String, Object> sourceFields, String sourceIndex) {
StringBuffer sb = new StringBuffer();
Matcher matcher = INDEX_NAME_REPLACEMENT_PATTERN.matcher(indexName);
while(matcher.find()) {
String fieldName = matcher.group(1);
String format = null;
int pos = fieldName.indexOf(':');
if(pos != -1) {
format = fieldName.substring(pos + 1);
fieldName = fieldName.substring(0, pos);
}
final String replacement;
if(fieldName.equals("_index")) {
replacement = sourceIndex;
} else {
Object obj = sourceFields.get(fieldName);
Preconditions.checkNotNull(obj, "Specified source field " + fieldName + " not found for index-name replacement");
String field = obj.toString();
if(format != null) {
// only support time based on milliseconds since the epoch for now
SimpleDateFormat formatter = new SimpleDateFormat(format);
replacement = formatter.format(new Date(Long.parseLong(field)));
} else {
replacement = field;
}
}
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
return sb.toString();
}
}