/*
***************************************************************************************
* 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.core;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.core.context.util.AgentInstanceContext;
import com.espertech.esper.epl.agg.rollup.GroupByRollupKey;
import com.espertech.esper.epl.agg.service.AggregationService;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprEvaluatorContext;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import java.util.*;
/**
* An order-by processor that sorts events according to the expressions
* in the order_by clause.
*/
public class OrderByProcessorImpl implements OrderByProcessor {
private final OrderByProcessorFactoryImpl factory;
private final AggregationService aggregationService;
public OrderByProcessorImpl(OrderByProcessorFactoryImpl factory, AggregationService aggregationService) {
this.factory = factory;
this.aggregationService = aggregationService;
}
public Object getSortKey(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
return getSortKeyInternal(eventsPerStream, isNewData, exprEvaluatorContext, factory.getOrderBy());
}
public Object getSortKey(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elementsForLevel) {
return getSortKeyInternal(eventsPerStream, isNewData, exprEvaluatorContext, elementsForLevel);
}
private static Object getSortKeyInternal(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext, OrderByElement[] elements) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(eventsPerStream, elements);
}
if (elements.length == 1) {
if (InstrumentationHelper.ENABLED) {
Object value = elements[0].getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
InstrumentationHelper.get().aOrderBy(value);
return value;
}
return elements[0].getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
}
Object[] values = new Object[elements.length];
int count = 0;
for (OrderByElement sortPair : elements) {
values[count++] = sortPair.getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(values);
}
return new MultiKeyUntyped(values);
}
public Object[] getSortKeyPerRow(EventBean[] generatingEvents, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
if (generatingEvents == null) {
return null;
}
Object[] sortProperties = new Object[generatingEvents.length];
int count = 0;
EventBean[] evalEventsPerStream = new EventBean[1];
if (factory.getOrderBy().length == 1) {
ExprEvaluator singleEval = factory.getOrderBy()[0].getExpr();
for (EventBean theEvent : generatingEvents) {
evalEventsPerStream[0] = theEvent;
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(evalEventsPerStream, factory.getOrderBy());
}
sortProperties[count] = singleEval.evaluate(evalEventsPerStream, isNewData, exprEvaluatorContext);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(sortProperties[count]);
}
count++;
}
} else {
for (EventBean theEvent : generatingEvents) {
Object[] values = new Object[factory.getOrderBy().length];
int countTwo = 0;
evalEventsPerStream[0] = theEvent;
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(evalEventsPerStream, factory.getOrderBy());
}
for (OrderByElement sortPair : factory.getOrderBy()) {
values[countTwo++] = sortPair.getExpr().evaluate(evalEventsPerStream, isNewData, exprEvaluatorContext);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(values);
}
sortProperties[count] = new MultiKeyUntyped(values);
count++;
}
}
return sortProperties;
}
public EventBean[] sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
if (outgoingEvents == null || outgoingEvents.length < 2) {
return outgoingEvents;
}
// Get the group by keys if needed
Object[] groupByKeys = null;
if (factory.isNeedsGroupByKeys()) {
groupByKeys = generateGroupKeys(generatingEvents, isNewData, exprEvaluatorContext);
}
return sort(outgoingEvents, generatingEvents, groupByKeys, isNewData, exprEvaluatorContext);
}
public EventBean[] sort(EventBean[] outgoingEvents, List<GroupByRollupKey> currentGenerators, boolean isNewData, AgentInstanceContext exprEvaluatorContext, OrderByElement[][] elementsPerLevel) {
List<Object> sortValuesMultiKeys = createSortPropertiesWRollup(currentGenerators, elementsPerLevel, isNewData, exprEvaluatorContext);
return sortInternal(outgoingEvents, sortValuesMultiKeys, factory.getComparator());
}
public EventBean[] sort(EventBean[] outgoingEvents, EventBean[][] generatingEvents, Object[] groupByKeys, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
if (outgoingEvents == null || outgoingEvents.length < 2) {
return outgoingEvents;
}
// Create the multikeys of sort values
List<Object> sortValuesMultiKeys = createSortProperties(generatingEvents, groupByKeys, isNewData, exprEvaluatorContext);
return sortInternal(outgoingEvents, sortValuesMultiKeys, factory.getComparator());
}
private List<Object> createSortProperties(EventBean[][] generatingEvents, Object[] groupByKeys, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
Object[] sortProperties = new Object[generatingEvents.length];
OrderByElement[] elements = factory.getOrderBy();
if (elements.length == 1) {
int count = 0;
for (EventBean[] eventsPerStream : generatingEvents) {
// Make a new multikey that contains the sort-by values.
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(groupByKeys[count], exprEvaluatorContext.getAgentInstanceId(), null);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(eventsPerStream, factory.getOrderBy());
}
sortProperties[count] = elements[0].getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(sortProperties[count]);
}
count++;
}
} else {
int count = 0;
for (EventBean[] eventsPerStream : generatingEvents) {
// Make a new multikey that contains the sort-by values.
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(groupByKeys[count], exprEvaluatorContext.getAgentInstanceId(), null);
}
Object[] values = new Object[factory.getOrderBy().length];
int countTwo = 0;
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(eventsPerStream, factory.getOrderBy());
}
for (OrderByElement sortPair : factory.getOrderBy()) {
values[countTwo++] = sortPair.getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(values);
}
sortProperties[count] = new MultiKeyUntyped(values);
count++;
}
}
return Arrays.asList(sortProperties);
}
public EventBean[] sort(EventBean[] outgoingEvents, Object[] orderKeys, ExprEvaluatorContext exprEvaluatorContext) {
TreeMap<Object, Object> sort = new TreeMap<Object, Object>(factory.getComparator());
if (outgoingEvents == null || outgoingEvents.length < 2) {
return outgoingEvents;
}
for (int i = 0; i < outgoingEvents.length; i++) {
Object entry = sort.get(orderKeys[i]);
if (entry == null) {
sort.put(orderKeys[i], outgoingEvents[i]);
} else if (entry instanceof EventBean) {
List<EventBean> list = new ArrayList<EventBean>();
list.add((EventBean) entry);
list.add(outgoingEvents[i]);
sort.put(orderKeys[i], list);
} else {
List<EventBean> list = (List<EventBean>) entry;
list.add(outgoingEvents[i]);
}
}
EventBean[] result = new EventBean[outgoingEvents.length];
int count = 0;
for (Object entry : sort.values()) {
if (entry instanceof List) {
List<EventBean> output = (List<EventBean>) entry;
for (EventBean theEvent : output) {
result[count++] = theEvent;
}
} else {
result[count++] = (EventBean) entry;
}
}
return result;
}
private Object[] generateGroupKeys(EventBean[][] generatingEvents, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
Object[] keys = new Object[generatingEvents.length];
int count = 0;
for (EventBean[] eventsPerStream : generatingEvents) {
keys[count++] = generateGroupKey(eventsPerStream, isNewData, exprEvaluatorContext);
}
return keys;
}
private Object generateGroupKey(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
ExprEvaluator[] evals = factory.getGroupByNodes();
if (evals.length == 1) {
return evals[0].evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
}
Object[] keys = new Object[evals.length];
int count = 0;
for (ExprEvaluator exprNode : evals) {
keys[count] = exprNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
count++;
}
return new MultiKeyUntyped(keys);
}
private List<Object> createSortPropertiesWRollup(List<GroupByRollupKey> currentGenerators, OrderByElement[][] elementsPerLevel, boolean isNewData, AgentInstanceContext exprEvaluatorContext) {
Object[] sortProperties = new Object[currentGenerators.size()];
OrderByElement[] elements = factory.getOrderBy();
if (elements.length == 1) {
int count = 0;
for (GroupByRollupKey rollup : currentGenerators) {
// Make a new multikey that contains the sort-by values.
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(rollup.getGroupKey(), exprEvaluatorContext.getAgentInstanceId(), rollup.getLevel());
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(rollup.getGenerator(), factory.getOrderBy());
}
sortProperties[count] = elementsPerLevel[rollup.getLevel().getLevelNumber()][0].getExpr().evaluate(rollup.getGenerator(), isNewData, exprEvaluatorContext);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(sortProperties[count]);
}
count++;
}
} else {
int count = 0;
for (GroupByRollupKey rollup : currentGenerators) {
// Make a new multikey that contains the sort-by values.
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(rollup.getGroupKey(), exprEvaluatorContext.getAgentInstanceId(), rollup.getLevel());
}
Object[] values = new Object[factory.getOrderBy().length];
int countTwo = 0;
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(rollup.getGenerator(), factory.getOrderBy());
}
for (OrderByElement sortPair : elementsPerLevel[rollup.getLevel().getLevelNumber()]) {
values[countTwo++] = sortPair.getExpr().evaluate(rollup.getGenerator(), isNewData, exprEvaluatorContext);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(values);
}
sortProperties[count] = new MultiKeyUntyped(values);
count++;
}
}
return Arrays.asList(sortProperties);
}
private static EventBean[] sortInternal(EventBean[] outgoingEvents, List<Object> sortValuesMultiKeys, Comparator<Object> comparator) {
// Map the sort values to the corresponding outgoing events
Map<Object, List<EventBean>> sortToOutgoing = new HashMap<Object, List<EventBean>>();
int countOne = 0;
for (Object sortValues : sortValuesMultiKeys) {
List<EventBean> list = sortToOutgoing.get(sortValues);
if (list == null) {
list = new ArrayList<EventBean>();
}
list.add(outgoingEvents[countOne++]);
sortToOutgoing.put(sortValues, list);
}
// Sort the sort values
Collections.sort(sortValuesMultiKeys, comparator);
// Sort the outgoing events in the same order
Set<Object> sortSet = new LinkedHashSet<Object>(sortValuesMultiKeys);
EventBean[] result = new EventBean[outgoingEvents.length];
int countTwo = 0;
for (Object sortValues : sortSet) {
Collection<EventBean> output = sortToOutgoing.get(sortValues);
for (EventBean theEvent : output) {
result[countTwo++] = theEvent;
}
}
return result;
}
public EventBean determineLocalMinMax(EventBean[] outgoingEvents, EventBean[][] generatingEvents, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
// Get the group by keys if needed
Object[] groupByKeys = null;
if (factory.isNeedsGroupByKeys()) {
groupByKeys = generateGroupKeys(generatingEvents, isNewData, exprEvaluatorContext);
}
OrderByElement[] elements = factory.getOrderBy();
Object localMinMax = null;
EventBean outgoingMinMaxBean = null;
if (elements.length == 1) {
int count = 0;
for (EventBean[] eventsPerStream : generatingEvents) {
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(groupByKeys[count], exprEvaluatorContext.getAgentInstanceId(), null);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(eventsPerStream, factory.getOrderBy());
}
Object sortKey = elements[0].getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(localMinMax);
}
boolean newMinMax = localMinMax == null || factory.getComparator().compare(localMinMax, sortKey) > 0;
if (newMinMax) {
localMinMax = sortKey;
outgoingMinMaxBean = outgoingEvents[count];
}
count++;
}
} else {
int count = 0;
Object[] values = new Object[factory.getOrderBy().length];
MultiKeyUntyped valuesMk = new MultiKeyUntyped(values);
for (EventBean[] eventsPerStream : generatingEvents) {
if (factory.isNeedsGroupByKeys()) {
aggregationService.setCurrentAccess(groupByKeys[count], exprEvaluatorContext.getAgentInstanceId(), null);
}
int countTwo = 0;
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qOrderBy(eventsPerStream, factory.getOrderBy());
}
for (OrderByElement sortPair : factory.getOrderBy()) {
values[countTwo++] = sortPair.getExpr().evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aOrderBy(values);
}
boolean newMinMax = localMinMax == null || factory.getComparator().compare(localMinMax, valuesMk) > 0;
if (newMinMax) {
localMinMax = valuesMk;
values = new Object[factory.getOrderBy().length];
valuesMk = new MultiKeyUntyped(values);
outgoingMinMaxBean = outgoingEvents[count];
}
count++;
}
}
return outgoingMinMaxBean;
}
public EventBean determineLocalMinMax(EventBean[] outgoingEvents, Object[] orderKeys) {
Object localMinMax = null;
EventBean outgoingMinMaxBean = null;
for (int i = 0; i < outgoingEvents.length; i++) {
boolean newMinMax = localMinMax == null || factory.getComparator().compare(localMinMax, orderKeys[i]) > 0;
if (newMinMax) {
localMinMax = orderKeys[i];
outgoingMinMaxBean = outgoingEvents[i];
}
}
return outgoingMinMaxBean;
}
}