Package com.impetus.client.cassandra.query

Source Code of com.impetus.client.cassandra.query.CassQuery

/*******************************************************************************
* * Copyright 2012 Impetus Infotech.
*  *
*  * 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.
******************************************************************************/
package com.impetus.client.cassandra.query;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javassist.Modifier;

import javax.persistence.Transient;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EmbeddableType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import org.apache.cassandra.thrift.IndexClause;
import org.apache.cassandra.thrift.IndexExpression;
import org.apache.cassandra.thrift.IndexOperator;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.impetus.client.cassandra.CassandraClientBase;
import com.impetus.client.cassandra.common.CassandraUtilities;
import com.impetus.client.cassandra.index.CassandraIndexHelper;
import com.impetus.client.cassandra.thrift.CQLTranslator;
import com.impetus.kundera.Constants;
import com.impetus.kundera.client.Client;
import com.impetus.kundera.client.ClientBase;
import com.impetus.kundera.client.EnhanceEntity;
import com.impetus.kundera.metadata.KunderaMetadataManager;
import com.impetus.kundera.metadata.MetadataUtils;
import com.impetus.kundera.metadata.model.ApplicationMetadata;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.MetamodelImpl;
import com.impetus.kundera.metadata.model.attributes.AbstractAttribute;
import com.impetus.kundera.metadata.model.type.AbstractManagedType;
import com.impetus.kundera.persistence.EntityManagerFactoryImpl.KunderaMetadata;
import com.impetus.kundera.persistence.EntityReader;
import com.impetus.kundera.persistence.PersistenceDelegator;
import com.impetus.kundera.property.PropertyAccessorHelper;
import com.impetus.kundera.query.KunderaQuery;
import com.impetus.kundera.query.KunderaQuery.FilterClause;
import com.impetus.kundera.query.KunderaQuery.SortOrdering;
import com.impetus.kundera.query.KunderaQuery.UpdateClause;
import com.impetus.kundera.query.QueryHandlerException;
import com.impetus.kundera.query.QueryImpl;
import com.impetus.kundera.utils.ReflectUtils;

/**
* @author vivek.mishra
*
*         Query implementation for Cassandra.
*/
@SuppressWarnings("unchecked")
public class CassQuery extends QueryImpl
{

    /** the log used by this class. */
    private static Logger log = LoggerFactory.getLogger(CassQuery.class);

    /** The reader. */
    private EntityReader reader;

    private Map<String, Object> externalProperties;

    /**
     * Instantiates a new cass query.
     *
     * @param query
     *            the query
     * @param kunderaQuery
     *            the kundera query
     * @param persistenceDelegator
     *            the persistence delegator
     */
    public CassQuery(KunderaQuery kunderaQuery, PersistenceDelegator persistenceDelegator,
            final KunderaMetadata kunderaMetadata)
    {
        super(kunderaQuery, persistenceDelegator, kunderaMetadata);
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.impetus.kundera.query.QueryImpl#populateEntities(com.impetus.kundera
     * .metadata.model.EntityMetadata, com.impetus.kundera.client.Client)
     */
    @Override
    protected List<Object> populateEntities(EntityMetadata m, Client client)
    {
        if (log.isDebugEnabled())
        {
            log.debug("Populating entities for Cassandra query {}.", getJPAQuery());
        }
        List<Object> result = new ArrayList<Object>();
        ApplicationMetadata appMetadata = kunderaMetadata.getApplicationMetadata();
        externalProperties = ((CassandraClientBase) client).getExternalProperties();

        // if id attribute is embeddable, it is meant for CQL translation.
        // make it independent of embedded stuff and allow even to add non
        // composite into where clause and let cassandra complain for it.

        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                m.getPersistenceUnit());

        String query = appMetadata.getQuery(getJPAQuery());
        boolean isNative = kunderaQuery.isNative();

