com.spidertracks.datanucleus.query.runtime.EqualityOperand.java Source code

Java tutorial

Introduction

Here is the source code for com.spidertracks.datanucleus.query.runtime.EqualityOperand.java

Source

/**********************************************************************
Copyright (c) 2010 Todd Nine. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    
http://www.apache.org/licenses/LICENSE-2.0
    
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    
Contributors :
...
 ***********************************************************************/
package com.spidertracks.datanucleus.query.runtime;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;

import com.spidertracks.datanucleus.client.Consistency;
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ConsistencyLevel;
import org.apache.cassandra.thrift.IndexClause;
import org.apache.cassandra.thrift.IndexExpression;
import org.apache.cassandra.thrift.IndexOperator;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;
import org.datanucleus.exceptions.NucleusException;
import org.scale7.cassandra.pelops.Bytes;
import org.scale7.cassandra.pelops.Pelops;
import org.scale7.cassandra.pelops.Selector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Todd Nine
 * 
 */
public class EqualityOperand extends Operand implements CompressableOperand {

    private static final Logger logger = LoggerFactory.getLogger(EqualityOperand.class);

    private IndexClause clause;

    /** True if the equality expression is on a field which has a cassandra secondary index. */
    private boolean isIndexed;

    public EqualityOperand(int count) {
        clause = new IndexClause();
        clause.setStart_key(new byte[] {});
        clause.setCount(count);
        clause.setExpressions(new ArrayList<IndexExpression>());//TODO Remove
        candidateKeys = new LinkedHashSet<Columns>();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.spidertracks.datanucleus.query.runtime.Operand#complete(com.spidertracks
     * .datanucleus.query.runtime.Operand)
     */
    @Override
    public void complete(Operand child) {
        throw new UnsupportedOperationException("Equality operands should have no children");
    }

    /**
     * Add the index expression to the clause
     * 
     * @param expression the expression to check.
     * @param isIndexed true if there is a secondary index on the field to check equality for.
     */
    public void addExpression(final IndexExpression expression, final boolean isIndexed) {
        this.isIndexed |= isIndexed;
        this.clause.addToExpressions(expression);

        if (logger.isDebugEnabled()) {
            logger.debug("Adding clause for name: {} value: {}",
                    new String(Hex.encodeHex(expression.getColumn_name())),
                    new String(Hex.encodeHex(expression.getValue())));
        }
    }

    /**
     * Add all expression to the index clause
     * 
     * @param expressions
     * @param isIndexed true if there is a secondary index on one of the fields to check
     *                  equality for.
     */
    public void addAll(List<IndexExpression> expressions, final boolean isIndexed) {
        this.isIndexed |= isIndexed;
        for (IndexExpression expr : expressions) {
            clause.addToExpressions(expr);

            if (logger.isDebugEnabled()) {
                logger.debug("Adding clause for name: {} value: {}",
                        new String(Hex.encodeHex(expr.getColumn_name())),
                        new String(Hex.encodeHex(expr.getValue())));
            }

        }
    }

    @Override
    public IndexClause getIndexClause() {
        return clause;
    }

    @Override
    public void performQuery(String poolName, String cfName, Bytes[] columns) {

        try {
            Map<Bytes, List<Column>> results = Pelops.createSelector(poolName).getIndexedColumns(cfName, clause,
                    Selector.newColumnsPredicate(columns), Consistency.get());
            Columns cols;

            for (Entry<Bytes, List<Column>> entry : results.entrySet()) {

                if (entry.getValue().size() == 0) {
                    continue;
                }

                cols = new Columns(entry.getKey());

                for (Column currentCol : entry.getValue()) {

                    cols.addResult(currentCol);
                }

                super.candidateKeys.add(cols);
            }

        } catch (Exception e) {
            throw new NucleusException("Error processing secondary index", e);
        }

        // signal to the parent node the query completed
        if (parent != null) {
            parent.complete(this);
        }

    }

    @Override
    public Operand optimizeDescriminator(Bytes descriminatorColumnValue, List<Bytes> possibleValues) {

        // the equality node is always a leaf, so we don't need to recurse

        if (possibleValues.size() == 1) {

            IndexExpression leaf = new IndexExpression();

            leaf.setColumn_name(descriminatorColumnValue.getBytes());

            leaf.setValue(possibleValues.get(0).getBytes());

            leaf.setOp(IndexOperator.EQ);

            // discriminator fields are always indexed.
            addExpression(leaf, true);

            return this;
        }

        Stack<EqualityOperand> eqOps = new Stack<EqualityOperand>();

        Stack<OrOperand> orOps = new Stack<OrOperand>();

        for (Bytes value : possibleValues) {

            if (orOps.size() == 2) {
                OrOperand orOp = new OrOperand();
                orOp.setLeft(orOps.pop());
                orOp.setRight(orOps.pop());

                orOps.push(orOp);
            }

            if (eqOps.size() == 2) {
                OrOperand orOp = new OrOperand();
                orOp.setLeft(eqOps.pop());
                orOp.setRight(eqOps.pop());
                orOps.push(orOp);
            }

            EqualityOperand subClass = new EqualityOperand(clause.getCount());

            // add the existing clause
            subClass.addAll(this.getIndexClause().getExpressions(), this.isIndexed());

            IndexExpression expression = new IndexExpression();

            expression.setColumn_name(descriminatorColumnValue.getBytes());

            expression.setValue(value.getBytes());

            expression.setOp(IndexOperator.EQ);

            // now add the discriminator, discriminator is always indexed.
            subClass.addExpression(expression, true);

            // push onto the stack
            eqOps.push(subClass);

        }

        // only rewritten without needing to OR to other clauses, short circuit

        while (eqOps.size() > 0) {

            OrOperand orOp = new OrOperand();

            if (eqOps.size() % 2 == 0) {
                orOp.setLeft(eqOps.pop());
                orOp.setRight(eqOps.pop());

            }

            else {
                orOp.setLeft(eqOps.pop());
                orOp.setRight(orOps.pop());
            }

            orOps.push(orOp);
        }

        while (orOps.size() > 1) {

            OrOperand orOp = new OrOperand();

            orOp.setLeft(orOps.pop());
            orOp.setRight(orOps.pop());

            orOps.push(orOp);
        }

        // check if there's anything left in the eqOps.

        return orOps.pop();

    }

    @Override
    public void toString(final StringBuilder sb) {
        final List<IndexExpression> expList = this.clause.getExpressions();
        int i;
        for (i = 0; i < expList.size(); i++) {
            if (i > 0) {
                sb.append(" AND ");
            }
            final IndexExpression exp = expList.get(i);
            sb.append(new String(exp.column_name.array()));
            switch (exp.op) {
            case EQ:
                sb.append(" = ");
                break;
            case GTE:
                sb.append(" >= ");
                break;
            case GT:
                sb.append(" > ");
                break;
            case LTE:
                sb.append(" <= ");
                break;
            case LT:
                sb.append(" < ");
                break;
            default:
                throw new RuntimeException("Unhandled operand [" + exp.op + "]");
            }
            ;

            final String val = new String(exp.value.array());

            if (!StringUtils.isAsciiPrintable(val)) {
                sb.append("hex('");
                sb.append(new String(Hex.encodeHex(exp.value.array())));
                sb.append("')");
            } else {
                sb.append('\'').append(val.replace("\\", "\\\\").replace("'", "\\'")).append('\'');
            }
        }
        sb.append(" ");
    }

    @Override
    public boolean isIndexed() {
        return this.isIndexed;
    }
}