/*
* Copyright (C) 2014 Indeed Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.indeed.imhotep.ez;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.indeed.flamdex.query.Query;
import com.indeed.imhotep.marshal.ImhotepClientMarshaller;
import com.indeed.imhotep.protobuf.QueryMessage;
import org.apache.commons.codec.binary.Base64;
import java.util.Arrays;
import java.util.List;
/**
* @author jwolfe
*/
public class Stats {
public static abstract class Stat {
protected abstract List<String> pushes(EZImhotepSession session);
}
public static class IntFieldStat extends Stat {
private final String fieldName;
IntFieldStat(String fieldName) {
this.fieldName = fieldName;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList(fieldName);
}
@Override
public String toString() {
return "int:"+fieldName;
}
public String getFieldName() {
return fieldName;
}
}
public static class DynamicMetricStat extends Stat {
private final DynamicMetric metric;
DynamicMetricStat(DynamicMetric metric) {
requireValid(metric);
this.metric = metric;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
requireValid(metric);
return Lists.newArrayList("dynamic "+metric.name);
}
@Override
public String toString() {
if (metric.valid) {
return "dynamic \""+metric.name+"\"";
} else {
return "invalid dynamic metric stat";
}
}
public DynamicMetricStat of(DynamicMetric metric) {
return new DynamicMetricStat(metric);
}
}
static void requireValid(StatReference ref) {
if (!ref.isValid()) {
throw new IllegalArgumentException("Stat reference is no longer valid!");
}
}
static void requireValid(DynamicMetric metric) {
if (!metric.valid) {
throw new IllegalArgumentException("Dynamic metric "+metric+"is not valid anymore.");
}
}
static class BinOpStat extends Stat {
private final String op;
private final List<Stat> stats;
public BinOpStat(String op, Stat... stats) {
this.op = op;
this.stats = Arrays.asList(stats);
for(Stat stat : stats) {
// TODO proper separation between client side and server side operations
if(stat instanceof AggregateBinOpStat) {
throw new UnsupportedOperationException("Result of aggregate operations like / can't be used as input for further calculations");
}
}
}
@Override
protected List<String> pushes(EZImhotepSession session) {
boolean first = true;
final List<String> ret = Lists.newArrayList();
for (Stat stat : stats) {
ret.addAll(stat.pushes(session));
if (!first) ret.add(op);
first = false;
}
return ret;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('(');
boolean first = true;
for (Stat stat : stats) {
if (!first) {
sb.append(' ').append(op).append(' ');
}
sb.append(stat.toString());
first = false;
}
sb.append(')');
return sb.toString();
}
}
static class AggregateBinOpStat extends Stat {
private final String op;
Stat statLeft;
Stat statRight;
public AggregateBinOpStat(String op, Stat statLeft, Stat statRight) {
this.op = op;
this.statLeft = statLeft;
this.statRight = statRight;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList(Iterables.concat(statLeft.pushes(session), statRight.pushes(session)));
}
@Override
public String toString() {
return "(" + statLeft.toString() + ") " + op + " (" + statRight.toString() + ")";
}
}
static class AggregateBinOpConstStat extends Stat {
private final String op;
private final long value;
Stat statLeft;
public AggregateBinOpConstStat(String op, Stat statLeft, long value) {
this.op = op;
this.statLeft = statLeft;
this.value = value;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return statLeft.pushes(session);
}
@Override
public String toString() {
return "(" + statLeft.toString() + ") " + op + " " + value;
}
public long getValue() {
return value;
}
public String getOp() {
return op;
}
}
static class ConstantStat extends Stat {
private final long value;
public ConstantStat(long value) {
this.value = value;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList(Long.toString(value));
}
@Override
public String toString() {
return Long.toString(value);
}
public long getValue() {
return value;
}
}
static class ExpStat extends Stat {
private final Stat stat;
private final int scaleFactor;
public ExpStat(Stat stat, int scaleFactor) {
this.stat = stat;
this.scaleFactor = scaleFactor;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
List<String> prev = Lists.newArrayList(stat.pushes(session));
prev.add("exp " + scaleFactor);
return prev;
}
@Override
public String toString() {
return "exp("+stat.toString()+", " + scaleFactor + ")";
}
}
static class HasIntStat extends Stat {
private final String field;
private final long value;
public HasIntStat(String field, long value) {
this.field = field;
this.value = value;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList("hasint " + field + ":" + Long.toString(value));
}
@Override
public String toString() {
return "hasint:"+ field + ":" + Long.toString(value);
}
}
static class HasStringStat extends Stat {
private final String field;
private final String value;
public HasStringStat(String field, String value) {
this.field = field;
this.value = value;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList("hasstr " + field + ":" + value);
}
@Override
public String toString() {
return "hasstr:" + field + ":" + value;
}
}
static class LuceneQueryStat extends Stat {
private final Query luceneQuery;
public LuceneQueryStat(Query luceneQuery) {
this.luceneQuery = luceneQuery;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
final QueryMessage luceneQueryMessage = ImhotepClientMarshaller.marshal(luceneQuery);
final String base64EncodedQuery = Base64.encodeBase64String(luceneQueryMessage.toByteArray());
return Lists.newArrayList("lucene " + base64EncodedQuery);
}
@Override
public String toString() {
return "lucene(" + luceneQuery + ")";
}
}
static class StatRefStat extends Stat {
private final SingleStatReference ref;
StatRefStat(SingleStatReference ref) {
this.ref = ref;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
requireValid(ref);
return Lists.newArrayList("ref "+(session.getStackDepth()-ref.depth-1));
}
@Override
public String toString() {
return "ref:"+ref.toString();
}
}
static class CountStat extends Stat {
CountStat() {
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList("count()");
}
@Override
public String toString() {
return "count()";
}
}
public static class CachedStat extends Stat {
private final Stat stat;
CachedStat(Stat stat) {
this.stat = stat;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
List<String> ret = Lists.newArrayList(stat.pushes(session));
ret.add("cached()");
return ret;
}
}
public static class AbsoluteValueStat extends Stat {
private final Stat stat;
AbsoluteValueStat(Stat stat) {
this.stat = stat;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
List<String> ret = Lists.newArrayList(stat.pushes(session));
ret.add("abs()");
return ret;
}
}
public static class FloatScaleStat extends Stat {
private final String fieldName;
private final int mult;
private final int add;
FloatScaleStat(String fieldName, int mult, int add) {
this.fieldName = fieldName;
this.mult = mult;
this.add = add;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
return Lists.newArrayList("floatscale "+fieldName+" * "+mult+" + "+add);
}
}
public static class MultiplyShiftRight extends Stat {
private final int shift;
private final Stat stat1;
private final Stat stat2;
MultiplyShiftRight(int shift, Stat stat1, Stat stat2) {
this.shift = shift;
this.stat1 = stat1;
this.stat2 = stat2;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
List<String> ret = Lists.newArrayList(stat1.pushes(session));
ret.addAll(stat2.pushes(session));
ret.add("mulshr " + shift);
return ret;
}
}
public static class ShiftLeftDivide extends Stat {
private final int shift;
private final Stat stat1;
private final Stat stat2;
ShiftLeftDivide(int shift, Stat stat1, Stat stat2) {
this.shift = shift;
this.stat1 = stat1;
this.stat2 = stat2;
}
@Override
protected List<String> pushes(EZImhotepSession session) {
List<String> ret = Lists.newArrayList(stat1.pushes(session));
ret.addAll(stat2.pushes(session));
ret.add("shldiv " + shift);
return ret;
}
}
}