package org.rakam;
import org.apache.avro.generic.GenericRecord;
import org.rakam.collection.Event;
import org.rakam.collection.FieldType;
import org.rakam.collection.SchemaField;
import org.rakam.plugin.EventStore;
import org.rakam.plugin.SyncEventStore;
import org.rakam.presto.analysis.PrestoConfig;
import org.rakam.presto.analysis.PrestoQueryExecutor;
import org.rakam.report.QueryResult;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.rakam.presto.analysis.PrestoQueryExecution.PRESTO_TIMESTAMP_FORMAT;
public class TestingPrestoEventStore implements SyncEventStore
{
private final PrestoQueryExecutor queryExecutor;
private final PrestoConfig config;
public TestingPrestoEventStore(PrestoQueryExecutor queryExecutor, PrestoConfig config) {
this.queryExecutor = queryExecutor;
this.config = config;
}
@Override
public void store(Event event) {
queryExecutor.executeRawStatement(String.format("INSERT INTO %s.%s.%s VALUES %s",
config.getColdStorageConnector(), event.project(),
event.collection(), buildValues(event.properties(), event.schema())));
}
@Override
public int[] storeBatch(List<Event> events) {
for (Map.Entry<String, List<Event>> collection : events.stream().collect(Collectors.groupingBy(e -> e.collection())).entrySet()) {
QueryResult join = queryExecutor.executeRawStatement(String.format("INSERT INTO %s.%s.%s (%s)",
config.getColdStorageConnector(), events.get(0).project(),
collection.getKey(),
collection.getValue().stream().map(e -> buildValues(e.properties(), e.schema())).collect(Collectors.joining(" union all "))))
.getResult().join();
if(join.isFailed()) {
throw new IllegalStateException(join.getError().message);
}
}
return EventStore.SUCCESSFUL_BATCH;
}
private String buildValues(GenericRecord properties, List<SchemaField> schema) {
StringBuilder builder = new StringBuilder("select ");
int size = properties.getSchema().getFields().size();
if (size > 0) {
appendValue(builder, properties.get(0), schema.get(0).getType());
for (int i = 1; i < size; i++) {
builder.append(", ");
appendValue(builder, properties.get(i), schema.get(i).getType());
}
}
return builder.toString();
}
private void appendValue(StringBuilder builder, Object value, FieldType type) {
switch (type) {
case STRING:
builder.append('\'').append(value).append('\'');
break;
case LONG:
builder.append(((Number) value).longValue());
case INTEGER:
builder.append(((Number) value).intValue());
case DECIMAL:
builder.append(((Number) value).doubleValue());
break;
case DOUBLE:
builder.append(String.format("%.2f", ((Number) value).doubleValue()));
break;
case BOOLEAN:
builder.append(value);
break;
case TIMESTAMP:
builder.append("timestamp '").append(PRESTO_TIMESTAMP_FORMAT.format(Instant.ofEpochMilli((Long) value).atZone(ZoneId.systemDefault()))).append('\'');
break;
case DATE:
builder.append("date '").append(LocalDate.ofEpochDay((int) value)).append('\'');
break;
default:
if (type.isArray()) {
builder.append("ARRAY [");
for (Object item : ((Collection) value)) {
appendValue(builder, item, type.getArrayElementType());
}
builder.append(']');
} else if (type.isMap()) {
builder.append("MAP(");
appendValue(builder, ((Map) value).keySet(), FieldType.ARRAY_STRING);
builder.append(", ");
appendValue(builder, ((Map) value).values(), type.getMapValueType().convertToArrayType());
builder.append(')');
}
}
}
}