/*******************************************************************************
* * 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();
}
}