/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * 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.client.EventType; import com.espertech.esper.collection.*; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.epl.expression.ExprEvaluator; import com.espertech.esper.epl.expression.ExprEvaluatorContext; import com.espertech.esper.util.CollectionUtil; import com.espertech.esper.view.Viewable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; /** * Result set processor for the simplest case: no aggregation functions used in the select clause, and no group-by. * <p> * The processor generates one row for each event entering (new event) and one row for each event leaving (old event). */ public class ResultSetProcessorSimple extends ResultSetProcessorBaseSimple { private static final Log log = LogFactory.getLog(ResultSetProcessorSimple.class); private final ResultSetProcessorSimpleFactory prototype; private final SelectExprProcessor selectExprProcessor; private final OrderByProcessor orderByProcessor; private ExprEvaluatorContext exprEvaluatorContext; public ResultSetProcessorSimple(ResultSetProcessorSimpleFactory prototype, SelectExprProcessor selectExprProcessor, OrderByProcessor orderByProcessor, ExprEvaluatorContext exprEvaluatorContext) { this.prototype = prototype; this.selectExprProcessor = selectExprProcessor; this.orderByProcessor = orderByProcessor; this.exprEvaluatorContext = exprEvaluatorContext; } public void setAgentInstanceContext(AgentInstanceContext context) { exprEvaluatorContext = context; } public EventType getResultEventType() { return prototype.getResultEventType(); } public UniformPair<EventBean[]> processJoinResult(Set<MultiKey<EventBean>> newEvents, Set<MultiKey<EventBean>> oldEvents, boolean isSynthesize) { EventBean[] selectOldEvents = null; EventBean[] selectNewEvents; if (prototype.getOptionalHavingExpr() == null) { if (prototype.isSelectRStream()) { selectOldEvents = getSelectEventsNoHaving(selectExprProcessor, orderByProcessor, oldEvents, false, isSynthesize, exprEvaluatorContext); } selectNewEvents = getSelectEventsNoHaving(selectExprProcessor, orderByProcessor, newEvents, true, isSynthesize, exprEvaluatorContext); } else { if (prototype.isSelectRStream()) { selectOldEvents = getSelectEventsHaving(selectExprProcessor, orderByProcessor, oldEvents, prototype.getOptionalHavingExpr(), false, isSynthesize, exprEvaluatorContext); } selectNewEvents = getSelectEventsHaving(selectExprProcessor, orderByProcessor, newEvents, prototype.getOptionalHavingExpr(), true, isSynthesize, exprEvaluatorContext); } return new UniformPair<EventBean[]>(selectNewEvents, selectOldEvents); } public UniformPair<EventBean[]> processViewResult(EventBean[] newData, EventBean[] oldData, boolean isSynthesize) { EventBean[] selectOldEvents = null; EventBean[] selectNewEvents; if (prototype.getOptionalHavingExpr() == null) { if (prototype.isSelectRStream()) { selectOldEvents = getSelectEventsNoHaving(selectExprProcessor, orderByProcessor, oldData, false, isSynthesize, exprEvaluatorContext); } selectNewEvents = getSelectEventsNoHaving(selectExprProcessor, orderByProcessor, newData, true, isSynthesize, exprEvaluatorContext); } else { if (prototype.isSelectRStream()) { selectOldEvents = getSelectEventsHaving(selectExprProcessor, orderByProcessor, oldData, prototype.getOptionalHavingExpr(), false, isSynthesize, exprEvaluatorContext); } selectNewEvents = getSelectEventsHaving(selectExprProcessor, orderByProcessor, newData, prototype.getOptionalHavingExpr(), true, isSynthesize, exprEvaluatorContext); } return new UniformPair<EventBean[]>(selectNewEvents, selectOldEvents); } /** * Process view results for the iterator. * @param newData new events * @return pair of insert and remove stream */ public UniformPair<EventBean[]> processViewResultIterator(EventBean[] newData) { EventBean[] selectOldEvents = null; EventBean[] selectNewEvents; if (prototype.getOptionalHavingExpr() == null) { selectNewEvents = getSelectEventsNoHaving(selectExprProcessor, null, newData, true, true, exprEvaluatorContext); } else { selectNewEvents = getSelectEventsHaving(selectExprProcessor, null, newData, prototype.getOptionalHavingExpr(), true, true, exprEvaluatorContext); } return new UniformPair<EventBean[]>(selectNewEvents, selectOldEvents); } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - orders the outgoing events according to the order-by clause * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsNoHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return null; } EventBean[] result = new EventBean[events.length]; EventBean[][] eventGenerators = null; if(orderByProcessor != null) { eventGenerators = new EventBean[events.length][]; } EventBean[] eventsPerStream = new EventBean[1]; for (int i = 0; i < events.length; i++) { eventsPerStream[0] = events[i]; // Wildcard select case if(exprProcessor == null) { result[i] = events[i]; } else { result[i] = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); } if(orderByProcessor != null) { eventGenerators[i] = new EventBean[] {events[i]}; } } if(orderByProcessor != null) { return orderByProcessor.sort(result, eventGenerators, isNewData, exprEvaluatorContext); } else { return result; } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsNoHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, Set<MultiKey<EventBean>> events, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if ((events == null) || (events.isEmpty())) { return null; } int length = events.size(); EventBean[] result = new EventBean[length]; EventBean[][] eventGenerators = null; if(orderByProcessor != null) { eventGenerators = new EventBean[length][]; } int count = 0; for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); result[count] = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); if(orderByProcessor != null) { eventGenerators[count] = eventsPerStream; } count++; } if(orderByProcessor != null) { return orderByProcessor.sort(result, eventGenerators, isNewData, exprEvaluatorContext); } else { return result; } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return null; } LinkedList<EventBean> result = new LinkedList<EventBean>(); List<EventBean[]> eventGenerators = null; if(orderByProcessor != null) { eventGenerators = new ArrayList<EventBean[]>(); } EventBean[] eventsPerStream = new EventBean[1]; for (EventBean theEvent : events) { eventsPerStream[0] = theEvent; Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } result.add(exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext)); if (orderByProcessor != null) { eventGenerators.add(new EventBean[]{theEvent}); } } if (!result.isEmpty()) { if(orderByProcessor != null) { return orderByProcessor.sort(result.toArray(new EventBean[result.size()]), eventGenerators.toArray(new EventBean[eventGenerators.size()][]), isNewData, exprEvaluatorContext); } else { return result.toArray(new EventBean[result.size()]); } } else { return null; } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, Set<MultiKey<EventBean>> events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if ((events == null) || (events.isEmpty())) { return null; } LinkedList<EventBean> result = new LinkedList<EventBean>(); List<EventBean[]> eventGenerators = null; if(orderByProcessor != null) { eventGenerators = new ArrayList<EventBean[]>(); } for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } EventBean resultEvent = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); result.add(resultEvent); if(orderByProcessor != null) { eventGenerators.add(eventsPerStream); } } if (!result.isEmpty()) { if(orderByProcessor != null) { return orderByProcessor.sort(result.toArray(new EventBean[result.size()]), eventGenerators.toArray(new EventBean[eventGenerators.size()][]), isNewData, exprEvaluatorContext); } else { return result.toArray(new EventBean[result.size()]); } } else { return null; } } public Iterator<EventBean> getIterator(Viewable parent) { if (orderByProcessor != null) { // Pull all events, generate order keys EventBean[] eventsPerStream = new EventBean[1]; List<EventBean> events = new ArrayList<EventBean>(); List<Object> orderKeys = new ArrayList<Object>(); Iterator parentIterator = parent.iterator(); if (parentIterator == null) { return CollectionUtil.NULL_EVENT_ITERATOR; } for (EventBean aParent : parent) { eventsPerStream[0] = aParent; Object orderKey = orderByProcessor.getSortKey(eventsPerStream, true, exprEvaluatorContext); UniformPair<EventBean[]> pair = processViewResultIterator(eventsPerStream); EventBean[] result = pair.getFirst(); if (result.length != 0) { events.add(result[0]); } orderKeys.add(orderKey); } // sort EventBean[] outgoingEvents = events.toArray(new EventBean[events.size()]); Object[] orderKeysArr = orderKeys.toArray(new Object[orderKeys.size()]); EventBean[] orderedEvents = orderByProcessor.sort(outgoingEvents, orderKeysArr, exprEvaluatorContext); return new ArrayEventIterator(orderedEvents); } // Return an iterator that gives row-by-row a result return new TransformEventIterator(parent.iterator(), new ResultSetProcessorSimpleTransform(this)); } public Iterator<EventBean> getIterator(Set<MultiKey<EventBean>> joinSet) { // Process join results set as a regular join, includes sorting and having-clause filter UniformPair<EventBean[]> result = processJoinResult(joinSet, CollectionUtil.EMPTY_ROW_SET, true); return new ArrayEventIterator(result.getFirst()); } public void clear() { // No need to clear state, there is no state held } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @return output events, one for each input event */ protected static EventBean[] getSelectEventsNoHaving(SelectExprProcessor exprProcessor, EventBean[] events, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return null; } EventBean[] result = new EventBean[events.length]; EventBean[] eventsPerStream = new EventBean[1]; for (int i = 0; i < events.length; i++) { eventsPerStream[0] = events[i]; result[i] = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); } return result; } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @return output events, one for each input event */ protected static EventBean[] getSelectEventsNoHaving(SelectExprProcessor exprProcessor, Set<MultiKey<EventBean>> events, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if ((events == null) || (events.isEmpty())) { return null; } int length = events.size(); EventBean[] result = new EventBean[length]; int count = 0; for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); result[count] = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); count++; } return result; } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsHaving(SelectExprProcessor exprProcessor, EventBean[] events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return null; } LinkedList<EventBean> result = new LinkedList<EventBean>(); EventBean[] eventsPerStream = new EventBean[1]; for (EventBean theEvent : events) { eventsPerStream[0] = theEvent; Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } result.add(exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext)); } if (!result.isEmpty()) { return result.toArray(new EventBean[result.size()]); } else { return null; } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param exprEvaluatorContext context for expression evalauation * @return output events, one for each input event */ protected static EventBean[] getSelectEventsHaving(SelectExprProcessor exprProcessor, Set<MultiKey<EventBean>> events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) { LinkedList<EventBean> result = new LinkedList<EventBean>(); for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } EventBean resultEvent = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); result.add(resultEvent); } if (!result.isEmpty()) { return result.toArray(new EventBean[result.size()]); } else { return null; } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - orders the outgoing events according to the order-by clause * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param result is the result event list to populate * @param exprEvaluatorContext context for expression evalauation * @param optSortKeys is the result sort key list to populate, for sorting */ protected static void getSelectEventsNoHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, boolean isNewData, boolean isSynthesize, List<EventBean> result, List<Object> optSortKeys, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return; } EventBean[] eventsPerStream = new EventBean[1]; for (EventBean theEvent : events) { eventsPerStream[0] = theEvent; result.add(exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext)); if (orderByProcessor != null) { optSortKeys.add(orderByProcessor.getSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); } } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param result is the result event list to populate * @param optSortKeys is the result sort key list to populate, for sorting * @param exprEvaluatorContext context for expression evalauation */ protected static void getSelectEventsNoHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, Set<MultiKey<EventBean>> events, boolean isNewData, boolean isSynthesize, List<EventBean> result, List<Object> optSortKeys, ExprEvaluatorContext exprEvaluatorContext) { int length = (events != null) ? events.size() : 0; if (length == 0) { return; } for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); result.add(exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext)); if(orderByProcessor != null) { optSortKeys.add(orderByProcessor.getSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); } } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param result is the result event list to populate * @param exprEvaluatorContext context for expression evalauation * @param optSortKeys is the result sort key list to populate, for sorting */ protected static void getSelectEventsHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, EventBean[] events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, List<EventBean> result, List<Object> optSortKeys, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return; } EventBean[] eventsPerStream = new EventBean[1]; for (EventBean theEvent : events) { eventsPerStream[0] = theEvent; Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } result.add(exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext)); if (orderByProcessor != null) { optSortKeys.add(orderByProcessor.getSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); } } } /** * Applies the select-clause to the given events returning the selected events. The number of events stays the * same, i.e. this method does not filter it just transforms the result set. * <p> * Also applies a having clause. * @param exprProcessor - processes each input event and returns output event * @param orderByProcessor - for sorting output events according to the order-by clause * @param events - input events * @param optionalHavingNode - supplies the having-clause expression * @param isNewData - indicates whether we are dealing with new data (istream) or old data (rstream) * @param isSynthesize - set to true to indicate that synthetic events are required for an iterator result set * @param result is the result event list to populate * @param optSortKeys is the result sort key list to populate, for sorting * @param exprEvaluatorContext context for expression evalauation */ protected static void getSelectEventsHaving(SelectExprProcessor exprProcessor, OrderByProcessor orderByProcessor, Set<MultiKey<EventBean>> events, ExprEvaluator optionalHavingNode, boolean isNewData, boolean isSynthesize, List<EventBean> result, List<Object> optSortKeys, ExprEvaluatorContext exprEvaluatorContext) { if (events == null) { return; } for (MultiKey<EventBean> key : events) { EventBean[] eventsPerStream = key.getArray(); Boolean passesHaving = (Boolean) optionalHavingNode.evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if ((passesHaving == null) || (!passesHaving)) { continue; } EventBean resultEvent = exprProcessor.process(eventsPerStream, isNewData, isSynthesize, exprEvaluatorContext); result.add(resultEvent); if(orderByProcessor != null) { optSortKeys.add(orderByProcessor.getSortKey(eventsPerStream, isNewData, exprEvaluatorContext)); } } } public boolean hasAggregation() { return false; } }