package org.rakam.analysis.stream; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.parser.SqlParser; import com.google.common.collect.ImmutableList; import org.apache.avro.generic.GenericRecord; import org.rakam.analysis.metadata.Metastore; import org.rakam.analysis.stream.APIEventStreamModule.CollectionStreamHolder; import org.rakam.analysis.stream.APIEventStreamModule.CollectionStreamHolder.CollectionFilter; import org.rakam.collection.Event; import org.rakam.collection.SchemaField; import org.rakam.plugin.stream.CollectionStreamQuery; import org.rakam.plugin.stream.EventStream; import org.rakam.plugin.stream.StreamResponse; import org.rakam.util.JsonHelper; import javax.inject.Inject; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import static org.rakam.presto.analysis.PrestoRakamRaptorMetastore.toType; public class APIEventStream implements EventStream { private final Map<String, List<CollectionStreamHolder>> holder; private final ExpressionCompiler expressionCompiler; private final Metastore metastore; @Inject public APIEventStream(Map<String, List<CollectionStreamHolder>> holder, Metastore metastore, ExpressionCompiler expressionCompiler) { this.holder = holder; this.expressionCompiler = expressionCompiler; this.metastore = metastore; } @Override public EventStreamer subscribe(String project, List<CollectionStreamQuery> collections, List<String> columns, StreamResponse response) { List<CollectionFilter> collect1; if(collections != null) { collect1 = collections.stream().map(item -> { Predicate<GenericRecord> predicate; if(item.getCollection() == null) { predicate = (val) -> true; } else { List<Map.Entry<String, Type>> collect = metastore.getCollection(project, item.getCollection()) .stream() .map((Function<SchemaField, Map.Entry<String, Type>>) f -> new SimpleImmutableEntry<>(f.getName(), toType(f.getType()))) .collect(Collectors.toList()); predicate = Optional.ofNullable(item.getFilter()) .map(value -> new SqlParser().createExpression(item.getFilter())) .map(expression -> expressionCompiler.generate(expression, collect)) .orElse(null); } return new CollectionFilter(item.getCollection(), predicate); }).collect(Collectors.toList()); } else { collect1 = ImmutableList.of(new CollectionFilter(null, null)); } CollectionStreamHolder streamHolder = new CollectionStreamHolder(collect1); List<CollectionStreamHolder> holders = this.holder.computeIfAbsent(project, s -> new ArrayList<>()); synchronized (this) { holders.add(streamHolder); } return new EventStreamer() { @Override public void sync() { Event message = streamHolder.messageQueue.poll(); StringBuilder builder = new StringBuilder("["); boolean isFirst = true; while (message != null) { builder.append("{\"project\":") .append(JsonHelper.encode(message.project())) .append(", \"collection\":") .append(JsonHelper.encode(message.collection())) .append(", \"properties\": ") .append(message.properties().toString()).append("}"); if (!isFirst) { builder.append(","); } isFirst = false; message = streamHolder.messageQueue.poll(); } builder.append("]"); response.send("data", builder.toString()); } @Override public void shutdown() { synchronized (this) { holder.remove(holder); } } }; } }