        if (!isNative && ((CassandraClientBase) client).isCql3Enabled(m)
                && MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()))
        {
            result = ((CassandraClientBase) client).executeQuery(m.getEntityClazz(), null, isNative,
                    onQueryOverCQL3(m, client, metaModel, null));
        }
        else
        {
            if (isNative)
            {
                result = ((CassandraClientBase) client).executeQuery(m.getEntityClazz(), null, isNative,
                        query != null ? query : getJPAQuery());
            }
            else
            {
                if (MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()))
                {
                    // Index in Inverted Index table if applicable
                    boolean useInvertedIndex = CassandraIndexHelper.isInvertedIndexingApplicable(m,
                            MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()));
                    Map<Boolean, List<IndexClause>> ixClause = prepareIndexClause(m, useInvertedIndex);
                    if (useInvertedIndex && !getKunderaQuery().getFilterClauseQueue().isEmpty())
                    {
                        result = (List) ((CassandraEntityReader) getReader()).readFromIndexTable(m, client, ixClause);
                    }
                    else
                    {
                        boolean isRowKeyQuery = ixClause.keySet().iterator().next();
                        if (!isRowKeyQuery)
                        {
                            result = ((CassandraClientBase) client).find(ixClause.get(isRowKeyQuery), m, false, null,
                                    isSingleResult ? 1 : this.maxResult,
                                    getColumnList(m, metaModel, getKunderaQuery().getResult(), null));
                        }
                        else
                        {
                            result = ((CassandraEntityReader) getReader()).handleFindByRange(m, client, result,
                                    ixClause, isRowKeyQuery,
                                    getColumnList(m, metaModel, getKunderaQuery().getResult(), null),
                                    isSingleResult ? 1 : this.maxResult);
                        }
                    }
                }
                else
                {
                    result = populateUsingLucene(m, client, result, getKunderaQuery().getResult());
                }
            }
        }
        return result;
    }

    protected List findUsingLucene(EntityMetadata m, Client client)
    {
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                m.getPersistenceUnit());
        boolean useInvertedIndex = CassandraIndexHelper.isInvertedIndexingApplicable(m,
                MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()));
        Map<Boolean, List<IndexClause>> ixClause = prepareIndexClause(m, useInvertedIndex);
        List<Object> result = new ArrayList<Object>();
        if (((CassandraClientBase) client).isCql3Enabled(m))
        {
            result = ((CassandraClientBase) client).executeQuery(m.getEntityClazz(), null, false,
                    onQueryOverCQL3(m, client, metaModel, null));
        }
        else
        {
            result = ((CassandraEntityReader) getReader()).handleFindByRange(m, client, result, ixClause, true,
                    getColumnList(m, metaModel, getKunderaQuery().getResult(), null), isSingleResult ? 1
                            : this.maxResult);
        }
        return result;
    }

    /**
     * (non-Javadoc)
     *
     * @see com.impetus.kundera.query.QueryImpl#recursivelyPopulateEntities(com.impetus
     *      .kundera.metadata.model.EntityMetadata,
     *      com.impetus.kundera.client.Client)
     */
    @SuppressWarnings("unchecked")
    @Override
    protected List<Object> recursivelyPopulateEntities(EntityMetadata m, Client client)
    {
        List<EnhanceEntity> ls = null;
        ApplicationMetadata appMetadata = kunderaMetadata.getApplicationMetadata();
        externalProperties = ((CassandraClientBase) client).getExternalProperties();
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                m.getPersistenceUnit());

        String query = appMetadata.getQuery(getJPAQuery());
        boolean isNative = kunderaQuery.isNative();

        if (isNative)
        {
            ls = (List<EnhanceEntity>) ((CassandraClientBase) client).executeQuery(m.getEntityClazz(), null, isNative,
                    query != null ? query : getJPAQuery());
        }
        else if (!isNative && ((CassandraClientBase) client).isCql3Enabled(m))
        {
            ls = ((CassandraClientBase) client).executeQuery(m.getEntityClazz(), m.getRelationNames(), isNative,
                    onQueryOverCQL3(m, client, metaModel, m.getRelationNames()));
        }
        else
        {
            // Index in Inverted Index table if applicable
            boolean useInvertedIndex = CassandraIndexHelper.isInvertedIndexingApplicable(m,
                    MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()));
            Map<Boolean, List<IndexClause>> ixClause = MetadataUtils.useSecondryIndex(((ClientBase) client)
                    .getClientMetadata()) ? prepareIndexClause(m, useInvertedIndex) : null;

            if (useInvertedIndex && !getKunderaQuery().getFilterClauseQueue().isEmpty())
            {
                ls = ((CassandraEntityReader) getReader()).readFromIndexTable(m, client, ixClause);
            }
            else
            {
                ((CassandraEntityReader) getReader()).setConditions(ixClause);
                ls = reader.populateRelation(m, client, isSingleResult ? 1 : this.maxResult);
            }
        }
        return setRelationEntities(ls, client, m);
    }

    /**
     * On executeUpdate.
     *
     * @return zero
     */
    @Override
    protected int onExecuteUpdate()
    {
        EntityMetadata m = getEntityMetadata();
        externalProperties = ((CassandraClientBase) persistenceDelegeator.getClient(m)).getExternalProperties();
        ApplicationMetadata appMetadata = kunderaMetadata.getApplicationMetadata();

        String query = appMetadata.getQuery(getJPAQuery());

        boolean isNative = kunderaQuery.isNative();

        if (isNative)
        {
            ((CassandraClientBase) persistenceDelegeator.getClient(m)).executeQuery(m.getEntityClazz(), null, isNative,
                    query != null ? query : getJPAQuery());
        }
        else if (kunderaQuery.isDeleteUpdate())
        {
            // If query is not convertible to CQL, fetch and merge records usual
            // way, otherwise
            // convert to CQL and execute
            if (!isQueryConvertibleToCQL(kunderaQuery))
            {
                return onUpdateDeleteEvent();
            }
            else
            {
                query = null;
                if (kunderaQuery.isUpdateClause())
                {
                    query = createUpdateQuery(kunderaQuery);
                }
                else
                {
                    query = createDeleteQuery(kunderaQuery);
                }
                return ((CassandraClientBase) persistenceDelegeator.getClient(m)).executeUpdateDeleteQuery(query);
            }
        }
        return 0;
    }

    /**
     * Checks whether a given JPA DML query is convertible to CQL
     *
     * @param m
     * @return
     */
    private boolean isQueryConvertibleToCQL(KunderaQuery kunderaQuery)
    {
        EntityMetadata m = kunderaQuery.getEntityMetadata();
        if (kunderaQuery.isUpdateClause() && m.isCounterColumnType())
            return false;

        List<String> opsNotAllowed = Arrays.asList(new String[] { ">", "<", ">=", "<=" });
        boolean result = false;
        if (!kunderaQuery.getFilterClauseQueue().isEmpty())
        {
            String idColumn = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName();
            for (Object o : kunderaQuery.getFilterClauseQueue())
            {
                if (o instanceof FilterClause)
                {
                    FilterClause filterClause = (FilterClause) o;
                    if (!idColumn.equals(filterClause.getProperty())
                            || opsNotAllowed.contains(filterClause.getCondition()))
                    {
                        result = false;
                        break;
                    }
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * Gets the column list.
     *
     * @param m
     *            the m
     * @param results
     *            the results
     * @return the column list
     */
    List<String> getColumnList(EntityMetadata m, MetamodelImpl metamodel, String[] results, EmbeddableType compoundKey)
    {
        List<String> columns = new ArrayList<String>();
        if (results != null && results.length > 0)
        {
            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                    m.getPersistenceUnit());
            EntityType entity = metaModel.entity(m.getEntityClazz());

            String keyFieldName = CassandraUtilities.getIdColumnName(kunderaMetadata, m, externalProperties,
                    ((CassandraClientBase) persistenceDelegeator.getClient(m)).isCql3Enabled(m));
            for (int i = 1; i < results.length; i++)
            {
                if (results[i] != null)
                {
                    if (results[i].indexOf(".") > 0)
                    {
                        String fieldName = results[i].substring(0, results[i].indexOf("."));
                        String embeddedFieldName = results[i].substring(results[i].indexOf(".") + 1,
                                results[i].length());
                        AbstractAttribute attribute = (AbstractAttribute) entity.getAttribute(fieldName);
                        EmbeddableType embeddable = metamodel.embeddable(attribute.getBindableJavaType());
                        Attribute embeddableAttribute = embeddable.getAttribute(embeddedFieldName);
                        columns.add(((AbstractAttribute) embeddableAttribute).getJPAColumnName());
                    }
                    else
                    {
                        Attribute attribute = entity.getAttribute(results[i]);
                        if (attribute == null)
                        {
                            throw new QueryHandlerException("Column type is null for : " + results);
                        }
                        else if (m.getIdAttribute().equals(attribute) && compoundKey != null)
                        {
                            Field[] fields = m.getIdAttribute().getBindableJavaType().getDeclaredFields();
                            for (Field field : fields)
                            {
                                if (!ReflectUtils.isTransientOrStatic(field))
                                {
                                    Attribute compositeColumn = compoundKey.getAttribute(field.getName());
                                    columns.add(((AbstractAttribute) compositeColumn).getJPAColumnName());
                                }
                            }
                        }
                        else if (m.getIdAttribute().equals(attribute) && compoundKey == null)
                        {
                            columns.add(keyFieldName);
                        }
                        else
                        {
                            columns.add(((AbstractAttribute) attribute).getJPAColumnName());
                        }
                    }
                }
            }
            return columns;
        }

        if (log.isInfoEnabled())
        {
            log.info("No record found, returning null.");
        }
        return null;
    }

    /**
     * Prepare index clause.
     *
     * @param m
     *            the m
     * @param isQueryForInvertedIndex
     *            the is query for inverted index
     * @return the map
     */
    Map<Boolean, List<IndexClause>> prepareIndexClause(EntityMetadata m, boolean isQueryForInvertedIndex)
    {
        IndexClause indexClause = new IndexClause(new ArrayList<IndexExpression>(), ByteBufferUtil.EMPTY_BYTE_BUFFER,
                maxResult);

        List<IndexClause> clauses = new ArrayList<IndexClause>();
        List<IndexExpression> expr = new ArrayList<IndexExpression>();

        Map<Boolean, List<IndexClause>> idxClauses = new HashMap<Boolean, List<IndexClause>>(1);
        // check if id column are mixed with other columns or not?
        String idColumn = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName();
        boolean idPresent = false;

        if (log.isInfoEnabled())
        {
            log.info("Preparing index clause for query {}", getJPAQuery());
        }

        for (Object o : getKunderaQuery().getFilterClauseQueue())
        {
            if (o instanceof FilterClause)
            {
                FilterClause clause = ((FilterClause) o);
                String fieldName = clause.getProperty();
                // in case id column matches with field name, set it for first
                // time.
                if (!idPresent && idColumn.equalsIgnoreCase(fieldName))
                {
                    idPresent = true;
                }

                String condition = clause.getCondition();
                List<Object> value = clause.getValue();
                if (value != null && value.size() > 1)
                {
                    log.error("IN clause is not enabled for thrift, use cql3.");
                    throw new QueryHandlerException("IN clause is not enabled for thrift, use cql3.");
                }
                IndexOperator operator = getOperator(condition, idPresent);

                IndexExpression expression = new IndexExpression(ByteBufferUtil.bytes(fieldName), operator,
                        getBytesValue(fieldName, m, value.get(0)));

                expr.add(expression);
            }
            else
            {
                // Case of AND and OR clause.
                String opr = o.toString();
                if (opr.equalsIgnoreCase("or"))
                {
                    log.error("Support for OR clause is not enabled within cassandra.");
                    throw new QueryHandlerException("Unsupported clause " + opr + " for cassandra.");
                }
            }
        }

        if (!StringUtils.isBlank(getKunderaQuery().getFilter()))
        {
            indexClause.setExpressions(expr);
            clauses.add(indexClause);
        }
        idxClauses.put(idPresent, clauses);

        return idxClauses;
    }

    /**
     * Gets the operator.
     *
     * @param condition
     *            the condition
     * @param idPresent
     *            the id present
     * @return the operator
     */
    private IndexOperator getOperator(String condition, boolean idPresent)
    {
        if (/* !idPresent && */condition.equals("="))
        {
            return IndexOperator.EQ;
        }
        else if (/* !idPresent && */condition.equals(">"))
        {
            return IndexOperator.GT;
        }
        else if (/* !idPresent && */condition.equals("<"))
        {
            return IndexOperator.LT;
        }
        else if (condition.equals(">="))
        {
            return IndexOperator.GTE;
        }
        else if (condition.equals("<="))
        {
            return IndexOperator.LTE;
        }
        else
        {
            if (!idPresent)
            {
                throw new UnsupportedOperationException("Condition " + condition + " is not suported in  cassandra.");
            }
            else
            {
                throw new UnsupportedOperationException("Condition " + condition
                        + " is not suported for query on row key.");
            }
        }

    }

    /*
     * (non-Javadoc)
     *
     * @see com.impetus.kundera.query.QueryImpl#getReader()
     */
    @Override
    protected EntityReader getReader()
    {
        if (reader == null)
        {
            reader = new CassandraEntityReader(kunderaQuery, kunderaMetadata);
        }
        return reader;
    }

    /**
     * Returns bytes value for given value.
     *
     * @param jpaFieldName
     *            field name.
     * @param m
     *            entity metadata
     * @param value
     *            value.
     * @return bytes value.
     */
    ByteBuffer getBytesValue(String jpaFieldName, EntityMetadata m, Object value)
    {
        Attribute idCol = m.getIdAttribute();
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                m.getPersistenceUnit());

        EntityType entity = metaModel.entity(m.getEntityClazz());
        Field f = null;
        boolean isId = false;
        if (((AbstractAttribute) idCol).getJPAColumnName().equals(jpaFieldName))
        {
            f = (Field) idCol.getJavaMember();
            isId = true;
        }
        else
        {
            if (jpaFieldName != null && jpaFieldName.indexOf(Constants.INDEX_TABLE_ROW_KEY_DELIMITER) > 0)
            {
                String embeddedFieldName = jpaFieldName.substring(0,
                        jpaFieldName.indexOf(Constants.INDEX_TABLE_ROW_KEY_DELIMITER));
                String columnFieldName = jpaFieldName.substring(
                        jpaFieldName.indexOf(Constants.INDEX_TABLE_ROW_KEY_DELIMITER) + 1, jpaFieldName.length());

                Attribute embeddedAttr = entity.getAttribute(embeddedFieldName);
                try
                {
                    Class<?> embeddedClass = embeddedAttr.getJavaType();
                    if (Collection.class.isAssignableFrom(embeddedClass))
                    {
                        Class<?> genericClass = PropertyAccessorHelper.getGenericClass((Field) embeddedAttr
                                .getJavaMember());
                        f = genericClass.getDeclaredField(columnFieldName);
                    }
                    else
                    {
                        f = embeddedClass.getDeclaredField(columnFieldName);
                    }

                }
                catch (SecurityException e)
                {
                    log.error("Error while extrating " + jpaFieldName + ", Caused by: ", e);
                    throw new QueryHandlerException("Error while extrating " + jpaFieldName + ".");
                }
                catch (NoSuchFieldException e)
                {
                    log.error("Error while extrating " + jpaFieldName + ", Caused by: ", e);
                    throw new QueryHandlerException("Error while extrating " + jpaFieldName + ".");
                }
            }
            else
            {
                String discriminatorColumn = ((AbstractManagedType) entity).getDiscriminatorColumn();

                if (!jpaFieldName.equals(discriminatorColumn))
                {
                    String fieldName = m.getFieldName(jpaFieldName);

                    Attribute col = entity.getAttribute(fieldName);
                    if (col == null)
                    {
                        throw new QueryHandlerException("column type is null for: " + jpaFieldName);
                    }
                    f = (Field) col.getJavaMember();
                }
            }
        }

        // need to do integer.parseInt..as value will be string in case of
        // create query.
        if (f != null && f.getType() != null)
        {
            return CassandraUtilities.toBytes(value, f);
        }
        else
        {
            // default is String type
            return CassandraUtilities.toBytes(value, String.class);
        }
    }

    /**
     * On query over composite columns.
     *
     * @param m
     *            the m
     * @param client
     *            the client
     * @param metaModel
     *            the meta model
     * @return the list
     */
    String onQueryOverCQL3(EntityMetadata m, Client client, MetamodelImpl metaModel, List<String> relations)
    {
        // select column will always be of entity field only!
        // where clause ordering

        Class compoundKeyClass = m.getIdAttribute().getBindableJavaType();
        EmbeddableType compoundKey = null;
        String idColumn;
        if (metaModel.isEmbeddable(compoundKeyClass))
        {
            compoundKey = metaModel.embeddable(compoundKeyClass);
            idColumn = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName();
        }
        else
        {
            idColumn = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName();
        }
        StringBuilder builder = new StringBuilder();

        boolean isPresent = false;
        List<String> columns = getColumnList(m, metaModel, getKunderaQuery().getResult(), compoundKey);
        String selectQuery = columns != null && !columns.isEmpty() ? CQLTranslator.SELECT_QUERY
                : CQLTranslator.SELECTALL_QUERY;

        CQLTranslator translator = new CQLTranslator();

        selectQuery = StringUtils.replace(selectQuery, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), m.getTableName(), false).toString());

        builder = CassandraUtilities.appendColumns(builder, columns, selectQuery, translator);

        addWhereClause(builder);

        onCondition(m, metaModel, compoundKey, idColumn, builder, isPresent, translator, true);

        return builder.toString();
    }

    /**
     * Add provided max result limit.
     *
     * @param builder
     *            string builder.
     */
    private void onLimit(StringBuilder builder)
    {
        if (Integer.MAX_VALUE != maxResult)
        {
            builder.append(CQLTranslator.LIMIT);
            builder.append(isSingleResult ? 1 : this.maxResult);
        }
    }

    /**
     * On condition.
     *
     * @param m
     *            the m
     * @param metaModel
     *            the meta model
     * @param keyObj
     *            the compound key
     * @param idColumn
     *            the id column
     * @param builder
     *            the builder
     * @param isPresent
     *            the is present
     * @param translator
     *            the translator
     * @return true, if successful
     */
    private boolean onCondition(EntityMetadata m, MetamodelImpl metaModel, EmbeddableType keyObj, String idColumn,
            StringBuilder builder, boolean isPresent, CQLTranslator translator, boolean use)
    {
        String partitionKey = null;
        boolean allowFiltering = false;
        for (Object o : getKunderaQuery().getFilterClauseQueue())
        {
            if (o instanceof FilterClause)
            {
                FilterClause clause = ((FilterClause) o);
                String fieldName = clause.getProperty();
                String condition = clause.getCondition();
                List<Object> value = clause.getValue();
                boolean useInClause = condition.trim().equalsIgnoreCase("IN");

                // if compound key field is given in where clause.
                isPresent = true;

                if (keyObj != null && idColumn.equals(fieldName))
                {
                    Field[] fields = m.getIdAttribute().getBindableJavaType().getDeclaredFields();

                    Map<Attribute, List<Object>> columnValues = new HashMap<Attribute, List<Object>>();

                    for (Field field : fields)
                    {
                        if (!ReflectUtils.isTransientOrStatic(field))
                        {
                            extractCompositeKey(metaModel, keyObj, builder, translator, value, useInClause,
                                    columnValues, field);
                        }
                    }

                    // Composite key always contains clusterKey.
                    allowFiltering = true;
                    if (useInClause)
                    {
                        for (Attribute columnAttribute : columnValues.keySet())
                        {
                            isPresent = appendInClause(builder, translator, columnValues.get(columnAttribute),
                                    ((AbstractAttribute) columnAttribute).getBindableJavaType(),
                                    ((AbstractAttribute) columnAttribute).getJPAColumnName(), isPresent);
                        }
                    }
                }
                else if (keyObj != null && metaModel.isEmbeddable(m.getIdAttribute().getBindableJavaType())
                        && StringUtils.contains(fieldName, '.'))
                {
                    // Means it is a case of composite column.
                    isPresent = getCompoundKeyColumn(metaModel, keyObj, builder, isPresent, translator, fieldName,
                            condition, value, useInClause);

                    allowFiltering = true;
                }
                else if (idColumn.equals(fieldName))
                {
                    isPresent = buildWhereClause(builder, isPresent, translator, condition, value, useInClause,
                            ((AbstractAttribute) m.getIdAttribute()), CassandraUtilities.getIdColumnName(
                                    kunderaMetadata, m, externalProperties,
                                    ((CassandraClientBase) persistenceDelegeator.getClient(m)).isCql3Enabled(m)), use);
                }
                else
                {
                    Metamodel metamodel = KunderaMetadataManager.getMetamodel(kunderaMetadata, m.getPersistenceUnit());
                    Attribute attribute = ((MetamodelImpl) metamodel).getEntityAttribute(m.getEntityClazz(),
                            m.getFieldName(fieldName));

                    isPresent = buildWhereClause(builder, isPresent, translator, condition, value, useInClause,
                            ((AbstractAttribute) attribute), fieldName, false);

                    allowFiltering = true;
                }
            }
        }

        // Strip last AND clause.
        if (isPresent)
        {
            builder.delete(builder.lastIndexOf(CQLTranslator.AND_CLAUSE), builder.length());
        }

        // Append arder by clause into query
        builder = appendOrderByClause(metaModel, m, keyObj, builder, translator);

        if (allowFiltering && use)
        {
            onLimit(builder);
            builder.append(" ");
            translator.buildFilteringClause(builder);
        }
        else if (use)
        {
            onLimit(builder);
        }

        return isPresent;
    }

    /**
     *
     * @param metamodel
     * @param keyObj
     * @param builder
     * @param isPresent
     * @param translator
     * @param fieldName
     * @param condition
     * @param value
     * @param useInClause
     * @return
     */
    private boolean getCompoundKeyColumn(MetamodelImpl metamodel, EmbeddableType keyObj, StringBuilder builder,
            boolean isPresent, CQLTranslator translator, String fieldName, String condition, List<Object> value,
            boolean useInClause)
    {
        fieldName = fieldName.substring(fieldName.indexOf(".") + 1);

        // If partition key part age given in query, i.e. restriction on
        // id.compositekey.compositePartitionkey.partitionkeyColumn.
        if (fieldName.indexOf(".") > 0)
        {
            String compositePartitionkeyName = fieldName.substring(0, fieldName.indexOf("."));
            AbstractAttribute attribute = (AbstractAttribute) keyObj.getAttribute(compositePartitionkeyName);
            fieldName = fieldName.substring(fieldName.indexOf(".") + 1);

            EmbeddableType compositePartitionkey = metamodel.embeddable(attribute.getBindableJavaType());

            attribute = (AbstractAttribute) compositePartitionkey.getAttribute(fieldName);

            String columnName = attribute.getJPAColumnName();

            isPresent = buildWhereClause(builder, isPresent, translator, condition, value, useInClause, attribute,
                    columnName, false);
        }
        // if composite partition key object is given in query, i.e. restriction
        // on id.compositekey.compositePartitionkey
        else if (metamodel.isEmbeddable(((AbstractAttribute) keyObj.getAttribute(fieldName)).getBindableJavaType()))
        {
            AbstractAttribute attribute = (AbstractAttribute) keyObj.getAttribute(fieldName);
            Set<Attribute> attributes = metamodel.embeddable(attribute.getBindableJavaType()).getAttributes();

            if (!useInClause)
            {
                // Iterating and appending each column of composite partition
                // key in query builder.
                for (Attribute nestedAttribute : attributes)
                {
                    String columnName = ((AbstractAttribute) nestedAttribute).getJPAColumnName();
                    Object valueObject = PropertyAccessorHelper.getObject(value.isEmpty() ? null : value.get(0),
                            (Field) nestedAttribute.getJavaMember());
                    translator.buildWhereClause(builder, nestedAttribute.getJavaType(), columnName, valueObject,
                            condition, false);
                }
            }
            else
            {
                throw new IllegalArgumentException("In clause is not supported on first part of partition key.");
            }
            isPresent = true;
        }
        // if Not a composite partition key,
        // id.compositekey.partitionkey/clusterKey.
        else
        {
            AbstractAttribute attribute = (AbstractAttribute) keyObj.getAttribute(fieldName);
            String columnName = attribute.getJPAColumnName();
            isPresent = buildWhereClause(builder, isPresent, translator, condition, value, useInClause, attribute,
                    columnName, false);
        }
        return isPresent;
    }

    /**
     *
     * @param metaModel
     * @param keyObj
     * @param builder
     * @param translator
     * @return
     */
    private StringBuilder appendOrderByClause(MetamodelImpl metaModel, EntityMetadata m, EmbeddableType keyObj,
            StringBuilder builder, CQLTranslator translator)
    {
        List<SortOrdering> orders = getKunderaQuery().getOrdering();

        if (orders != null)
        {
            builder.append(CQLTranslator.SPACE_STRING);
            builder.append(CQLTranslator.SORT_CLAUSE);

            for (SortOrdering order : orders)
            {
                String orderColumnName = order.getColumnName();

                orderColumnName = orderColumnName.substring(orderColumnName.indexOf(".") + 1, orderColumnName.length());

                String orderByColumnName;

                if (StringUtils.contains(orderColumnName, '.'))
                {
                    String propertyName = orderColumnName.substring(0, orderColumnName.indexOf("."));
                    Attribute embeddableAttribute = metaModel.getEntityAttribute(m.getEntityClazz(), propertyName);
                    EmbeddableType embeddableType = metaModel.embeddable(((AbstractAttribute) embeddableAttribute)
                            .getBindableJavaType());
                    orderColumnName = orderColumnName.substring(orderColumnName.indexOf(".") + 1);
                    AbstractAttribute attribute = (AbstractAttribute) embeddableType.getAttribute(orderColumnName);
                    orderByColumnName = attribute.getJPAColumnName();

                }
                else
                {
                    Attribute attribute = metaModel.getEntityAttribute(m.getEntityClazz(), orderColumnName);
                    orderByColumnName = ((AbstractAttribute) attribute).getJPAColumnName();
                }

                builder = translator.ensureCase(builder, orderByColumnName, false);
                builder.append(CQLTranslator.SPACE_STRING);
                builder.append(order.getOrder());
                builder.append(CQLTranslator.COMMA_STR);
            }

            if (!orders.isEmpty())
            {
                builder.deleteCharAt(builder.lastIndexOf(CQLTranslator.COMMA_STR));
            }
        }
        return builder;
    }

    /**
     *
     * @param keyObj
     * @param builder
     * @param translator
     * @param value
     * @param useInClause
     * @param columnValues
     * @param field
     * @return
     */
    private boolean extractCompositeKey(MetamodelImpl metaModel, EmbeddableType keyObj, StringBuilder builder,
            CQLTranslator translator, List<Object> value, boolean useInClause,
            Map<Attribute, List<Object>> columnValues, Field field)
    {
        Attribute compositeColumn = keyObj.getAttribute(field.getName());
        String jpaColumnName = ((AbstractAttribute) compositeColumn).getJPAColumnName();

        if (useInClause)
        {
            for (Object embeddedObject : value)
            {

                Object valueObject = PropertyAccessorHelper.getObject(embeddedObject, field);
                // Checking for composite partition key.
                if (metaModel.isEmbeddable(((AbstractAttribute) compositeColumn).getBindableJavaType()))
                {
                    Set<Attribute> attributes = metaModel.embeddable(
                            ((AbstractAttribute) compositeColumn).getBindableJavaType()).getAttributes();

                    // Iterating over composite partition key columns.
                    for (Attribute nestedAttribute : attributes)
                    {
                        List<Object> valueList = columnValues.get(compositeColumn);

                        if (valueList == null)
                        {
                            valueList = new ArrayList<Object>();
                        }

                        Object obj = PropertyAccessorHelper.getObject(valueObject,
                                (Field) nestedAttribute.getJavaMember());
                        valueList.add(obj);
                        columnValues.put(nestedAttribute, valueList);
                    }
                }
                else
                {
                    List<Object> valueList = columnValues.get(compositeColumn);

                    if (valueList == null)
                    {
                        valueList = new ArrayList<Object>();
                    }
                    valueList.add(valueObject);
                    columnValues.put(compositeColumn, valueList);
                }
            }
        }
        else
        {
            Object valueObject = PropertyAccessorHelper.getObject(value.isEmpty() ? null : value.get(0), field);

            // Checking for composite partition key.
            if (metaModel.isEmbeddable(((AbstractAttribute) compositeColumn).getBindableJavaType()))
            {
                Set<Attribute> attributes = metaModel.embeddable(
                        ((AbstractAttribute) compositeColumn).getBindableJavaType()).getAttributes();

                // Iterating over composite partition key columns.
                for (Attribute nestedAttribute : attributes)
                {
                    String columnName = ((AbstractAttribute) nestedAttribute).getJPAColumnName();
                    Object obj = PropertyAccessorHelper.getObject(valueObject, (Field) nestedAttribute.getJavaMember());
                    translator.buildWhereClause(builder, nestedAttribute.getJavaType(), columnName, obj,
                            CQLTranslator.EQ_CLAUSE, false);
                }
                // returning true because builder has AND clause at end.
                return true;
            }
            else
            {
                translator.buildWhereClause(builder, field.getType(), jpaColumnName, valueObject,
                        CQLTranslator.EQ_CLAUSE, false);
                // returning true because builder has AND clause at end.
                return true;
            }
        }
        // returning false because builder does not have AND clause at end.
        return false;
    }

    /**
     *
     * @param builder
     * @param isPresent
     * @param translator
     * @param condition
     * @param value
     * @param useInClause
     * @param idAttributeColumn
     * @param ColumnName
     * @return
     */
    private boolean buildWhereClause(StringBuilder builder, boolean isPresent, CQLTranslator translator,
            String condition, List<Object> value, boolean useInClause, AbstractAttribute idAttributeColumn,
            String columnName, boolean useToken)
    {
        if (value.isEmpty())
        {
            isPresent = appendIn(builder, translator, columnName);
            builder.append("( )");
            builder.append(" AND ");
        }
        else if (useInClause && value.size() > 1)
        {
            isPresent = appendInClause(builder, translator, value, idAttributeColumn.getBindableJavaType(), columnName,
                    isPresent);
        }
        else
        {
            // TODO for partition key in case of embedded key.
            translator.buildWhereClause(builder, idAttributeColumn.getBindableJavaType(), columnName,
                    value.isEmpty() ? null : value.get(0), condition, useToken);
        }
        return isPresent;
    }

    private boolean appendIn(StringBuilder builder, CQLTranslator translator, String columnName)
    {
        boolean isPresent;
        isPresent = true;
        translator.ensureCase(builder, columnName, false);
        builder.append(" IN ");
        return isPresent;
    }

    /**
     *
     * @param queryBuilder
     * @param translator
     * @param value
     * @param fieldClazz
     * @param columnName
     * @param isPresent
     * @return
     */
    private boolean appendInClause(StringBuilder queryBuilder, CQLTranslator translator, List<Object> value,
            Class fieldClazz, String columnName, boolean isPresent)
    {
        isPresent = appendIn(queryBuilder, translator, columnName);
        queryBuilder.append("(");
        for (Object objectvalue : value)
        {
            translator.appendValue(queryBuilder, fieldClazz, objectvalue, isPresent, false);
            queryBuilder.append(", ");
        }

        queryBuilder.deleteCharAt(queryBuilder.lastIndexOf(", "));
        queryBuilder.append(") ");

        queryBuilder.append(" AND ");
        return isPresent;
    }

    /**
     * Adds the where clause.
     *
     * @param builder
     *            the builder
     */
    void addWhereClause(StringBuilder builder)
    {
        if (!getKunderaQuery().getFilterClauseQueue().isEmpty())
        {
            builder.append(CQLTranslator.ADD_WHERE_CLAUSE);
        }
    }

    @Override
    public void close()
    {
        // Nothing to close.
    }

    @Override
    public Iterator iterate()
    {
        if (kunderaQuery.isNative())
        {
            throw new UnsupportedOperationException("Iteration not supported over native queries");
        }
        EntityMetadata m = getEntityMetadata();
        Client client = persistenceDelegeator.getClient(m);
        externalProperties = ((CassandraClientBase) client).getExternalProperties();

        if (!MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()))
        {
            throw new UnsupportedOperationException("Scrolling over cassandra is unsupported for lucene queries");
        }

        return new ResultIterator(this, m, persistenceDelegeator.getClient(m), this.getReader(),
                getFetchSize() != null ? getFetchSize() : this.maxResult, kunderaMetadata);
    }

    void setRelationalEntities(List enhanceEntities, Client client, EntityMetadata m)
    {
        super.setRelationEntities(enhanceEntities, client, m);
    }

    /**
     * Create Update CQL query from a given JPA query.
     *
     * @param kunderaQuery
     * @return
     */
    public String createUpdateQuery(KunderaQuery kunderaQuery)
    {
        EntityMetadata metadata = kunderaQuery.getEntityMetadata();
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                metadata.getPersistenceUnit());

        CQLTranslator translator = new CQLTranslator();
        String update_Query = translator.UPDATE_QUERY;

        String tableName = metadata.getTableName();
        update_Query = StringUtils.replace(update_Query, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), tableName, false).toString());

        StringBuilder builder = new StringBuilder(update_Query);

        Object ttlColumns = ((CassandraClientBase) persistenceDelegeator.getClient(metadata)).getTtlValues().get(
                metadata.getTableName());

        if ((ttlColumns != null && ttlColumns instanceof Integer) || this.ttl != null)
        {
            int ttl = this.ttl != null ? this.ttl : ((Integer) ttlColumns).intValue();
            if (ttl != 0)
            {
                builder.append(" USING TTL ");
                builder.append(ttl);
                builder.append(" ");
            }
        }

        builder.append(CQLTranslator.ADD_SET_CLAUSE);

        for (UpdateClause updateClause : kunderaQuery.getUpdateClauseQueue())
        {

            String property = updateClause.getProperty();

            String jpaColumnName = getColumnName(metadata, property);

            Object value = updateClause.getValue();

            translator.buildSetClause(metadata, builder, jpaColumnName, value);
        }
        builder.delete(builder.lastIndexOf(CQLTranslator.COMMA_STR), builder.length());
        builder.append(CQLTranslator.ADD_WHERE_CLAUSE);

        Class compoundKeyClass = metadata.getIdAttribute().getBindableJavaType();
        EmbeddableType compoundKey = null;
        String idColumn;
        if (metaModel.isEmbeddable(compoundKeyClass))
        {
            compoundKey = metaModel.embeddable(compoundKeyClass);
            idColumn = ((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName();
        }
        else
        {
            idColumn = ((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName();
        }

        onCondition(metadata, metaModel, compoundKey, idColumn, builder, false, translator, false);

        return builder.toString();
    }

    /**
     * Create Delete query from a given JPA query
     *
     * @param kunderaQuery
     * @return
     */
    public String createDeleteQuery(KunderaQuery kunderaQuery)
    {
        EntityMetadata metadata = kunderaQuery.getEntityMetadata();
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                metadata.getPersistenceUnit());
        CQLTranslator translator = new CQLTranslator();
        String delete_query = translator.DELETE_QUERY;

        String tableName = kunderaQuery.getEntityMetadata().getTableName();
        delete_query = StringUtils.replace(delete_query, CQLTranslator.COLUMN_FAMILY,
                translator.ensureCase(new StringBuilder(), tableName, false).toString());

        StringBuilder builder = new StringBuilder(delete_query);
        builder.append(CQLTranslator.ADD_WHERE_CLAUSE);

        Class compoundKeyClass = metadata.getIdAttribute().getBindableJavaType();
        EmbeddableType compoundKey = null;
        String idColumn;
        if (metaModel.isEmbeddable(compoundKeyClass))
        {
            compoundKey = metaModel.embeddable(compoundKeyClass);
            idColumn = ((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName();
        }
        else
        {
            idColumn = ((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName();
        }

        onCondition(metadata, metaModel, compoundKey, idColumn, builder, false, translator, false);

        return builder.toString();
    }

    /**
     * Builds where Clause
     *
     * @param kunderaQuery
     * @param metadata
     * @param metaModel
     * @param translator
     * @param builder
     */
    private void buildWhereClause(KunderaQuery kunderaQuery, EntityMetadata metadata, MetamodelImpl metaModel,
            CQLTranslator translator, StringBuilder builder)
    {
        for (Object clause : kunderaQuery.getFilterClauseQueue())
        {
            FilterClause filterClause = (FilterClause) clause;
            Field f = (Field) metaModel.entity(metadata.getEntityClazz())
                    .getAttribute(metadata.getFieldName(filterClause.getProperty())).getJavaMember();
            String jpaColumnName = getColumnName(metadata, filterClause.getProperty());

            if (metaModel.isEmbeddable(metadata.getIdAttribute().getBindableJavaType()))
            {
                Field[] fields = metadata.getIdAttribute().getBindableJavaType().getDeclaredFields();
                EmbeddableType compoundKey = metaModel.embeddable(metadata.getIdAttribute().getBindableJavaType());
                for (Field field : fields)
                {
                    if (field != null && !Modifier.isStatic(field.getModifiers())
                            && !Modifier.isTransient(field.getModifiers())
                            && !field.isAnnotationPresent(Transient.class))
                    {
                        Attribute attribute = compoundKey.getAttribute(field.getName());
                        String columnName = ((AbstractAttribute) attribute).getJPAColumnName();
                        Object value = PropertyAccessorHelper.getObject(filterClause.getValue().get(0), field);
                        // TODO
                        translator.buildWhereClause(builder, field.getType(), columnName, value,
                                filterClause.getCondition(), false);
                    }
                }
            }
            else
            {
                translator.buildWhereClause(builder, f.getType(), jpaColumnName, filterClause.getValue().get(0),
                        filterClause.getCondition(), false);
            }
        }
        builder.delete(builder.lastIndexOf(CQLTranslator.AND_CLAUSE), builder.length());
    }

    /**
     * Gets column name for a given field name
     *
     * @param metadata
     * @param metaModel
     * @param property
     * @return
     */
    private String getColumnName(EntityMetadata metadata, String property)
    {
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                metadata.getPersistenceUnit());
        String jpaColumnName = null;

        if (property.equals(((AbstractAttribute) metadata.getIdAttribute()).getJPAColumnName()))
        {
            jpaColumnName = CassandraUtilities.getIdColumnName(kunderaMetadata, metadata,
                    ((CassandraClientBase) persistenceDelegeator.getClient(metadata)).getExternalProperties(),
                    ((CassandraClientBase) persistenceDelegeator.getClient(metadata)).isCql3Enabled(metadata));
        }
        else
        {
            jpaColumnName = ((AbstractAttribute) metaModel.getEntityAttribute(metadata.getEntityClazz(), property))
                    .getJPAColumnName();
        }
        return jpaColumnName;
    }

    boolean isNative()
    {
        return kunderaQuery.isNative();
    }

}
TOP

Related Classes of com.impetus.client.cassandra.query.CassQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.