Ignite-950 - Implemented new configuration for indexes.

Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/6f741823
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/6f741823
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/6f741823

Branch: refs/heads/ignite-950-new
Commit: 6f741823c16b0f6b2bc9506fdfe004f17af6f120
Parents: 81b899a
Author: Alexey Goncharuk <[email protected]>
Authored: Sat Oct 24 15:41:00 2015 +0300
Committer: Alexey Goncharuk <[email protected]>
Committed: Sat Oct 24 15:41:00 2015 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/cache/QueryEntity.java    | 217 +++++++++++++++++++
 .../apache/ignite/cache/QueryEntityIndex.java   | 100 +++++++++
 .../configuration/CacheConfiguration.java       | 140 ++++++------
 .../CacheObjectPortableProcessorImpl.java       |   3 +
 .../processors/query/GridQueryProcessor.java    | 150 ++++++++++++-
 .../IgniteCacheAbstractFieldsQuerySelfTest.java |   3 +-
 6 files changed, 546 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java 
b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
new file mode 100644
index 0000000..2cf8015
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.cache;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.A;
+
+/**
+ * Query entity is a description of {@link org.apache.ignite.IgniteCache 
cache} entry (composed of key and value)
+ * in a way of how it must be indexed and can be queried.
+ */
+public class QueryEntity implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Key type. */
+    private String keyType;
+
+    /** Value type. */
+    private String valType;
+
+    /** Fields available for query. A map from field name to type name. */
+    private LinkedHashMap<String, String> fields = new LinkedHashMap<>();
+
+    /** Aliases. */
+    private Map<String, String> aliases = new HashMap<>();
+
+    /** Collection of query indexes. */
+    private Map<String, QueryEntityIndex> idxs = new HashMap<>();
+
+    /**
+     * Gets key type for this query pair.
+     *
+     * @return Key type.
+     */
+    public String getKeyType() {
+        return keyType;
+    }
+
+    /**
+     * Sets key type for this query pair.
+     *
+     * @param keyType Key type.
+     */
+    public void setKeyType(String keyType) {
+        this.keyType = keyType;
+    }
+
+    /**
+     * Gets value type for this query pair.
+     *
+     * @return Value type.
+     */
+    public String getValueType() {
+        return valType;
+    }
+
+    /**
+     * Sets value type for this query pair.
+     *
+     * @param valType Value type.
+     */
+    public void setValueType(String valType) {
+        this.valType = valType;
+    }
+
+    /**
+     * Gets query fields for this query pair. The order of fields is important 
as it defines the order
+     * of columns returned by the 'select *' queries.
+     *
+     * @return Field-to-type map.
+     */
+    public LinkedHashMap<String, String> getFields() {
+        return fields;
+    }
+
+    /**
+     * Sets query fields for this query pair. The order if fields is important 
as it defines the
+     * order of columns returned by the 'select *' queries.
+     *
+     * @param fields Field-to-type map.
+     */
+    public void setFields(LinkedHashMap<String, String> fields) {
+        this.fields = fields;
+    }
+
+    /**
+     * Gets a collection of index entities.
+     *
+     * @return Collection of index entities.
+     */
+    public Collection<QueryEntityIndex> getIndexes() {
+        return idxs.values();
+    }
+
+    /**
+     * Gets aliases map.
+     *
+     * @return Aliases.
+     */
+    public Map<String, String> getAliases() {
+        return aliases;
+    }
+
+    /**
+     * Sets mapping from full property name in dot notation to an alias that 
will be used as SQL column name.
+     * Example: {"parent.name" -> "parentName"}.
+
+     * @param aliases Aliases map.
+     */
+    public void setAliases(Map<String, String> aliases) {
+        this.aliases = aliases;
+    }
+
+    /**
+     * Sets a collection of index entities.
+     *
+     * @param idxs Collection of index entities.
+     */
+    public void setIndexes(Collection<QueryEntityIndex> idxs) {
+        for (QueryEntityIndex idx : idxs) {
+            if (!F.isEmpty(idx.getFields())) {
+                if (idx.getName() == null)
+                    idx.setName(defaultIndexName(idx));
+
+                if (!this.idxs.containsKey(idx.getName()))
+                    this.idxs.put(idx.getName(), idx);
+                else
+                    throw new IllegalArgumentException("Duplicate index name: 
" + idx.getName());
+            }
+        }
+    }
+
+    /**
+     * Utility method for building query entities programmatically.
+     */
+    public void addQueryField(String fullName, String type, String alias) {
+        A.notNull(fullName, "fullName");
+        A.notNull(type, "type");
+
+        fields.put(fullName, type);
+
+        if (alias != null)
+            aliases.put(alias, fullName);
+    }
+
+    /**
+     * Ensures that index with the given name exists.
+     *
+     * @param idxName Index name.
+     * @param idxType Index type.
+     */
+    public void ensureIndex(String idxName, QueryEntityIndex.Type idxType) {
+        QueryEntityIndex idx = idxs.get(idxName);
+
+        if (idx == null) {
+            idx = new QueryEntityIndex();
+
+            idx.setName(idxName);
+            idx.setType(idxType);
+
+            idxs.put(idxName, idx);
+        }
+        else
+            throw new IllegalArgumentException("An index with the same name 
and of a different type already exists " +
+                "[idxName=" + idxName + ", existingIdxType=" + idx.getType() + 
", newIdxType=" + idxType + ']');
+    }
+
+    /**
+     * Generates default index name by concatenating all index field names.
+     *
+     * @param idx Index to build name for.
+     * @return Index name.
+     */
+    public static String defaultIndexName(QueryEntityIndex idx) {
+        StringBuilder idxName = new StringBuilder();
+
+        for (String field : idx.getFields()) {
+            idxName.append(field);
+
+            idxName.append('_');
+        }
+
+        for (int i = 0; i < idxName.length(); i++) {
+            char ch = idxName.charAt(i);
+
+            if (Character.isWhitespace(ch))
+                idxName.setCharAt(i, '_');
+            else
+                idxName.setCharAt(i, Character.toLowerCase(ch));
+        }
+
+        idxName.append("idx");
+
+        return idxName.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/core/src/main/java/org/apache/ignite/cache/QueryEntityIndex.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntityIndex.java 
b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntityIndex.java
new file mode 100644
index 0000000..18e7572
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntityIndex.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.ignite.cache;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * Contains list of fields to be indexed. It is possible to provide field name
+ * suffixed with index specific extension, for example for {@link Type#SORTED 
sorted} index
+ * the list can be provided as following {@code (id, name asc, age desc)}.
+ */
+public class QueryEntityIndex implements Serializable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** Index name. */
+    private String name;
+
+    /** */
+    private Collection<String> fields;
+
+    /** */
+    private Type type;
+
+    /**
+     * Index type.
+     */
+    public enum Type {
+        SORTED, FULLTEXT, GEOSPATIAL
+    }
+
+    /**
+     * Gets index name. Will be automatically set if not provided by a user.
+     *
+     * @return Index name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets index name.
+     *
+     * @param name Index name.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Gets fields included in the index.
+     *
+     * @return Collection of index fields.
+     */
+    public Collection<String> getFields() {
+        return fields;
+    }
+
+    /**
+     * Sets fields included in the index.
+     *
+     * @param fields Collection of index fields.
+     */
+    public void setFields(Collection<String> fields) {
+        this.fields = fields;
+    }
+
+    /**
+     * Gets index type.
+     *
+     * @return Index type.
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * Sets index type.
+     *
+     * @param type Index type.
+     */
+    public void setType(Type type) {
+        this.type = type;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index 23d5256..ac527f6 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -23,12 +23,14 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 import javax.cache.Cache;
@@ -48,6 +50,8 @@ import org.apache.ignite.cache.CacheMode;
 import org.apache.ignite.cache.CacheRebalanceMode;
 import org.apache.ignite.cache.CacheTypeMetadata;
 import org.apache.ignite.cache.CacheWriteSynchronizationMode;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryEntityIndex;
 import org.apache.ignite.cache.affinity.AffinityFunction;
 import org.apache.ignite.cache.affinity.AffinityKeyMapper;
 import org.apache.ignite.cache.eviction.EvictionFilter;
@@ -367,6 +371,9 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
     /** Cache store session listeners. */
     private Factory<? extends CacheStoreSessionListener>[] storeSesLsnrs;
 
+    /** Query entities. */
+    private Collection<QueryEntity> qryEntities;
+
     /** Empty constructor (all values are initialized to their defaults). */
     public CacheConfiguration() {
         /* No-op. */
@@ -429,6 +436,8 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         name = cc.getName();
         nearCfg = cc.getNearConfiguration();
         nodeFilter = cc.getNodeFilter();
+        pluginCfgs = cc.getPluginConfigurations();
+        qryEntities = cc.getQueryEntities();
         rebalanceMode = cc.getRebalanceMode();
         rebalanceBatchSize = cc.getRebalanceBatchSize();
         rebalanceDelay = cc.getRebalanceDelay();
@@ -453,7 +462,6 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
         writeBehindFlushSize = cc.getWriteBehindFlushSize();
         writeBehindFlushThreadCnt = cc.getWriteBehindFlushThreadCount();
         writeSync = cc.getWriteSynchronizationMode();
-        pluginCfgs = cc.getPluginConfigurations();
     }
 
     /**
@@ -1595,14 +1603,10 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
      *
      * @param typeMeta Collection of type metadata.
      * @return {@code this} for chaining.
+     * @deprecated Use {@link #setQueryEntities(java.util.Collection)} instead.
      */
     public CacheConfiguration<K, V> 
setTypeMetadata(Collection<CacheTypeMetadata> typeMeta) {
-        if (this.typeMeta == null)
-            this.typeMeta = new ArrayList<>(typeMeta);
-        else if (indexedTypes != null)
-            this.typeMeta.addAll(typeMeta);
-        else
-            throw new CacheException("Type metadata can be set only once.");
+        this.typeMeta = new ArrayList<>(typeMeta);
 
         return this;
     }
@@ -1779,8 +1783,8 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
             newIndexedTypes[i] = U.box(indexedTypes[i]);
         }
 
-        if (typeMeta == null)
-            typeMeta = new ArrayList<>();
+        if (qryEntities == null)
+            qryEntities = new ArrayList<>();
 
         for (int i = 0; i < len; i += 2) {
             Class<?> keyCls = newIndexedTypes[i];
@@ -1788,7 +1792,7 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
 
             TypeDescriptor desc = processKeyAndValueClasses(keyCls, valCls);
 
-            typeMeta.add(convert(desc));
+            qryEntities.add(convert(desc));
         }
 
         return this;
@@ -1841,6 +1845,32 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
     }
 
     /**
+     * Gets a collection of configured  query entities.
+     *
+     * @return Query entities configurations.
+     */
+    public Collection<QueryEntity> getQueryEntities() {
+        return qryEntities != null ? qryEntities : 
Collections.<QueryEntity>emptyList();
+    }
+
+    /**
+     * Sets query entities configuration.
+     *
+     * @param qryEntities Query entities.
+     * @return {@code this} for chaining.
+     */
+    public CacheConfiguration<K, V> setQueryEntities(Collection<QueryEntity> 
qryEntities) {
+        if (this.qryEntities == null)
+            this.qryEntities = new ArrayList<>(qryEntities);
+        else if (indexedTypes != null)
+            this.qryEntities.addAll(qryEntities);
+        else
+            throw new CacheException("Query entities can be set only once.");
+
+        return this;
+    }
+
+    /**
      * Gets topology validator.
      * <p>
      * See {@link TopologyValidator} for details.
@@ -1911,85 +1941,65 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
      * @param desc Type descriptor.
      * @return Type metadata.
      */
-    private static CacheTypeMetadata convert(TypeDescriptor desc) {
-        CacheTypeMetadata meta = new CacheTypeMetadata();
+    static QueryEntity convert(TypeDescriptor desc) {
+        QueryEntity entity = new QueryEntity();
 
         // Key and val types.
-        meta.setKeyType(desc.keyClass());
-        meta.setValueType(desc.valueClass());
-
-        // Query fields.
-        Map<String, Class<?>> qryFields = new HashMap<>();
+        entity.setKeyType(desc.keyClass().getName());
+        entity.setValueType(desc.valueClass().getName());
 
         for (ClassProperty prop : desc.props.values())
-            qryFields.put(prop.fullName(), mask(prop.type()));
+            entity.addQueryField(prop.fullName(), prop.type().getName(), 
prop.alias());
 
-        meta.setQueryFields(qryFields);
+        QueryEntityIndex txtIdx = null;
 
-        // Indexes.
-        Collection<String> txtFields = new ArrayList<>();
-        Map<String, LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>>> 
grps = new HashMap<>();
+        Collection<QueryEntityIndex> idxs = new ArrayList<>();
 
-        for (Map.Entry<String,GridQueryIndexDescriptor> idxEntry : 
desc.indexes().entrySet()) {
+        for (Map.Entry<String, GridQueryIndexDescriptor> idxEntry : 
desc.indexes().entrySet()) {
             GridQueryIndexDescriptor idx = idxEntry.getValue();
 
             if (idx.type() == FULLTEXT) {
-                assert txtFields.isEmpty();
+                assert txtIdx == null;
+
+                txtIdx = new QueryEntityIndex();
 
-                txtFields.addAll(idx.fields());
+                txtIdx.setType(QueryEntityIndex.Type.FULLTEXT);
+                txtIdx.setFields(new ArrayList<>(idx.fields()));
             }
             else {
-                LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>> grp = 
new LinkedHashMap<>();
-
-                for (String fieldName : idx.fields()) {
-                    Class<?> cls;
-
-                    if (_VAL.equals(fieldName))
-                        cls = desc.valueClass();
-                    else {
-                        ClassProperty prop = desc.props.get(fieldName);
+                Collection<String> grp = new ArrayList<>();
 
-                        assert prop != null : fieldName;
+                for (String fieldName : idx.fields())
+                    grp.add(idx.descending(fieldName) ? fieldName + " desc" : 
fieldName);
 
-                        cls = prop.type();
-                    }
+                QueryEntityIndex sortedIdx = new QueryEntityIndex();
 
-                    cls = mask(cls);
+                sortedIdx.setType(QueryEntityIndex.Type.SORTED);
+                sortedIdx.setFields(grp);
 
-                    grp.put(fieldName, new IgniteBiTuple<Class<?>, 
Boolean>(cls, idx.descending(fieldName)));
-                }
-
-                grps.put(idxEntry.getKey(), grp);
+                idxs.add(sortedIdx);
             }
         }
 
-        meta.setGroups(grps);
-
-        if (desc.valueTextIndex())
-            txtFields.add(_VAL);
-
-        if (!txtFields.isEmpty())
-            meta.setTextFields(txtFields);
+        if (desc.valueTextIndex()) {
+            if (txtIdx == null) {
+                txtIdx = new QueryEntityIndex();
 
-        // Aliases.
-        Map<String,String> aliases = null;
+                txtIdx.setType(QueryEntityIndex.Type.FULLTEXT);
 
-        for (ClassProperty prop : desc.props.values()) {
-            while (prop != null) {
-                if (!F.isEmpty(prop.alias)) {
-                    if (aliases == null)
-                        aliases = new HashMap<>();
-
-                    aliases.put(prop.fullName(), prop.alias);
-                }
-
-                prop = prop.parent;
+                txtIdx.setFields(Arrays.asList(_VAL));
             }
+            else
+                txtIdx.getFields().add(_VAL);
         }
 
-        meta.setAliases(aliases);
+        if (txtIdx != null)
+            idxs.add(txtIdx);
+
+        if (!F.isEmpty(idxs))
+            entity.setIndexes(idxs);
 
-        return meta;
+        return entity;
     }
 
     /**
@@ -2007,7 +2017,7 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
      * @param valCls Value class.
      * @return Type descriptor.
      */
-    private static TypeDescriptor processKeyAndValueClasses(
+    static TypeDescriptor processKeyAndValueClasses(
         Class<?> keyCls,
         Class<?> valCls
     ) {
@@ -2177,7 +2187,7 @@ public class CacheConfiguration<K, V> extends 
MutableConfiguration<K, V> {
 
         /** */
         @GridToStringExclude
-        private final Map<String, ClassProperty> props = new HashMap<>();
+        private final Map<String, ClassProperty> props = new LinkedHashMap<>();
 
         /** */
         @GridToStringInclude

http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/portable/CacheObjectPortableProcessorImpl.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/portable/CacheObjectPortableProcessorImpl.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/portable/CacheObjectPortableProcessorImpl.java
index 1be5aea..6ee6e12 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/portable/CacheObjectPortableProcessorImpl.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/portable/CacheObjectPortableProcessorImpl.java
@@ -417,6 +417,9 @@ public class CacheObjectPortableProcessorImpl extends 
IgniteCacheObjectProcessor
 
     /** {@inheritDoc} */
     @Override public int typeId(String typeName) {
+        if (portableCtx == null)
+            return super.typeId(typeName);
+
         return portableCtx.typeId(typeName);
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 5f7646b..a4a75fd 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -45,6 +45,8 @@ import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.cache.CacheTypeMetadata;
+import org.apache.ignite.cache.QueryEntity;
+import org.apache.ignite.cache.QueryEntityIndex;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
 import org.apache.ignite.cache.query.SqlQuery;
@@ -167,7 +169,9 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
      * @return {@code true} If query index must be enabled for this cache.
      */
     public static boolean isEnabled(CacheConfiguration<?,?> ccfg) {
-        return !F.isEmpty(ccfg.getIndexedTypes()) || 
!F.isEmpty(ccfg.getTypeMetadata());
+        return !F.isEmpty(ccfg.getIndexedTypes()) ||
+            !F.isEmpty(ccfg.getTypeMetadata()) ||
+            !F.isEmpty(ccfg.getQueryEntities());
     }
 
     /**
@@ -185,6 +189,56 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
         idx.registerCache(ccfg);
 
         try {
+            if (!F.isEmpty(ccfg.getQueryEntities())) {
+                for (QueryEntity qryEntity : ccfg.getQueryEntities()) {
+                    if (F.isEmpty(qryEntity.getValueType()))
+                        throw new IgniteCheckedException("Value type is not 
set: " + qryEntity);
+
+                    TypeDescriptor desc = new TypeDescriptor();
+
+                    // Key and value classes still can be available if they 
are primitive or JDK part.
+                    // We need that to set correct types for _key and _val 
columns.
+                    Class<?> valCls = U.classForName(qryEntity.getValueType(), 
null);
+
+                    String simpleValType = valCls == null ? 
qryEntity.getValueType() : typeName(valCls);
+
+                    desc.name(simpleValType);
+
+                    desc.valueClass(valCls != null ? valCls : Object.class);
+                    desc.keyClass(
+                        qryEntity.getKeyType() == null ?
+                            Object.class :
+                            U.classForName(qryEntity.getKeyType(), 
Object.class));
+
+                    TypeId typeId;
+                    TypeId altTypeId = null;
+
+                    if (valCls == null || 
ctx.cacheObjects().isPortableEnabled(ccfg)) {
+                        processPortableMeta(qryEntity, desc);
+
+                        typeId = new TypeId(ccfg.getName(), 
ctx.cacheObjects().typeId(qryEntity.getValueType()));
+
+                        if (valCls != null)
+                            altTypeId = new TypeId(ccfg.getName(), valCls);
+                    }
+                    else {
+                        processClassMeta(qryEntity, desc);
+
+                        typeId = new TypeId(ccfg.getName(), valCls);
+                        altTypeId = new TypeId(ccfg.getName(), 
ctx.cacheObjects().typeId(qryEntity.getValueType()));
+                    }
+
+                    addTypeByName(ccfg, desc);
+                    types.put(typeId, desc);
+
+                    if (altTypeId != null)
+                        types.put(altTypeId, desc);
+
+                    desc.registered(idx.registerType(ccfg.getName(), desc));
+
+                }
+            }
+
             if (!F.isEmpty(ccfg.getTypeMetadata())) {
                 for (CacheTypeMetadata meta : ccfg.getTypeMetadata()) {
                     if (F.isEmpty(meta.getValueType()))
@@ -1269,6 +1323,100 @@ public class GridQueryProcessor extends 
GridProcessorAdapter {
     }
 
     /**
+     * Processes declarative metadata for portable object.
+     *
+     * @param qryEntity Declared metadata.
+     * @param d Type descriptor.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void processPortableMeta(QueryEntity qryEntity, TypeDescriptor d) 
throws IgniteCheckedException {
+        Map<String,String> aliases = qryEntity.getAliases();
+
+        if (aliases == null)
+            aliases = Collections.emptyMap();
+
+        for (Map.Entry<String, String> entry : 
qryEntity.getFields().entrySet()) {
+            PortableProperty prop = buildPortableProperty(entry.getKey(), 
U.classForName(entry.getValue(), Object.class), aliases);
+
+            d.addProperty(prop, false);
+        }
+
+        processIndexes(qryEntity, d);
+    }
+
+    /**
+     * Processes declarative metadata for portable object.
+     *
+     * @param qryEntity Declared metadata.
+     * @param d Type descriptor.
+     * @throws IgniteCheckedException If failed.
+     */
+    private void processClassMeta(QueryEntity qryEntity, TypeDescriptor d) 
throws IgniteCheckedException {
+        Map<String,String> aliases = qryEntity.getAliases();
+
+        if (aliases == null)
+            aliases = Collections.emptyMap();
+
+        for (Map.Entry<String, String> entry : 
qryEntity.getFields().entrySet()) {
+            ClassProperty prop = buildClassProperty(
+                d.keyClass(),
+                d.valueClass(),
+                entry.getKey(),
+                U.classForName(entry.getValue(), Object.class),
+                aliases);
+
+
+            d.addProperty(prop, false);
+        }
+
+        processIndexes(qryEntity, d);
+    }
+
+    /**
+     * Processes indexes based on query entity.
+     *
+     * @param qryEntity Query entity to process.
+     * @param d Type descriptor to populate.
+     * @throws IgniteCheckedException If failed to build index information.
+     */
+    private void processIndexes(QueryEntity qryEntity, TypeDescriptor d) 
throws IgniteCheckedException {
+        if (!F.isEmpty(qryEntity.getIndexes())) {
+            for (QueryEntityIndex idx : qryEntity.getIndexes()) {
+                String idxName = idx.getName();
+
+                if (idxName == null)
+                    idxName = QueryEntity.defaultIndexName(idx);
+
+                if (idx.getType() == QueryEntityIndex.Type.SORTED || 
idx.getType() == QueryEntityIndex.Type.GEOSPATIAL) {
+                    d.addIndex(idxName, idx.getType() == 
QueryEntityIndex.Type.SORTED ? SORTED : GEO_SPATIAL);
+
+                    int i = 0;
+
+                    for (String field : idx.getFields()) {
+                        boolean desc = false;
+
+                        int space = field.indexOf(' ');
+
+                        if (space != -1) {
+                            desc = field.toLowerCase().startsWith("desc", 
space + 1);
+
+                            field = field.substring(0, space);
+                        }
+
+                        d.addFieldToIndex(idxName, field, i++, desc);
+                    }
+                }
+                else {
+                    assert idx.getType() == QueryEntityIndex.Type.FULLTEXT;
+
+                    for (String field : idx.getFields())
+                        d.addFieldToTextIndex(field);
+                }
+            }
+        }
+    }
+
+    /**
      * Builds portable object property.
      *
      * @param pathStr String representing path to the property. May contains 
dots '.' to identify

http://git-wip-us.apache.org/repos/asf/ignite/blob/6f741823/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
----------------------------------------------------------------------
diff --git 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
index 17f929a..9ccb893 100644
--- 
a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
+++ 
b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java
@@ -143,7 +143,8 @@ public abstract class 
IgniteCacheAbstractFieldsQuerySelfTest extends GridCommonA
             }
         }
 
-        cache.setIndexedTypes(indexedTypes.toArray(new 
Class[indexedTypes.size()]));
+        if (!indexedTypes.isEmpty())
+            cache.setIndexedTypes(indexedTypes.toArray(new 
Class[indexedTypes.size()]));
 
         if (cacheMode() == PARTITIONED)
             cache.setBackups(1);

Reply via email to