Package com.impetus.client.hbase.query

Source Code of com.impetus.client.hbase.query.HBaseQuery$QueryTranslator

/*******************************************************************************
* * 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.hbase.query;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

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

import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.impetus.client.hbase.HBaseClient;
import com.impetus.client.hbase.HBaseEntityReader;
import com.impetus.client.hbase.utils.HBaseUtils;
import com.impetus.kundera.client.Client;
import com.impetus.kundera.client.ClientBase;
import com.impetus.kundera.metadata.MetadataUtils;
import com.impetus.kundera.metadata.model.ClientMetadata;
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.QueryImpl;
import com.impetus.kundera.utils.ReflectUtils;

/**
* Query implementation for HBase, translates JPQL into HBase Filters using
* {@link QueryTranslator}.
*
* @author vivek.mishra
*
*/
public class HBaseQuery extends QueryImpl
{

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

    /**
     * Constructor using fields.
     *
     * @param query
     *            jpa query.
     * @param persistenceDelegator
     *            persistence delegator interface.
     */
    public HBaseQuery(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)
    {
        List results = onQuery(m, client);
        return results;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * com.impetus.kundera.query.QueryImpl#recursivelyPopulateEntities(com.impetus
     * .kundera.metadata.model.EntityMetadata,
     * com.impetus.kundera.client.Client)
     */
    @Override
    protected List<Object> recursivelyPopulateEntities(EntityMetadata m, Client client)
    {
        // required in case of associated entities.
        List ls = onQuery(m, client);
        return setRelationEntities(ls, client, m);
    }

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

    /*
     * (non-Javadoc)
     *
     * @see com.impetus.kundera.query.QueryImpl#onExecuteUpdate()
     */
    @Override
    protected int onExecuteUpdate()
    {
        return onUpdateDeleteEvent();
    }

