package org.rakam.postgresql.plugin.user;
import com.facebook.presto.sql.ExpressionFormatter;
import com.facebook.presto.sql.tree.Expression;
import com.google.common.collect.ImmutableMap;
import org.rakam.analysis.ConfigManager;
import org.rakam.analysis.MaterializedViewService;
import org.rakam.plugin.MaterializedView;
import org.rakam.plugin.user.UserPropertyMapper;
import org.rakam.postgresql.report.PostgresqlQueryExecutor;
import org.rakam.report.QueryExecutor;
import org.rakam.report.QueryExecutorService;
import javax.inject.Inject;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static java.lang.String.format;
import static org.rakam.report.realtime.AggregationType.COUNT;
import static org.rakam.util.ValidationUtil.checkCollection;
public class PostgresqlUserStorage
extends AbstractPostgresqlUserStorage
{
public static final String USER_TABLE = "_users";
private final MaterializedViewService materializedViewService;
private final QueryExecutorService queryExecutorService;
@Inject
public PostgresqlUserStorage(
QueryExecutorService queryExecutorService,
MaterializedViewService materializedViewService,
ConfigManager configManager,
PostgresqlQueryExecutor queryExecutor)
{
super(queryExecutorService, queryExecutor, configManager);
this.queryExecutorService = queryExecutorService;
this.materializedViewService = materializedViewService;
}
@Override
public QueryExecutorService getExecutorForWithEventFilter()
{
return queryExecutorService;
}
@Override
public List<String> getEventFilterPredicate(String project, List<EventFilter> eventFilter)
{
List<String> filters = new ArrayList<>(2);
for (EventFilter filter : eventFilter) {
StringBuilder builder = new StringBuilder();
String collection = checkCollection(filter.collection);
if (filter.aggregation == null) {
builder.append(format("select \"_user\" from %s", collection));
if (filter.filterExpression != null) {
builder.append(" where ").append(new ExpressionFormatter.Formatter(Optional.empty()).process(filter.getExpression(), true));
}
// TODO: timeframe
filters.add((format("id in (%s)", builder.toString())));
}
else {
builder.append(format("select \"_user\" from %s", collection));
if (filter.filterExpression != null) {
builder.append(" where ").append(new ExpressionFormatter.Formatter(Optional.empty()).process(filter.getExpression(), true));
}
String field;
if (filter.aggregation.type == COUNT && filter.aggregation.field == null) {
field = "_user";
}
else {
field = filter.aggregation.field;
}
builder.append(" group by \"_user\" ");
if (filter.aggregation.minimum != null || filter.aggregation.maximum != null) {
builder.append(" having ");
}
if (filter.aggregation.minimum != null) {
builder.append(format(" %s(\"%s\") >= %d ", filter.aggregation.type, field, filter.aggregation.minimum));
}
if (filter.aggregation.maximum != null) {
if (filter.aggregation.minimum != null) {
builder.append(" and ");
}
builder.append(format(" %s(\"%s\") < %d ", filter.aggregation.type, field, filter.aggregation.maximum));
}
filters.add((format("id in (%s)", builder.toString())));
}
}
return filters;
}
@Override
public String getUserTable(String project, boolean isEventFilterActive)
{
return project + "." + USER_TABLE;
}
@Override
public void createSegment(String project, String name, String tableName, Expression filterExpression, List<EventFilter> eventFilter, Duration interval)
{
StringBuilder builder = new StringBuilder("select distinct id from _users where ");
if (filterExpression != null) {
builder.append(filterExpression.toString());
}
if (eventFilter != null && !eventFilter.isEmpty()) {
if(filterExpression != null) {
builder.append(" AND ");
}
builder.append(getEventFilterPredicate(project, eventFilter).stream().collect(Collectors.joining(" AND ")));
}
materializedViewService.create(project, new MaterializedView(tableName,
"Users who did " + (tableName == null ? "at least one event" : tableName + " event"),
builder.toString(), interval, null, null, ImmutableMap.of()));
}
}