private static CollectionExpression parseInternalCollectionExpression(Expression expression,
                                                                          MatchingMetaInfoHolder matchingMetaInfoHolder,
                                                                          IndexedEventHolder indexedEventHolder) {
        if (expression instanceof And) {

            CollectionExpression leftCollectionExpression = parseInternalCollectionExpression(((And) expression)
                    .getLeftExpression(), matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression rightCollectionExpression = parseInternalCollectionExpression(((And) expression)
                    .getRightExpression(), matchingMetaInfoHolder, indexedEventHolder);

            if (leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON &&
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else if ((leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET)
                    && (rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET)) {
                Set<String> primaryKeys = new HashSet<>();
                primaryKeys.addAll(leftCollectionExpression.getMultiPrimaryKeys());
                primaryKeys.addAll(rightCollectionExpression.getMultiPrimaryKeys());
                if (indexedEventHolder.getPrimaryKeyReferenceHolders() != null
                        && primaryKeys.size() == indexedEventHolder.getPrimaryKeyReferenceHolders().length) {
                    return new AndMultiPrimaryKeyCollectionExpression(expression,
                            CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET,
                            leftCollectionExpression, rightCollectionExpression);
                } else {
                    return new AndCollectionExpression(expression,
                            CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET,
                            leftCollectionExpression, rightCollectionExpression);
                }
                //TODO support query rewriting to group all PARTIAL_PRIMARY_KEY_RESULT_SETs together such that it can
                // build AndMultiPrimaryKeyCollectionExpression.
            } else if ((leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.EXHAUSTIVE)
                    && (rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.EXHAUSTIVE)) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            } else {
                return new AndCollectionExpression(expression, CollectionExpression.CollectionScope.OPTIMISED_PRIMARY_KEY_OR_INDEXED_RESULT_SET,
                        leftCollectionExpression, rightCollectionExpression);
            }
        } else if (expression instanceof Or) {
            CollectionExpression leftCollectionExpression = parseInternalCollectionExpression(((Or) expression)
                    .getLeftExpression(), matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression rightCollectionExpression = parseInternalCollectionExpression(((Or) expression)
                    .getRightExpression(), matchingMetaInfoHolder, indexedEventHolder);

            if (leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON &&
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else if (leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.EXHAUSTIVE
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.EXHAUSTIVE
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE
                    || rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            } else {
                return new OrCollectionExpression(expression,
                        CollectionExpression.CollectionScope.OPTIMISED_PRIMARY_KEY_OR_INDEXED_RESULT_SET,
                        leftCollectionExpression, rightCollectionExpression);
            }
        } else if (expression instanceof Not) {
            CollectionExpression notCollectionExpression = parseInternalCollectionExpression(
                    ((Not) expression).getExpression(), matchingMetaInfoHolder, indexedEventHolder);
            switch (notCollectionExpression.getCollectionScope()) {
                case NON:
                    return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
                case PRIMARY_KEY_ATTRIBUTE:
                    return new NotCollectionExpression(expression, CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET, notCollectionExpression);
                case INDEXED_ATTRIBUTE:
                    return new NotCollectionExpression(expression, CollectionExpression.CollectionScope.INDEXED_RESULT_SET, notCollectionExpression);
                case PRIMARY_KEY_RESULT_SET:
                case INDEXED_RESULT_SET:
                case OPTIMISED_PRIMARY_KEY_OR_INDEXED_RESULT_SET:
                    return new NotCollectionExpression(expression, CollectionExpression.CollectionScope.OPTIMISED_PRIMARY_KEY_OR_INDEXED_RESULT_SET,
                            notCollectionExpression);
                case PARTIAL_PRIMARY_KEY_ATTRIBUTE:
                case PARTIAL_PRIMARY_KEY_RESULT_SET:
                case EXHAUSTIVE:
                    return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Compare) {
            CollectionExpression leftCollectionExpression = parseInternalCollectionExpression(((Compare) expression)
                    .getLeftExpression(), matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression rightCollectionExpression = parseInternalCollectionExpression(((Compare) expression)
                    .getRightExpression(), matchingMetaInfoHolder, indexedEventHolder);

            if (leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON &&
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                //comparing two stream attributes with O(1) time complexity
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else if ((leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.INDEXED_ATTRIBUTE ||
                    leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE ||
                    leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE) &&
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                switch (leftCollectionExpression.getCollectionScope()) {
                    case INDEXED_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.INDEXED_RESULT_SET,
                                leftCollectionExpression, ((Compare) expression).getOperator(),
                                rightCollectionExpression);
                    case PRIMARY_KEY_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET,
                                leftCollectionExpression, ((Compare) expression).getOperator(),
                                rightCollectionExpression);
                    case PARTIAL_PRIMARY_KEY_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET,
                                leftCollectionExpression, ((Compare) expression).getOperator(),
                                rightCollectionExpression);
                }
            } else if (leftCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON
                    && (rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.INDEXED_ATTRIBUTE ||
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE ||
                    rightCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE)) {
                Compare.Operator operator = ((Compare) expression).getOperator();
                //moving let to right
                switch (operator) {
                    case LESS_THAN:
                        operator = Compare.Operator.GREATER_THAN;
                        break;
                    case GREATER_THAN:
                        operator = Compare.Operator.LESS_THAN;
                        break;
                    case LESS_THAN_EQUAL:
                        operator = Compare.Operator.GREATER_THAN_EQUAL;
                        break;
                    case GREATER_THAN_EQUAL:
                        operator = Compare.Operator.LESS_THAN_EQUAL;
                        break;
                    case EQUAL:
                        break;
                    case NOT_EQUAL:
                        break;
                }
                switch (rightCollectionExpression.getCollectionScope()) {
                    case INDEXED_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.INDEXED_RESULT_SET,
                                rightCollectionExpression, operator, leftCollectionExpression);
                    case PRIMARY_KEY_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET,
                                rightCollectionExpression, operator, leftCollectionExpression);
                    case PARTIAL_PRIMARY_KEY_ATTRIBUTE:
                        return new CompareCollectionExpression((Compare) expression, CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_RESULT_SET,
                                rightCollectionExpression, operator, leftCollectionExpression);
                }
            } else {
                //comparing non indexed table with stream attributes or another table attribute
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Constant) {
            return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
        } else if (expression instanceof Variable) {
            if (((Variable) expression).getStreamId() == null) {
                List<String> attributeNameList = Arrays.asList(matchingMetaInfoHolder.getMatchingStreamDefinition().
                        getAttributeNameArray());
                if (attributeNameList.contains(((Variable) expression).getAttributeName())) {
                    String streamId = matchingMetaInfoHolder.getMatchingStreamDefinition().getId();
                    ((Variable) expression).setStreamId(streamId);
                }
            }

            if (isCollectionVariable(matchingMetaInfoHolder, (Variable) expression)) {
                if (indexedEventHolder.isAttributeIndexed(((Variable) expression).getAttributeName())) {
                    return new AttributeCollectionExpression(expression, ((Variable) expression).getAttributeName(),
                            CollectionExpression.CollectionScope.INDEXED_ATTRIBUTE);
                } else if (indexedEventHolder.isMultiPrimaryKeyAttribute(((Variable) expression).getAttributeName())) {
                    if (indexedEventHolder.getPrimaryKeyReferenceHolders() != null
                            && indexedEventHolder.getPrimaryKeyReferenceHolders().length == 1) {
                        return new AttributeCollectionExpression(expression, ((Variable) expression).getAttributeName(),
                                CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE);
                    } else {
                        return new AttributeCollectionExpression(expression, ((Variable) expression).getAttributeName(),
                                CollectionExpression.CollectionScope.PARTIAL_PRIMARY_KEY_ATTRIBUTE);
                    }
                } else {
                    return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
                }
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            }
        } else if (expression instanceof Multiply) {
            CollectionExpression left = parseInternalCollectionExpression(((Multiply) expression).getLeftValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression right = parseInternalCollectionExpression(((Multiply) expression).getRightValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (left.getCollectionScope() == CollectionExpression.CollectionScope.NON && right.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Add) {
            CollectionExpression left = parseInternalCollectionExpression(((Add) expression).getLeftValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression right = parseInternalCollectionExpression(((Add) expression).getRightValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (left.getCollectionScope() == CollectionExpression.CollectionScope.NON && right.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Subtract) {
            CollectionExpression left = parseInternalCollectionExpression(((Subtract) expression).getLeftValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression right = parseInternalCollectionExpression(((Subtract) expression).getRightValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (left.getCollectionScope() == CollectionExpression.CollectionScope.NON && right.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Mod) {
            CollectionExpression left = parseInternalCollectionExpression(((Mod) expression).getLeftValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression right = parseInternalCollectionExpression(((Mod) expression).getRightValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (left.getCollectionScope() == CollectionExpression.CollectionScope.NON && right.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof Divide) {
            CollectionExpression left = parseInternalCollectionExpression(((Divide) expression).getLeftValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            CollectionExpression right = parseInternalCollectionExpression(((Divide) expression).getRightValue(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (left.getCollectionScope() == CollectionExpression.CollectionScope.NON && right.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        } else if (expression instanceof AttributeFunction) {
            Expression[] innerExpressions = ((AttributeFunction) expression).getParameters();
            for (Expression aExpression : innerExpressions) {
                CollectionExpression aCollectionExpression = parseInternalCollectionExpression(aExpression,
                        matchingMetaInfoHolder, indexedEventHolder);
                if (aCollectionExpression.getCollectionScope() != CollectionExpression.CollectionScope.NON) {
                    return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
                }
            }
            return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
        } else if (expression instanceof In) {
            CollectionExpression inCollectionExpression = parseInternalCollectionExpression(
                    ((In) expression).getExpression(), matchingMetaInfoHolder, indexedEventHolder);
            if (inCollectionExpression.getCollectionScope() != CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
            return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
        } else if (expression instanceof IsNull) {
            CollectionExpression nullCollectionExpression = parseInternalCollectionExpression(((IsNull) expression)
                            .getExpression(),
                    matchingMetaInfoHolder, indexedEventHolder);
            if (nullCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.NON) {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.NON);
            } else if (nullCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.INDEXED_ATTRIBUTE) {
                return new NullCollectionExpression(expression, CollectionExpression.CollectionScope.INDEXED_RESULT_SET,
                        ((AttributeCollectionExpression) nullCollectionExpression).getAttribute());
            } else if (nullCollectionExpression.getCollectionScope() == CollectionExpression.CollectionScope.PRIMARY_KEY_ATTRIBUTE) {
                return new NullCollectionExpression(expression, CollectionExpression.CollectionScope.PRIMARY_KEY_RESULT_SET,
                        ((AttributeCollectionExpression) nullCollectionExpression).getAttribute());
            } else {
                return new BasicCollectionExpression(expression, CollectionExpression.CollectionScope.EXHAUSTIVE);
            }
        }
        throw new UnsupportedOperationException(expression.toString() + " not supported!");
    }
Parse the given expression and create the appropriate Executor by recursively traversing the expression.

@param expression             Expression to be parsed
@param matchingMetaInfoHolder matchingMetaInfoHolder
@param indexedEventHolder     indexed event holder
@return ExpressionExecutor
modules/siddhi-core/src/main/java/io/siddhi/core/util/parser/CollectionExpressionParser.java