    /**
     * Parses and translates query into HBase filter and invokes client's method
     * to return list of entities.
     *
     * @param m
     *            Entity metadata
     * @param client
     *            hbase client
     * @return list of entities.
     */
    private List onQuery(EntityMetadata m, Client client)
    {
        // Called only in case of standalone entity.
        QueryTranslator translator = new QueryTranslator();
        translator.translate(getKunderaQuery(), m, ((ClientBase) client).getClientMetadata());
        // start with 1 as first element is alias.
        List<String> columns = getTranslatedColumns(m, getKunderaQuery().getResult(), 1);
        Filter filter = translator.getFilter();

        if (translator.rowList != null && !translator.rowList.isEmpty())
        {
            return ((HBaseClient) client).findAll(m.getEntityClazz(), columns.toArray(new String[columns.size()]),
                    translator.getRowList());
        }
        if (filter == null && columns != null)
        {
            return ((HBaseClient) client).findByRange(m.getEntityClazz(), m, translator.getStartRow(),
                    translator.getEndRow(), columns.toArray(new String[columns.size()]), null);
        }

        if (MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()))
        {

            if (filter == null)
            {
                // means complete scan without where clause, scan all records.
                // findAll.
                if (translator.isRangeScan())
                {
                    return ((HBaseClient) client).findByRange(m.getEntityClazz(), m, translator.getStartRow(),
                            translator.getEndRow(), columns.toArray(new String[columns.size()]), null);
                }
                else
                {
                    return ((HBaseClient) client).findByRange(m.getEntityClazz(), m, null, null,
                            columns.toArray(new String[columns.size()]), null);
                }
            }
            else
            {

                // means WHERE clause is present.
                if (translator.isRangeScan())
                {
                    return ((HBaseClient) client).findByRange(m.getEntityClazz(), m, translator.getStartRow(),
                            translator.getEndRow(), columns.toArray(new String[columns.size()]), filter);
                }
                else
                {
                    // if range query. means query over id column. create range
                    // scan method.

                    // else setFilter to client and invoke new method. find by
                    // query if isFindById is false! else invoke findById
                    return ((HBaseClient) client).findByQuery(m.getEntityClazz(), m, filter,
                            columns.toArray(new String[columns.size()]));
                }
            }
        }
        else
        {
            List results = null;
            return populateUsingLucene(m, client, results, null);
        }
    }

    /**
     * @param columns
     * @param m
     * @return
     */
    private List<String> getTranslatedColumns(EntityMetadata m, String[] columns, final int startWith)
    {
        List<String> translatedColumns = new ArrayList<String>();
        if (columns != null)
        {
            MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                    m.getPersistenceUnit());

            EntityType entity = metaModel.entity(m.getEntityClazz());
            for (int i = startWith; i < columns.length; i++)
            {
                if (columns[i] != null)
                {
                    String fieldName = null;
                    String embeddedFieldName = null;
                    // used string tokenizer to check for embedded column.
                    StringTokenizer stringTokenizer = new StringTokenizer(columns[i], ".");
                    // if need to select embedded columns
                    if (stringTokenizer.countTokens() > 1)
                    {
                        fieldName = stringTokenizer.nextToken();
                        embeddedFieldName = stringTokenizer.nextToken();
                        Attribute col = entity.getAttribute(fieldName); // get
                                                                        // embedded
                                                                        // column
                        EmbeddableType embeddableType = metaModel.embeddable(col.getJavaType()); // get
                                                                                                 // embeddable
                                                                                                 // type
                        Attribute attribute = embeddableType.getAttribute(embeddedFieldName);
                        translatedColumns.add(((AbstractAttribute) attribute).getJPAColumnName());
                    }
                    else
                    {
                        // For all columns
                        fieldName = columns[i];
                        Attribute col = entity.getAttribute(fieldName);
                        onEmbeddable(translatedColumns, metaModel, col,
                                metaModel.isEmbeddable(((AbstractAttribute) col).getBindableJavaType()));
                    }
                }
            }
        }
        return translatedColumns;
    }

    /**
     * @param translatedColumns
     * @param metaModel
     * @param col
     */
    private void onEmbeddable(List<String> translatedColumns, MetamodelImpl metaModel, Attribute col,
            boolean isEmbeddable)
    {
        if (isEmbeddable)
        {
            EmbeddableType embeddableType = metaModel.embeddable(col.getJavaType());

            Set<Attribute> attributes = embeddableType.getAttributes();

            for (Attribute attribute : attributes)
            {
                translatedColumns.add(((AbstractAttribute) attribute).getJPAColumnName());
            }
        }
        else
        {
            translatedColumns.add(((AbstractAttribute) col).getJPAColumnName());
        }
    }

    /**
     * Query translator to translate JPQL into HBase query definition(e.g.
     * Filter/Filterlist)
     *
     * @author vivek.mishra
     *
     */
    class QueryTranslator
    {

        private static final String CLOSE_BRACKET = ")";

        private static final String IN_CLAUSE = "IN";

        private static final String OR_OPERATOR = "OR";

        private static final String OPEN_BRACKET = "(";

        /* filter list to hold collection for applied filters */
        private List<Filter> filterList;

        /*
         * byte[] value for start row, in case of range query, else will contain
         * null.
         */
        private byte[] startRow;

        /*
         * byte[] value for end row, in case of range query, else will contain
         * null.
         */
        private byte[] endRow;

        /*
         * byte[] value for list of rows null.
         */
        private List rowList = new ArrayList();

        /*
         * boolean value for finding whether its OR or in query.
         */
        private boolean isORQuery;

        /**
         * @return
         */
        public boolean isORQuery()
        {
            return isORQuery;
        }

        /**
         * @return
         */
        public Object[] getRowList()
        {
            return rowList.toArray();
        }

        /**
         * @param rowList
         */
        public void setRowList(List rowList)
        {
            if (rowList == null)
            {
                rowList = new ArrayList<Filter>();
            }
            this.rowList = rowList;
        }

        /**
         * Translates kundera query into collection of to be applied HBase
         * filter/s.
         *
         * @param query
         *            kundera query.
         * @param m
         *            entity's metadata.
         */
        void translate(KunderaQuery query, EntityMetadata m, ClientMetadata clientMetadata)
        {
            String idColumn = ((AbstractAttribute) m.getIdAttribute()).getJPAColumnName();

            boolean useFilter = MetadataUtils.useSecondryIndex(clientMetadata);
            for (Object obj : query.getFilterClauseQueue())
            {
                boolean isIdColumn = false;
                // parse for filter(e.g. where) clause.

                if (obj instanceof FilterClause)
                {
                    String condition = ((FilterClause) obj).getCondition();
                    String name = ((FilterClause) obj).getProperty();
                    Object value = ((FilterClause) obj).getValue().get(0);
                    if (idColumn.equalsIgnoreCase(name))
                    {
                        isIdColumn = true;
                    }
                    onParseFilter(condition, name, value, isIdColumn, m, useFilter);
                }
                else
                {
                    // Case of AND and OR clause.
                    String opr = obj.toString();
                    if (MetadataUtils.useSecondryIndex(clientMetadata))
                    {
                        if (opr.trim().equalsIgnoreCase(OR_OPERATOR))
                        {
                            this.isORQuery = true;
                            // log.error("Support for OR clause is not enabled with in Hbase");
                            // throw new
                            // QueryHandlerException("Unsupported clause " + opr
                            // + " for Hbase");
                        }
                    }
                }
            }
        }

        /**
         * Returns collection of parsed filter.
         *
         * @return map.
         */
        Filter getFilter()
        {
            if (filterList != null)
            {
                if (this.isORQuery)
                {
                    return new FilterList(FilterList.Operator.MUST_PASS_ONE, filterList);
                }
                else
                {
                    return new FilterList(filterList);
                }
            }
            return null;
        }

        /**
         * On parsing filter clause(e.g. WHERE clause).
         *
         * @param condition
         *            condition
         * @param name
         *            column name.
         * @param value
         *            column value.
         * @param isIdColumn
         *            if it is an id column.
         * @param m
         *            entity metadata.
         */
        private void onParseFilter(String condition, String name, Object value, boolean isIdColumn, EntityMetadata m,
                boolean useFilter)
        {
            if (condition.trim().equalsIgnoreCase(IN_CLAUSE))
            {

                onInClause(name, value, m, isIdColumn);

            }
            else
            {
                CompareOp operator = HBaseUtils.getOperator(condition, isIdColumn, useFilter);

                if (!isIdColumn)
                {
                    Filter f = createQualifierValueFilter(name, value, m, operator);
                    addToFilter(f);
                }
                else
                {
                    if (operator.equals(CompareOp.GREATER_OR_EQUAL) || operator.equals(CompareOp.GREATER))
                    {
                        byte[] valueInBytes = getBytes(name, m, value);
                        startRow = valueInBytes;
                    }
                    else if (operator.equals(CompareOp.LESS_OR_EQUAL) || operator.equals(CompareOp.LESS))
                    {
                        byte[] valueInBytes = getBytes(name, m, value);
                        endRow = valueInBytes;
                    }
                    else if (operator.equals(CompareOp.EQUAL))
                    {
                        startRow = endRow = getBytes(m.getIdAttribute().getName(), m, value);
                    }
                }
            }
        }

        /**
         * @param name
         * @param value
         * @param m
         * @param operator
         * @return
         */
        private Filter createQualifierValueFilter(String name, Object value, EntityMetadata m, CompareOp operator)
        {
            List<String> columns = null;
            byte[] valueInBytes = getBytes(name, m, value);
            if (new StringTokenizer(name, ".").countTokens() > 1)
            {
                columns = getTranslatedColumns(m, new String[] { name }, 0);
            }

            if (columns != null && !columns.isEmpty())
            {
                name = columns.get(0);
            }
            Filter f = new SingleColumnValueFilter(Bytes.toBytes(m.getTableName()), Bytes.toBytes(name), operator,
                    valueInBytes);
            return f;
        }

        /**
         * @param name
         * @param value
         * @param m
         */
        private void onInClause(String name, Object value, EntityMetadata m, boolean isIdColumn)
        {
            List<String> columns = null;
            FilterList inClauseFilterList = new FilterList(FilterList.Operator.MUST_PASS_ONE);
            if (new StringTokenizer(name, ".").countTokens() > 1)
            {
                columns = getTranslatedColumns(m, new String[] { name }, 0);
            }

            if (columns != null && !columns.isEmpty())
            {
                name = columns.get(0);
            }

            String itemValues = String.valueOf(value);
            itemValues = itemValues.startsWith(OPEN_BRACKET) && itemValues.endsWith(CLOSE_BRACKET) ? itemValues
                    .substring(1, itemValues.length() - 1) : itemValues;
            List<String> items = Arrays.asList(((String) itemValues).split("\\s*,\\s*"));

            for (String str : items)
            {
                str = str.trim();
                str = (str.startsWith("\"") && str.endsWith("\"")) || (str.startsWith("'") && str.endsWith("'")) ? str
                        .substring(1, str.length() - 1) : str;

                byte[] valueInBytes = getBytes(name, m, str);
                if (!isIdColumn)
                {
                    Filter f = new SingleColumnValueFilter(Bytes.toBytes(m.getTableName()), Bytes.toBytes(name),
                            CompareOp.EQUAL, valueInBytes);
                    inClauseFilterList.addFilter(f);

                }
                else
                {
                    rowList.add(valueInBytes);
                }

            }
            if (!inClauseFilterList.getFilters().isEmpty())
            {
                addToFilter(inClauseFilterList);
            }
        }

        /**
         * @return the startRow
         */
        byte[] getStartRow()
        {
            return startRow;
        }

        /**
         * @return the endRow
         */
        byte[] getEndRow()
        {
            return endRow;
        }

        boolean isRangeScan()
        {
            return startRow != null || endRow != null;
        }

        /**
         * @param f
         */
        private void addToFilter(Filter f)
        {
            if (filterList == null)
            {
                filterList = new ArrayList<Filter>();
            }
            filterList.add(f);
        }
    }

    /**
     * Returns bytes of value object.
     *
     * @param jpaFieldName
     * @param m
     * @param value
     * @return
     */
    private byte[] getBytes(String jpaFieldName, EntityMetadata m, Object value)
    {
        AbstractAttribute idCol = (AbstractAttribute) m.getIdAttribute();
        MetamodelImpl metaModel = (MetamodelImpl) kunderaMetadata.getApplicationMetadata().getMetamodel(
                m.getPersistenceUnit());

        EntityType entity = metaModel.entity(m.getEntityClazz());
        Class fieldClazz = null;
        if (idCol.getName().equals(jpaFieldName))
        {
            Field f = (Field) idCol.getJavaMember();

            if (metaModel.isEmbeddable(idCol.getBindableJavaType()))
            {
                fieldClazz = String.class;
                Map<Attribute, List<Object>> columnValues = new HashMap<Attribute, List<Object>>();
                Field[] fields = m.getIdAttribute().getBindableJavaType().getDeclaredFields();
                EmbeddableType embeddable = metaModel.embeddable(m.getIdAttribute().getBindableJavaType());

                StringBuilder compositeKey = new StringBuilder();
                for (Field field : fields)
                {
                    if (!ReflectUtils.isTransientOrStatic(field))
                    {
                        AbstractAttribute attrib = (AbstractAttribute) embeddable.getAttribute(field.getName());
                        Object obj = PropertyAccessorHelper.getObject(value, field);
                        compositeKey.append(
                                PropertyAccessorHelper.fromSourceToTargetClass(String.class,
                                        attrib.getBindableJavaType(), obj)).append("\001");
                    }
                }
                compositeKey.delete(compositeKey.lastIndexOf("\001"), compositeKey.length());
                value = compositeKey.toString();
            }
            else
            {
                fieldClazz = f.getType();
            }
        }
        else
        {
            StringTokenizer tokenizer = new StringTokenizer(jpaFieldName, ".");
            String embeddedFieldName = null;
            if (tokenizer.countTokens() > 1)
            {
                embeddedFieldName = tokenizer.nextToken();
                String fieldName = tokenizer.nextToken();
                Attribute embeddableAttribute = entity.getAttribute(embeddedFieldName);
                EmbeddableType embeddableType = metaModel.embeddable(embeddableAttribute.getJavaType());
                Attribute embeddedAttribute = embeddableType.getAttribute(fieldName);
                jpaFieldName = ((AbstractAttribute) embeddedAttribute).getJPAColumnName();
                fieldClazz = ((AbstractAttribute) embeddedAttribute).getBindableJavaType();
            }
            else
            {
                String discriminatorColumn = ((AbstractManagedType) entity).getDiscriminatorColumn();

                if (!jpaFieldName.equals(discriminatorColumn))
                {
                    String fieldName = m.getFieldName(jpaFieldName);
                    Attribute col = entity.getAttribute(fieldName);
                    fieldClazz = ((AbstractAttribute) col).getBindableJavaType();
                }
            }
        }

        if (fieldClazz != null)
        {
            return HBaseUtils.getBytes(value, fieldClazz);
        }
        else
        {
            // Treat default as UTF8-Type. { in case of discriminator column}
            return HBaseUtils.getBytes(value, String.class);
        }
    }

    @Override
    public void close()
    {
        // TODO Auto-generated method stub

    }

    @Override
    public Iterator iterate()
    {
        EntityMetadata m = getEntityMetadata();
        Client client = persistenceDelegeator.getClient(m);

        if (!MetadataUtils.useSecondryIndex(((ClientBase) client).getClientMetadata()))
        {
            throw new UnsupportedOperationException("Scrolling over hbase is unsupported for lucene queries");
        }
        QueryTranslator translator = new QueryTranslator();
        translator.translate(getKunderaQuery(), m, ((ClientBase) client).getClientMetadata());
        // start with 1 as first element is alias.
        List<String> columns = getTranslatedColumns(m, getKunderaQuery().getResult(), 1);

        return new ResultIterator((HBaseClient) client, m, persistenceDelegeator,
                getFetchSize() != null ? getFetchSize() : this.maxResult, translator, columns);
    }

    @Override
    protected List findUsingLucene(EntityMetadata m, Client client)
    {
        QueryTranslator translator = new QueryTranslator();
        translator.translate(getKunderaQuery(), m, ((ClientBase) client).getClientMetadata());
        List<String> columns = getTranslatedColumns(m, getKunderaQuery().getResult(), 1);
       
        return ((HBaseClient) client).findByRange(m.getEntityClazz(), m, translator.getStartRow(),
                translator.getEndRow(), columns.toArray(new String[columns.size()]), null);
    }

}
TOP

Related Classes of com.impetus.client.hbase.query.HBaseQuery$QueryTranslator

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.