/*
***************************************************************************************
* 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.join.plan;
import com.espertech.esper.epl.expression.core.ExprIdentNode;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.epl.expression.core.ExprNodeUtility;
import com.espertech.esper.util.CollectionUtil;
import java.io.StringWriter;
import java.util.*;
/**
* Property lists stored as a value for each stream-to-stream relationship, for use by {@link QueryGraph}.
*/
public class QueryGraphValue {
private List<QueryGraphValueDesc> items;
/**
* Ctor.
*/
public QueryGraphValue() {
items = new ArrayList<>();
}
public boolean isEmptyNotNavigable() {
return items.isEmpty();
}
public List<QueryGraphValueDesc> getItems() {
return items;
}
/**
* Add key and index property.
*
* @param keyProperty - key property
* @param indexPropertyIdent - index property
* @param keyPropNode key node
* @return true if added and either property did not exist, false if either already existed
*/
public boolean addStrictCompare(String keyProperty, ExprIdentNode keyPropNode, ExprIdentNode indexPropertyIdent) {
QueryGraphValueDesc value = findIdentEntry(indexPropertyIdent);
if (value != null && value.getEntry() instanceof QueryGraphValueEntryHashKeyedExpr) {
// if this index property exists and is compared to a constant, ignore the index prop
QueryGraphValueEntryHashKeyedExpr expr = (QueryGraphValueEntryHashKeyedExpr) value.getEntry();
if (expr.isConstant()) {
return false;
}
}
if (value != null && value.getEntry() instanceof QueryGraphValueEntryHashKeyedProp) {
return false; // second comparison, ignore
}
items.add(new QueryGraphValueDesc(new ExprNode[]{indexPropertyIdent}, new QueryGraphValueEntryHashKeyedProp(keyPropNode, keyProperty)));
return true;
}
public void addRange(QueryGraphRangeEnum rangeType, ExprNode propertyStart, ExprNode propertyEnd, ExprIdentNode propertyValueIdent) {
if (!rangeType.isRange()) {
throw new IllegalArgumentException("Expected range type, received " + rangeType);
}
// duplicate can be removed right away
if (findIdentEntry(propertyValueIdent) != null) {
return;
}
items.add(new QueryGraphValueDesc(new ExprNode[]{propertyValueIdent}, new QueryGraphValueEntryRangeIn(rangeType, propertyStart, propertyEnd, true)));
}
public void addRelOp(ExprNode propertyKey, QueryGraphRangeEnum op, ExprIdentNode propertyValueIdent, boolean isBetweenOrIn) {
// Note: Read as follows:
// System.out.println("If I have an index on '" + propertyValue + "' I'm evaluating " + propertyKey + " and finding all values of " + propertyValue + " " + op + " then " + propertyKey);
// Check if there is an opportunity to convert this to a range or remove an earlier specification
QueryGraphValueDesc existing = findIdentEntry(propertyValueIdent);
if (existing == null) {
items.add(new QueryGraphValueDesc(new ExprNode[]{propertyValueIdent}, new QueryGraphValueEntryRangeRelOp(op, propertyKey, isBetweenOrIn)));
return;
}
if (!(existing.getEntry() instanceof QueryGraphValueEntryRangeRelOp)) {
return; // another comparison exists already, don't add range
}
QueryGraphValueEntryRangeRelOp relOp = (QueryGraphValueEntryRangeRelOp) existing.getEntry();
QueryGraphRangeConsolidateDesc opsDesc = QueryGraphRangeUtil.getCanConsolidate(op, relOp.getType());
if (opsDesc != null) {
ExprNode start = !opsDesc.isReverse() ? relOp.getExpression() : propertyKey;
ExprNode end = !opsDesc.isReverse() ? propertyKey : relOp.getExpression();
items.remove(existing);
addRange(opsDesc.getType(), start, end, propertyValueIdent);
}
}
public void addUnkeyedExpr(ExprIdentNode indexedPropIdent, ExprNode exprNodeNoIdent) {
items.add(new QueryGraphValueDesc(new ExprNode[]{indexedPropIdent}, new QueryGraphValueEntryHashKeyedExpr(exprNodeNoIdent, false)));
}
public void addKeyedExpr(ExprIdentNode indexedPropIdent, ExprNode exprNodeNoIdent) {
items.add(new QueryGraphValueDesc(new ExprNode[]{indexedPropIdent}, new QueryGraphValueEntryHashKeyedExpr(exprNodeNoIdent, true)));
}
public QueryGraphValuePairHashKeyIndex getHashKeyProps() {
List<QueryGraphValueEntryHashKeyed> keys = new ArrayList<QueryGraphValueEntryHashKeyed>();
Deque<String> indexed = new ArrayDeque<String>();
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryHashKeyed) {
QueryGraphValueEntryHashKeyed keyprop = (QueryGraphValueEntryHashKeyed) desc.getEntry();
keys.add(keyprop);
indexed.add(getSingleIdentNodeProp(desc.getIndexExprs()));
}
}
String[] strictKeys = new String[indexed.size()];
int count = 0;
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryHashKeyed) {
if (desc.getEntry() instanceof QueryGraphValueEntryHashKeyedProp) {
QueryGraphValueEntryHashKeyedProp keyprop = (QueryGraphValueEntryHashKeyedProp) desc.getEntry();
strictKeys[count] = keyprop.getKeyProperty();
}
count++;
}
}
return new QueryGraphValuePairHashKeyIndex(indexed.toArray(new String[indexed.size()]), keys, strictKeys);
}
public QueryGraphValuePairRangeIndex getRangeProps() {
Deque<String> indexed = new ArrayDeque<String>();
List<QueryGraphValueEntryRange> keys = new ArrayList<QueryGraphValueEntryRange>();
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryRange) {
QueryGraphValueEntryRange keyprop = (QueryGraphValueEntryRange) desc.getEntry();
keys.add(keyprop);
indexed.add(getSingleIdentNodeProp(desc.getIndexExprs()));
}
}
return new QueryGraphValuePairRangeIndex(indexed.toArray(new String[indexed.size()]), keys);
}
public String toString() {
StringWriter writer = new StringWriter();
writer.append("QueryGraphValue ");
String delimiter = "";
for (QueryGraphValueDesc desc : items) {
writer.append(delimiter);
writer.append(ExprNodeUtility.toExpressionStringMinPrecedenceAsList(desc.getIndexExprs()));
writer.append(": ");
writer.append(desc.getEntry().toString());
delimiter = ", ";
}
return writer.toString();
}
public void addInKeywordMultiIdx(ExprNode testPropExpr, ExprNode[] setProps) {
items.add(new QueryGraphValueDesc(setProps, new QueryGraphValueEntryInKeywordMultiIdx(testPropExpr)));
}
public void addInKeywordSingleIdx(ExprNode testPropIdent, ExprNode[] setPropExpr) {
ExprNode[] indexExpressions = new ExprNode[]{testPropIdent};
QueryGraphValueDesc found = findEntry(indexExpressions);
ExprNode[] setExpressions = setPropExpr;
if (found != null && found.getEntry() instanceof QueryGraphValueEntryInKeywordSingleIdx) {
QueryGraphValueEntryInKeywordSingleIdx existing = (QueryGraphValueEntryInKeywordSingleIdx) found.getEntry();
setExpressions = (ExprNode[]) CollectionUtil.addArrays(existing.getKeyExprs(), setPropExpr);
items.remove(found);
}
items.add(new QueryGraphValueDesc(new ExprNode[]{testPropIdent}, new QueryGraphValueEntryInKeywordSingleIdx(setExpressions)));
}
public QueryGraphValuePairInKWSingleIdx getInKeywordSingles() {
List<String> indexedProps = new ArrayList<String>();
List<QueryGraphValueEntryInKeywordSingleIdx> single = new ArrayList<QueryGraphValueEntryInKeywordSingleIdx>();
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryInKeywordSingleIdx) {
QueryGraphValueEntryInKeywordSingleIdx keyprop = (QueryGraphValueEntryInKeywordSingleIdx) desc.getEntry();
single.add(keyprop);
indexedProps.add(getSingleIdentNodeProp(desc.getIndexExprs()));
}
}
return new QueryGraphValuePairInKWSingleIdx(indexedProps.toArray(new String[indexedProps.size()]), single);
}
public List<QueryGraphValuePairInKWMultiIdx> getInKeywordMulti() {
List<QueryGraphValuePairInKWMultiIdx> multi = new ArrayList<QueryGraphValuePairInKWMultiIdx>();
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryInKeywordMultiIdx) {
QueryGraphValueEntryInKeywordMultiIdx keyprop = (QueryGraphValueEntryInKeywordMultiIdx) desc.getEntry();
multi.add(new QueryGraphValuePairInKWMultiIdx(desc.getIndexExprs(), keyprop));
}
}
return multi;
}
public void addCustom(ExprNode[] indexExpressions, String operationName, int expressionPosition, ExprNode expression) {
// find existing custom-entry for same index expressions
QueryGraphValueEntryCustom found = null;
for (QueryGraphValueDesc desc : items) {
if (desc.getEntry() instanceof QueryGraphValueEntryCustom) {
if (ExprNodeUtility.deepEquals(desc.getIndexExprs(), indexExpressions, true)) {
found = (QueryGraphValueEntryCustom) desc.getEntry();
break;
}
}
}
if (found == null) {
found = new QueryGraphValueEntryCustom();
items.add(new QueryGraphValueDesc(indexExpressions, found));
}
// find/create operation against the indexed fields
QueryGraphValueEntryCustomKey key = new QueryGraphValueEntryCustomKey(operationName, indexExpressions);
QueryGraphValueEntryCustomOperation op = found.getOperations().get(key);
if (op == null) {
op = new QueryGraphValueEntryCustomOperation();
found.getOperations().put(key, op);
}
op.getPositionalExpressions().put(expressionPosition, expression);
}
private QueryGraphValueDesc findIdentEntry(ExprIdentNode search) {
for (QueryGraphValueDesc desc : items) {
if (desc.getIndexExprs().length > 1 || !(desc.getIndexExprs()[0] instanceof ExprIdentNode)) {
continue;
}
ExprIdentNode other = (ExprIdentNode) desc.getIndexExprs()[0];
if (search.getResolvedPropertyName().equals(other.getResolvedPropertyName())) {
return desc;
}
}
return null;
}
private QueryGraphValueDesc findEntry(ExprNode[] search) {
for (QueryGraphValueDesc desc : items) {
if (ExprNodeUtility.deepEquals(search, desc.getIndexExprs(), true)) {
return desc;
}
}
return null;
}
private String getSingleIdentNodeProp(ExprNode[] indexExprs) {
if (indexExprs.length != 1) {
throw new IllegalStateException("Incorrect number of index expressions");
}
ExprIdentNode identNode = (ExprIdentNode) indexExprs[0];
return identNode.getResolvedPropertyName();
}
}