/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.epl.expression.accessagg; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.util.CountMinSketchAgent; import com.espertech.esper.client.util.CountMinSketchAgentStringUTF16; import com.espertech.esper.core.service.StatementType; import com.espertech.esper.epl.agg.factory.AggregationStateFactoryCountMinSketch; import com.espertech.esper.epl.agg.service.AggregationMethodFactory; import com.espertech.esper.epl.approx.CountMinSketchAggType; import com.espertech.esper.epl.approx.CountMinSketchSpec; import com.espertech.esper.epl.approx.CountMinSketchSpecHashes; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNode; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeBase; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.table.mgmt.TableMetadataColumnAggregation; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.PopulateFieldValueSetter; import com.espertech.esper.util.PopulateFieldWValueDescriptor; import com.espertech.esper.util.PopulateUtil; import java.util.Collection; import java.util.Map; /** * Represents the Count-min sketch aggregate function. */ public class ExprAggCountMinSketchNode extends ExprAggregateNodeBase implements ExprAggregateAccessMultiValueNode { private static final long serialVersionUID = 202339518989532184L; private static final double DEFAULT_EPS_OF_TOTAL_COUNT = 0.0001; private static final double DEFAULT_CONFIDENCE = 0.99; private static final int DEFAULT_SEED = 1234567; private static final CountMinSketchAgentStringUTF16 DEFAULT_AGENT = new CountMinSketchAgentStringUTF16(); private static final String MSG_NAME = "Count-min-sketch"; private static final String NAME_EPS_OF_TOTAL_COUNT = "epsOfTotalCount"; private static final String NAME_CONFIDENCE = "confidence"; private static final String NAME_SEED = "seed"; private static final String NAME_TOPK = "topk"; private static final String NAME_AGENT = "agent"; private final CountMinSketchAggType aggType; public ExprAggCountMinSketchNode(boolean distinct, CountMinSketchAggType aggType) { super(distinct); this.aggType = aggType; } public AggregationMethodFactory validateAggregationChild(ExprValidationContext validationContext) throws ExprValidationException { return validateAggregationInternal(validationContext, null); } public AggregationMethodFactory validateAggregationParamsWBinding(ExprValidationContext context, TableMetadataColumnAggregation tableAccessColumn) throws ExprValidationException { return validateAggregationInternal(context, tableAccessColumn); } public String getAggregationFunctionName() { return aggType.getFuncName(); } public final boolean equalsNodeAggregateMethodOnly(ExprAggregateNode node) { return false; } public CountMinSketchAggType getAggType() { return aggType; } public EventType getEventTypeCollection(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { return null; } public Collection<EventBean> evaluateGetROCollectionEvents(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return null; } public Class getComponentTypeCollection() throws ExprValidationException { return null; } public Collection evaluateGetROCollectionScalar(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return null; } public EventType getEventTypeSingle(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { return null; } public EventBean evaluateGetEventBean(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return null; } @Override protected boolean isExprTextWildcardWhenNoParams() { return false; } private AggregationMethodFactory validateAggregationInternal(ExprValidationContext context, TableMetadataColumnAggregation tableAccessColumn) throws ExprValidationException { if (isDistinct()) { throw new ExprValidationException(getMessagePrefix() + "is not supported with distinct"); } // for declaration, validate the specification and return the state factory if (aggType == CountMinSketchAggType.STATE) { if (context.getExprEvaluatorContext().getStatementType() != StatementType.CREATE_TABLE) { throw new ExprValidationException(getMessagePrefix() + "can only be used in create-table statements"); } CountMinSketchSpec specification = validateSpecification(context); AggregationStateFactoryCountMinSketch stateFactory = context.getEngineImportService().getAggregationFactoryFactory().makeCountMinSketch(context.getStatementExtensionSvcContext(), this, specification); return new ExprAggCountMinSketchNodeFactoryState(stateFactory); } // validate number of parameters if (aggType == CountMinSketchAggType.ADD || aggType == CountMinSketchAggType.FREQ) { if (positionalParams.length == 0 || positionalParams.length > 1) { throw new ExprValidationException(getMessagePrefix() + "requires a single parameter expression"); } } else { if (positionalParams.length != 0) { throw new ExprValidationException(getMessagePrefix() + "requires a no parameter expressions"); } } // validate into-table and table-access if (aggType == CountMinSketchAggType.ADD) { if (context.getIntoTableName() == null) { throw new ExprValidationException(getMessagePrefix() + "can only be used with into-table"); } } else { if (tableAccessColumn == null) { throw new ExprValidationException(getMessagePrefix() + "requires the use of a table-access expression"); } ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.AGGPARAM, this.getChildNodes(), context); } // obtain evaluator ExprEvaluator addOrFrequencyEvaluator = null; if (aggType == CountMinSketchAggType.ADD || aggType == CountMinSketchAggType.FREQ) { addOrFrequencyEvaluator = getChildNodes()[0].getExprEvaluator(); } return new ExprAggCountMinSketchNodeFactoryUse(this, addOrFrequencyEvaluator); } private CountMinSketchSpec validateSpecification(final ExprValidationContext exprValidationContext) throws ExprValidationException { // default specification final CountMinSketchSpec spec = new CountMinSketchSpec(new CountMinSketchSpecHashes(DEFAULT_EPS_OF_TOTAL_COUNT, DEFAULT_CONFIDENCE, DEFAULT_SEED), null, DEFAULT_AGENT); // no parameters if (this.getChildNodes().length == 0) { return spec; } // check expected parameter type: a json object if (this.getChildNodes().length > 1 || !(this.getChildNodes()[0] instanceof ExprConstantNode)) { throw getDeclaredWrongParameterExpr(); } ExprConstantNode constantNode = (ExprConstantNode) this.getChildNodes()[0]; Object value = constantNode.getConstantValue(exprValidationContext.getExprEvaluatorContext()); if (!(value instanceof Map)) { throw getDeclaredWrongParameterExpr(); } // define what to populate PopulateFieldWValueDescriptor[] descriptors = new PopulateFieldWValueDescriptor[]{ new PopulateFieldWValueDescriptor(NAME_EPS_OF_TOTAL_COUNT, Double.class, spec.getHashesSpec().getClass(), new PopulateFieldValueSetter() { public void set(Object value) { if (value != null) { spec.getHashesSpec().setEpsOfTotalCount((Double) value); } } }, true), new PopulateFieldWValueDescriptor(NAME_CONFIDENCE, Double.class, spec.getHashesSpec().getClass(), new PopulateFieldValueSetter() { public void set(Object value) { if (value != null) { spec.getHashesSpec().setConfidence((Double) value); } } }, true), new PopulateFieldWValueDescriptor(NAME_SEED, Integer.class, spec.getHashesSpec().getClass(), new PopulateFieldValueSetter() { public void set(Object value) { if (value != null) { spec.getHashesSpec().setSeed((Integer) value); } } }, true), new PopulateFieldWValueDescriptor(NAME_TOPK, Integer.class, spec.getClass(), new PopulateFieldValueSetter() { public void set(Object value) { if (value != null) { spec.setTopkSpec((Integer) value); } } }, true), new PopulateFieldWValueDescriptor(NAME_AGENT, String.class, spec.getClass(), new PopulateFieldValueSetter() { public void set(Object value) throws ExprValidationException { if (value != null) { CountMinSketchAgent transform; try { Class transformClass = exprValidationContext.getEngineImportService().resolveClass((String) value, false); transform = (CountMinSketchAgent) JavaClassHelper.instantiate(CountMinSketchAgent.class, transformClass); } catch (Exception e) { throw new ExprValidationException("Failed to instantiate agent provider: " + e.getMessage(), e); } spec.setAgent(transform); } } }, true), }; // populate from json, validates incorrect names, coerces types, instantiates transform PopulateUtil.populateSpecCheckParameters(descriptors, (Map<String, Object>) value, spec, ExprNodeOrigin.AGGPARAM, exprValidationContext); return spec; } public ExprValidationException getDeclaredWrongParameterExpr() throws ExprValidationException { return new ExprValidationException(getMessagePrefix() + " expects either no parameter or a single json parameter object"); } protected boolean isFilterExpressionAsLastParameter() { return false; } private String getMessagePrefix() { return MSG_NAME + " aggregation function '" + aggType.getFuncName() + "' "; } }