From f027c03167183254768c24c0aca4a8ddc33b985a Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Thu, 3 Aug 2017 17:36:23 +0300 Subject: [PATCH 1/2] IGNITE-5211 Classes based constructor for QueryEntities --- .../org/apache/ignite/cache/QueryEntity.java | 231 ++++++- .../configuration/CacheConfiguration.java | 600 +----------------- .../cache/config/ClassProperty.java | 116 ++++ .../cache/config/IndexDescriptor.java | 121 ++++ .../cache/config/TypeDescriptor.java | 219 +++++++ .../h2/sql/AbstractH2CompareQueryTest.java | 4 +- 6 files changed, 694 insertions(+), 597 deletions(-) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/ClassProperty.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/IndexDescriptor.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/TypeDescriptor.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 index 955e7d2dcb036..b78bfc4f5687c 100644 --- a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java +++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java @@ -18,7 +18,9 @@ package org.apache.ignite.cache; import java.io.Serializable; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -27,10 +29,20 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import javax.cache.CacheException; +import org.apache.ignite.cache.query.annotations.QueryGroupIndex; +import org.apache.ignite.cache.query.annotations.QuerySqlField; +import org.apache.ignite.cache.query.annotations.QueryTextField; +import org.apache.ignite.internal.processors.cache.config.ClassProperty; +import org.apache.ignite.internal.processors.cache.config.TypeDescriptor; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.processors.query.QueryUtils; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.jetbrains.annotations.Nullable; /** * Query entity is a description of {@link org.apache.ignite.IgniteCache cache} entry (composed of key and value) @@ -110,6 +122,16 @@ public QueryEntity(String keyType, String valType) { this.valType = valType; } + /** + * Creates a query entity with the given key and value types. + * + * @param keyCls Key type. + * @param valCls Value type. + */ + public QueryEntity(Class keyCls, Class valCls) { + this(convert(processKeyAndValueClasses(keyCls,valCls))); + } + /** * Gets key type for this query pair. * @@ -303,7 +325,7 @@ public QueryEntity setAliases(Map aliases) { return this; } - /** + /**1 * Sets a collection of index entities. * * @param idxs Collection of index entities. @@ -351,6 +373,213 @@ public QueryEntity addQueryField(String fullName, String type, String alias) { return this; } + /** + * @param desc Type descriptor. + * @return Type metadata. + */ + private static QueryEntity convert(TypeDescriptor desc) { + QueryEntity entity = new QueryEntity(); + + // Key and val types. + entity.setKeyType(desc.keyClass().getName()); + entity.setValueType(desc.valueClass().getName()); + + for (ClassProperty prop : desc.properties().values()) + entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias()); + + entity.setKeyFields(desc.keyProperties()); + + QueryIndex txtIdx = null; + + Collection idxs = new ArrayList<>(); + + for (Map.Entry idxEntry : desc.indexes().entrySet()) { + GridQueryIndexDescriptor idx = idxEntry.getValue(); + + if (idx.type() == QueryIndexType.FULLTEXT) { + assert txtIdx == null; + + txtIdx = new QueryIndex(); + + txtIdx.setIndexType(QueryIndexType.FULLTEXT); + + txtIdx.setFieldNames(idx.fields(), true); + txtIdx.setName(idxEntry.getKey()); + } + else { + QueryIndex sortedIdx = new QueryIndex(); + + sortedIdx.setIndexType(idx.type()); + + LinkedHashMap fields = new LinkedHashMap<>(); + + for (String f : idx.fields()) + fields.put(f, !idx.descending(f)); + + sortedIdx.setFields(fields); + + sortedIdx.setName(idxEntry.getKey()); + + idxs.add(sortedIdx); + } + } + + if (desc.valueTextIndex()) { + if (txtIdx == null) { + txtIdx = new QueryIndex(); + + txtIdx.setIndexType(QueryIndexType.FULLTEXT); + + txtIdx.setFieldNames(Arrays.asList(QueryUtils.VAL_FIELD_NAME), true); + } + else + txtIdx.getFields().put(QueryUtils.VAL_FIELD_NAME, true); + } + + if (txtIdx != null) + idxs.add(txtIdx); + + if (!F.isEmpty(idxs)) + entity.setIndexes(idxs); + + return entity; + } + + /** + * @param keyCls Key class. + * @param valCls Value class. + * @return Type descriptor. + */ + private static TypeDescriptor processKeyAndValueClasses( + Class keyCls, + Class valCls + ) { + TypeDescriptor d = new TypeDescriptor(); + + d.keyClass(keyCls); + d.valueClass(valCls); + + processAnnotationsInClass(true, d.keyClass(), d, null); + processAnnotationsInClass(false, d.valueClass(), d, null); + + return d; + } + + /** + * Process annotations for class. + * + * @param key If given class relates to key. + * @param cls Class. + * @param type Type descriptor. + * @param parent Parent in case of embeddable. + */ + private static void processAnnotationsInClass(boolean key, Class cls, TypeDescriptor type, + @Nullable ClassProperty parent) { + if (U.isJdk(cls) || QueryUtils.isGeometryClass(cls)) { + if (parent == null && !key && QueryUtils.isSqlType(cls)) { // We have to index primitive _val. + String idxName = cls.getSimpleName() + "_" + QueryUtils.VAL_FIELD_NAME + "_idx"; + + type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ? + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + + type.addFieldToIndex(idxName, QueryUtils.VAL_FIELD_NAME, 0, false); + } + + return; + } + + if (parent != null && parent.knowsClass(cls)) + throw new CacheException("Recursive reference found in type: " + cls.getName()); + + if (parent == null) { // Check class annotation at top level only. + QueryTextField txtAnnCls = cls.getAnnotation(QueryTextField.class); + + if (txtAnnCls != null) + type.valueTextIndex(true); + + QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class); + + if (grpIdx != null) + type.addIndex(grpIdx.name(), QueryIndexType.SORTED); + + QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class); + + if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) { + for (QueryGroupIndex idx : grpIdxList.value()) + type.addIndex(idx.name(), QueryIndexType.SORTED); + } + } + + for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) { + for (Field field : c.getDeclaredFields()) { + QuerySqlField sqlAnn = field.getAnnotation(QuerySqlField.class); + QueryTextField txtAnn = field.getAnnotation(QueryTextField.class); + + if (sqlAnn != null || txtAnn != null) { + ClassProperty prop = new ClassProperty(field); + + prop.parent(parent); + + // Add parent property before its possible nested properties so that + // resulting parent column comes before columns corresponding to those + // nested properties in the resulting table - that way nested + // properties override will happen properly (first parent, then children). + type.addProperty(prop, key, true); + + processAnnotation(key, sqlAnn, txtAnn, cls, c, field.getType(), prop, type); + } + } + } + } + + /** + * Processes annotation at field or method. + * + * @param key If given class relates to key. + * @param sqlAnn SQL annotation, can be {@code null}. + * @param txtAnn H2 text annotation, can be {@code null}. + * @param cls Entity class. + * @param curCls Current entity class. + * @param fldCls Class of field or return type for method. + * @param prop Current property. + * @param desc Class description. + */ + private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTextField txtAnn, + Class cls, Class curCls, Class fldCls, ClassProperty prop, TypeDescriptor desc) { + if (sqlAnn != null) { + processAnnotationsInClass(key, fldCls, desc, prop); + + if (!sqlAnn.name().isEmpty()) + prop.alias(sqlAnn.name()); + + if (sqlAnn.index()) { + String idxName = curCls.getSimpleName() + "_" + prop.alias() + "_idx"; + + if (cls != curCls) + idxName = cls.getSimpleName() + "_" + idxName; + + desc.addIndex(idxName, QueryUtils.isGeometryClass(prop.type()) ? + QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); + + desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending()); + } + + if (!F.isEmpty(sqlAnn.groups())) { + for (String group : sqlAnn.groups()) + desc.addFieldToIndex(group, prop.fullName(), 0, false); + } + + if (!F.isEmpty(sqlAnn.orderedGroups())) { + for (QuerySqlField.Group idx : sqlAnn.orderedGroups()) + desc.addFieldToIndex(idx.name(), prop.fullName(), idx.order(), idx.descending()); + } + } + + if (txtAnn != null) + desc.addFieldToTextIndex(prop.fullName()); + } + + /** {@inheritDoc} */ @Override public boolean equals(Object o) { if (this == o) 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 708913ab6da08..866b88080e0e9 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,7 +23,6 @@ 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; @@ -53,16 +52,12 @@ import org.apache.ignite.cache.CacheWriteSynchronizationMode; import org.apache.ignite.cache.PartitionLossPolicy; import org.apache.ignite.cache.QueryEntity; -import org.apache.ignite.cache.QueryIndex; import org.apache.ignite.cache.QueryIndexType; import org.apache.ignite.cache.affinity.AffinityFunction; import org.apache.ignite.cache.affinity.AffinityKeyMapper; import org.apache.ignite.cache.eviction.EvictionFilter; import org.apache.ignite.cache.eviction.EvictionPolicy; -import org.apache.ignite.cache.query.annotations.QueryGroupIndex; -import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.cache.query.annotations.QuerySqlFunction; -import org.apache.ignite.cache.query.annotations.QueryTextField; import org.apache.ignite.cache.store.CacheStore; import org.apache.ignite.cache.store.CacheStoreSessionListener; import org.apache.ignite.cluster.ClusterNode; @@ -1768,14 +1763,12 @@ public CacheConfiguration setIndexedTypes(Class... indexedTypes) { Class keyCls = newIndexedTypes[i]; Class valCls = newIndexedTypes[i + 1]; - TypeDescriptor desc = processKeyAndValueClasses(keyCls, valCls); - - QueryEntity converted = convert(desc); + QueryEntity qryEnt = new QueryEntity(keyCls, valCls); boolean dup = false; for (QueryEntity entity : qryEntities) { - if (F.eq(entity.findValueType(), converted.findValueType())) { + if (F.eq(entity.findValueType(), qryEnt.findValueType())) { dup = true; break; @@ -1783,13 +1776,13 @@ public CacheConfiguration setIndexedTypes(Class... indexedTypes) { } if (!dup) - qryEntities.add(converted); + qryEntities.add(qryEnt); // Set key configuration if needed. - String affFieldName = desc.affinityFieldName(); + String affFieldName = BinaryContext.affinityFieldName(keyCls); if (affFieldName != null) { - CacheKeyConfiguration newKeyCfg = new CacheKeyConfiguration(converted.getKeyType(), affFieldName); + CacheKeyConfiguration newKeyCfg = new CacheKeyConfiguration(qryEnt.getKeyType(), affFieldName); if (F.isEmpty(keyCfg)) keyCfg = new CacheKeyConfiguration[] { newKeyCfg }; @@ -2041,77 +2034,6 @@ protected Object writeReplace() { return cfg; } - /** - * @param desc Type descriptor. - * @return Type metadata. - */ - private static QueryEntity convert(TypeDescriptor desc) { - QueryEntity entity = new QueryEntity(); - - // Key and val types. - entity.setKeyType(desc.keyClass().getName()); - entity.setValueType(desc.valueClass().getName()); - - for (ClassProperty prop : desc.props.values()) - entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias()); - - entity.setKeyFields(desc.keyProps); - - QueryIndex txtIdx = null; - - Collection idxs = new ArrayList<>(); - - for (Map.Entry idxEntry : desc.indexes().entrySet()) { - GridQueryIndexDescriptor idx = idxEntry.getValue(); - - if (idx.type() == QueryIndexType.FULLTEXT) { - assert txtIdx == null; - - txtIdx = new QueryIndex(); - - txtIdx.setIndexType(QueryIndexType.FULLTEXT); - - txtIdx.setFieldNames(idx.fields(), true); - txtIdx.setName(idxEntry.getKey()); - } - else { - QueryIndex sortedIdx = new QueryIndex(); - - sortedIdx.setIndexType(idx.type()); - - LinkedHashMap fields = new LinkedHashMap<>(); - - for (String f : idx.fields()) - fields.put(f, !idx.descending(f)); - - sortedIdx.setFields(fields); - - sortedIdx.setName(idxEntry.getKey()); - - idxs.add(sortedIdx); - } - } - - if (desc.valueTextIndex()) { - if (txtIdx == null) { - txtIdx = new QueryIndex(); - - txtIdx.setIndexType(QueryIndexType.FULLTEXT); - - txtIdx.setFieldNames(Arrays.asList(QueryUtils.VAL_FIELD_NAME), true); - } - else - txtIdx.getFields().put(QueryUtils.VAL_FIELD_NAME, true); - } - - if (txtIdx != null) - idxs.add(txtIdx); - - if (!F.isEmpty(idxs)) - entity.setIndexes(idxs); - - return entity; - } /** * @param cls Class. @@ -2123,141 +2045,6 @@ private static Class mask(Class cls) { return QueryUtils.isSqlType(cls) ? cls : Object.class; } - /** - * @param keyCls Key class. - * @param valCls Value class. - * @return Type descriptor. - */ - static TypeDescriptor processKeyAndValueClasses( - Class keyCls, - Class valCls - ) { - TypeDescriptor d = new TypeDescriptor(); - - d.keyClass(keyCls); - d.valueClass(valCls); - - d.affinityFieldName(BinaryContext.affinityFieldName(keyCls)); - - processAnnotationsInClass(true, d.keyCls, d, null); - processAnnotationsInClass(false, d.valCls, d, null); - - return d; - } - - /** - * Process annotations for class. - * - * @param key If given class relates to key. - * @param cls Class. - * @param type Type descriptor. - * @param parent Parent in case of embeddable. - */ - private static void processAnnotationsInClass(boolean key, Class cls, TypeDescriptor type, - @Nullable ClassProperty parent) { - if (U.isJdk(cls) || QueryUtils.isGeometryClass(cls)) { - if (parent == null && !key && QueryUtils.isSqlType(cls)) { // We have to index primitive _val. - String idxName = cls.getSimpleName() + "_" + QueryUtils.VAL_FIELD_NAME + "_idx"; - - type.addIndex(idxName, QueryUtils.isGeometryClass(cls) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); - - type.addFieldToIndex(idxName, QueryUtils.VAL_FIELD_NAME, 0, false); - } - - return; - } - - if (parent != null && parent.knowsClass(cls)) - throw new CacheException("Recursive reference found in type: " + cls.getName()); - - if (parent == null) { // Check class annotation at top level only. - QueryTextField txtAnnCls = cls.getAnnotation(QueryTextField.class); - - if (txtAnnCls != null) - type.valueTextIndex(true); - - QueryGroupIndex grpIdx = cls.getAnnotation(QueryGroupIndex.class); - - if (grpIdx != null) - type.addIndex(grpIdx.name(), QueryIndexType.SORTED); - - QueryGroupIndex.List grpIdxList = cls.getAnnotation(QueryGroupIndex.List.class); - - if (grpIdxList != null && !F.isEmpty(grpIdxList.value())) { - for (QueryGroupIndex idx : grpIdxList.value()) - type.addIndex(idx.name(), QueryIndexType.SORTED); - } - } - - for (Class c = cls; c != null && !c.equals(Object.class); c = c.getSuperclass()) { - for (Field field : c.getDeclaredFields()) { - QuerySqlField sqlAnn = field.getAnnotation(QuerySqlField.class); - QueryTextField txtAnn = field.getAnnotation(QueryTextField.class); - - if (sqlAnn != null || txtAnn != null) { - ClassProperty prop = new ClassProperty(field); - - prop.parent(parent); - - // Add parent property before its possible nested properties so that - // resulting parent column comes before columns corresponding to those - // nested properties in the resulting table - that way nested - // properties override will happen properly (first parent, then children). - type.addProperty(prop, key, true); - - processAnnotation(key, sqlAnn, txtAnn, cls, c, field.getType(), prop, type); - } - } - } - } - - /** - * Processes annotation at field or method. - * - * @param key If given class relates to key. - * @param sqlAnn SQL annotation, can be {@code null}. - * @param txtAnn H2 text annotation, can be {@code null}. - * @param cls Entity class. - * @param curCls Current entity class. - * @param fldCls Class of field or return type for method. - * @param prop Current property. - * @param desc Class description. - */ - private static void processAnnotation(boolean key, QuerySqlField sqlAnn, QueryTextField txtAnn, - Class cls, Class curCls, Class fldCls, ClassProperty prop, TypeDescriptor desc) { - if (sqlAnn != null) { - processAnnotationsInClass(key, fldCls, desc, prop); - - if (!sqlAnn.name().isEmpty()) - prop.alias(sqlAnn.name()); - - if (sqlAnn.index()) { - String idxName = curCls.getSimpleName() + "_" + prop.alias() + "_idx"; - - if (cls != curCls) - idxName = cls.getSimpleName() + "_" + idxName; - - desc.addIndex(idxName, QueryUtils.isGeometryClass(prop.type()) ? - QueryIndexType.GEOSPATIAL : QueryIndexType.SORTED); - - desc.addFieldToIndex(idxName, prop.fullName(), 0, sqlAnn.descending()); - } - - if (!F.isEmpty(sqlAnn.groups())) { - for (String group : sqlAnn.groups()) - desc.addFieldToIndex(group, prop.fullName(), 0, false); - } - - if (!F.isEmpty(sqlAnn.orderedGroups())) { - for (QuerySqlField.Group idx : sqlAnn.orderedGroups()) - desc.addFieldToIndex(idx.name(), prop.fullName(), idx.order(), idx.descending()); - } - } - - if (txtAnn != null) - desc.addFieldToTextIndex(prop.fullName()); - } /** {@inheritDoc} */ @Override public CacheConfiguration setStatisticsEnabled(boolean enabled) { @@ -2370,381 +2157,4 @@ public static class IgniteAllNodesPredicate implements IgnitePredicate> fields = new LinkedHashMap<>(); - - /** */ - @GridToStringExclude - private final Map props = new LinkedHashMap<>(); - - /** */ - @GridToStringInclude - private final Set keyProps = new HashSet<>(); - - /** */ - @GridToStringInclude - private final Map indexes = new HashMap<>(); - - /** */ - private IndexDescriptor fullTextIdx; - - /** */ - private Class keyCls; - - /** */ - private Class valCls; - - /** */ - private boolean valTextIdx; - - /** Affinity field name. */ - private String affFieldName; - - /** - * @return Indexes. - */ - public Map indexes() { - return Collections.unmodifiableMap(indexes); - } - - /** - * Adds index. - * - * @param idxName Index name. - * @param type Index type. - * @param inlineSize Inline size. - * @return Index descriptor. - */ - public IndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { - IndexDescriptor idx = new IndexDescriptor(type, inlineSize); - - if (indexes.put(idxName, idx) != null) - throw new CacheException("Index with name '" + idxName + "' already exists."); - - return idx; - } - - /** - * Adds index. - * - * @param idxName Index name. - * @param type Index type. - * @return Index descriptor. - */ - public IndexDescriptor addIndex(String idxName, QueryIndexType type) { - return addIndex(idxName, type, -1); - } - - /** - * Adds field to index. - * - * @param idxName Index name. - * @param field Field name. - * @param orderNum Fields order number in index. - * @param descending Sorting order. - */ - public void addFieldToIndex(String idxName, String field, int orderNum, - boolean descending) { - IndexDescriptor desc = indexes.get(idxName); - - if (desc == null) - desc = addIndex(idxName, QueryIndexType.SORTED); - - desc.addField(field, orderNum, descending); - } - - /** - * Adds field to text index. - * - * @param field Field name. - */ - public void addFieldToTextIndex(String field) { - if (fullTextIdx == null) { - fullTextIdx = new IndexDescriptor(QueryIndexType.FULLTEXT); - - indexes.put(null, fullTextIdx); - } - - fullTextIdx.addField(field, 0, false); - } - - /** - * @return Value class. - */ - public Class valueClass() { - return valCls; - } - - /** - * Sets value class. - * - * @param valCls Value class. - */ - void valueClass(Class valCls) { - this.valCls = valCls; - } - - /** - * @return Key class. - */ - public Class keyClass() { - return keyCls; - } - - /** - * Set key class. - * - * @param keyCls Key class. - */ - void keyClass(Class keyCls) { - this.keyCls = keyCls; - } - - /** - * @return Affinity field name. - */ - @Nullable public String affinityFieldName() { - return affFieldName; - } - - /** - * @param affFieldName Affinity field name. - */ - private void affinityFieldName(@Nullable String affFieldName) { - this.affFieldName = affFieldName; - } - - /** - * Adds property to the type descriptor. - * - * @param prop Property. - * @param key Property ownership flag (key or not). - * @param failOnDuplicate Fail on duplicate flag. - */ - void addProperty(ClassProperty prop, boolean key, boolean failOnDuplicate) { - String name = prop.fullName(); - - if (props.put(name, prop) != null && failOnDuplicate) - throw new CacheException("Property with name '" + name + "' already exists."); - - fields.put(name, prop.type()); - - if (key) - keyProps.add(name); - } - - /** - * @return {@code true} If we need to have a fulltext index on value. - */ - public boolean valueTextIndex() { - return valTextIdx; - } - - /** - * Sets if this value should be text indexed. - * - * @param valTextIdx Flag value. - */ - public void valueTextIndex(boolean valTextIdx) { - this.valTextIdx = valTextIdx; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(TypeDescriptor.class, this); - } - } - - /** - * Index descriptor. - */ - private static class IndexDescriptor implements GridQueryIndexDescriptor { - /** Fields sorted by order number. */ - private final Collection> fields = new TreeSet<>( - new Comparator>() { - @Override public int compare(T2 o1, T2 o2) { - if (o1.get2().equals(o2.get2())) // Order is equal, compare field names to avoid replace in Set. - return o1.get1().compareTo(o2.get1()); - - return o1.get2() < o2.get2() ? -1 : 1; - } - }); - - /** Fields which should be indexed in descending order. */ - private Collection descendings; - - /** */ - private final QueryIndexType type; - - /** */ - private final int inlineSize; - - /** - * @param type Type. - * @param inlineSize Inline size. - */ - private IndexDescriptor(QueryIndexType type, int inlineSize) { - assert type != null; - - this.type = type; - this.inlineSize = inlineSize; - } - - /** - * @param type Type. - */ - private IndexDescriptor(QueryIndexType type) { - this(type, -1); - } - - /** {@inheritDoc} */ - @Override public String name() { - return null; - } - - /** {@inheritDoc} */ - @Override public Collection fields() { - Collection res = new ArrayList<>(fields.size()); - - for (T2 t : fields) - res.add(t.get1()); - - return res; - } - - /** {@inheritDoc} */ - @Override public boolean descending(String field) { - return descendings != null && descendings.contains(field); - } - - /** - * Adds field to this index. - * - * @param field Field name. - * @param orderNum Field order number in this index. - * @param descending Sort order. - */ - public void addField(String field, int orderNum, boolean descending) { - fields.add(new T2<>(field, orderNum)); - - if (descending) { - if (descendings == null) - descendings = new HashSet<>(); - - descendings.add(field); - } - } - - /** {@inheritDoc} */ - @Override public QueryIndexType type() { - return type; - } - - /** {@inheritDoc} */ - @Override public int inlineSize() { - return inlineSize; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(IndexDescriptor.class, this); - } - } - - /** - * Description of type property. - */ - private static class ClassProperty { - /** */ - private final Member member; - - /** */ - private ClassProperty parent; - - /** */ - private String name; - - /** */ - private String alias; - - /** - * Constructor. - * - * @param member Element. - */ - ClassProperty(Member member) { - this.member = member; - - name = member.getName(); - - if (member instanceof Method) { - if (member.getName().startsWith("get") && member.getName().length() > 3) - name = member.getName().substring(3); - - if (member.getName().startsWith("is") && member.getName().length() > 2) - name = member.getName().substring(2); - } - - ((AccessibleObject)member).setAccessible(true); - } - - /** - * @param alias Alias. - */ - public void alias(String alias) { - this.alias = alias; - } - - /** - * @return Alias. - */ - String alias() { - return F.isEmpty(alias) ? name : alias; - } - - /** - * @return Type. - */ - public Class type() { - return member instanceof Field ? ((Field)member).getType() : ((Method)member).getReturnType(); - } - - /** - * @param parent Parent property if this is embeddable element. - */ - public void parent(ClassProperty parent) { - this.parent = parent; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return S.toString(ClassProperty.class, this); - } - - /** - * @param cls Class. - * @return {@code true} If this property or some parent relates to member of the given class. - */ - public boolean knowsClass(Class cls) { - return member.getDeclaringClass() == cls || (parent != null && parent.knowsClass(cls)); - } - - /** - * @return Full name with all parents in dot notation. - */ - public String fullName() { - assert name != null; - - if (parent == null) - return name; - - return parent.fullName() + '.' + name; - } - } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/ClassProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/ClassProperty.java new file mode 100644 index 0000000000000..cbdd730430a2a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/ClassProperty.java @@ -0,0 +1,116 @@ +/* + * 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.internal.processors.cache.config; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Description of type property. + */ +public class ClassProperty { + /** */ + private final Member member; + + /** */ + private ClassProperty parent; + + /** */ + private String name; + + /** */ + private String alias; + + /** + * Constructor. + * + * @param member Element. + */ + public ClassProperty(Member member) { + this.member = member; + + name = member.getName(); + + if (member instanceof Method) { + if (member.getName().startsWith("get") && member.getName().length() > 3) + name = member.getName().substring(3); + + if (member.getName().startsWith("is") && member.getName().length() > 2) + name = member.getName().substring(2); + } + + ((AccessibleObject)member).setAccessible(true); + } + + /** + * @param alias Alias. + */ + public void alias(String alias) { + this.alias = alias; + } + + /** + * @return Alias. + */ + public String alias() { + return F.isEmpty(alias) ? name : alias; + } + + /** + * @return Type. + */ + public Class type() { + return member instanceof Field ? ((Field)member).getType() : ((Method)member).getReturnType(); + } + + /** + * @param parent Parent property if this is embeddable element. + */ + public void parent(ClassProperty parent) { + this.parent = parent; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(ClassProperty.class, this); + } + + /** + * @param cls Class. + * @return {@code true} If this property or some parent relates to member of the given class. + */ + public boolean knowsClass(Class cls) { + return member.getDeclaringClass() == cls || (parent != null && parent.knowsClass(cls)); + } + + /** + * @return Full name with all parents in dot notation. + */ + public String fullName() { + assert name != null; + + if (parent == null) + return name; + + return parent.fullName() + '.' + name; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/IndexDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/IndexDescriptor.java new file mode 100644 index 0000000000000..0e45f980babee --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/IndexDescriptor.java @@ -0,0 +1,121 @@ +/* + * 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.internal.processors.cache.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.TreeSet; +import org.apache.ignite.cache.QueryIndexType; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Index descriptor. + */ +public class IndexDescriptor implements GridQueryIndexDescriptor { + /** Fields sorted by order number. */ + private final Collection> fields = new TreeSet<>( + new Comparator>() { + @Override public int compare(T2 o1, T2 o2) { + if (o1.get2().equals(o2.get2())) // Order is equal, compare field names to avoid replace in Set. + return o1.get1().compareTo(o2.get1()); + + return o1.get2() < o2.get2() ? -1 : 1; + } + }); + /** */ + private final QueryIndexType type; + /** */ + private final int inlineSize; + /** Fields which should be indexed in descending order. */ + private Collection descendings; + + /** + * @param type Type. + * @param inlineSize Inline size. + */ + IndexDescriptor(QueryIndexType type, int inlineSize) { + assert type != null; + + this.type = type; + this.inlineSize = inlineSize; + } + + /** + * @param type Type. + */ + IndexDescriptor(QueryIndexType type) { + this(type, -1); + } + + /** {@inheritDoc} */ + @Override public String name() { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection fields() { + Collection res = new ArrayList<>(fields.size()); + + for (T2 t : fields) + res.add(t.get1()); + + return res; + } + + /** {@inheritDoc} */ + @Override public boolean descending(String field) { + return descendings != null && descendings.contains(field); + } + + /** + * Adds field to this index. + * + * @param field Field name. + * @param orderNum Field order number in this index. + * @param descending Sort order. + */ + public void addField(String field, int orderNum, boolean descending) { + fields.add(new T2<>(field, orderNum)); + + if (descending) { + if (descendings == null) + descendings = new HashSet<>(); + + descendings.add(field); + } + } + + /** {@inheritDoc} */ + @Override public QueryIndexType type() { + return type; + } + + /** {@inheritDoc} */ + @Override public int inlineSize() { + return inlineSize; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(IndexDescriptor.class, this); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/TypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/TypeDescriptor.java new file mode 100644 index 0000000000000..b2604b9b05f9b --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/config/TypeDescriptor.java @@ -0,0 +1,219 @@ +/* + * 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.internal.processors.cache.config; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import javax.cache.CacheException; +import org.apache.ignite.cache.QueryIndexType; +import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; +import org.apache.ignite.internal.util.tostring.GridToStringExclude; +import org.apache.ignite.internal.util.tostring.GridToStringInclude; +import org.apache.ignite.internal.util.typedef.internal.S; + +/** + * Descriptor of type. + */ +public class TypeDescriptor { + /** Value field names and types with preserved order. */ + @GridToStringInclude + private final Map> fields = new LinkedHashMap<>(); + + /** */ + @GridToStringExclude + private final Map props = new LinkedHashMap<>(); + + /** */ + @GridToStringInclude + private final Set keyProps = new HashSet<>(); + + /** */ + @GridToStringInclude + private final Map indexes = new HashMap<>(); + + /** */ + private IndexDescriptor fullTextIdx; + + /** */ + private Class keyCls; + + /** */ + private Class valCls; + + /** */ + private boolean valTextIdx; + + /** + * @return Indexes. + */ + public Map indexes() { + return Collections.unmodifiableMap(indexes); + } + + /** + * Adds index. + * + * @param idxName Index name. + * @param type Index type. + * @param inlineSize Inline size. + * @return Index descriptor. + */ + public IndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { + IndexDescriptor idx = new IndexDescriptor(type, inlineSize); + + if (indexes.put(idxName, idx) != null) + throw new CacheException("Index with name '" + idxName + "' already exists."); + + return idx; + } + + /** + * Adds index. + * + * @param idxName Index name. + * @param type Index type. + * @return Index descriptor. + */ + public IndexDescriptor addIndex(String idxName, QueryIndexType type) { + return addIndex(idxName, type, -1); + } + + /** + * Adds field to index. + * + * @param idxName Index name. + * @param field Field name. + * @param orderNum Fields order number in index. + * @param descending Sorting order. + */ + public void addFieldToIndex(String idxName, String field, int orderNum, + boolean descending) { + IndexDescriptor desc = indexes.get(idxName); + + if (desc == null) + desc = addIndex(idxName, QueryIndexType.SORTED); + + desc.addField(field, orderNum, descending); + } + + /** + * Adds field to text index. + * + * @param field Field name. + */ + public void addFieldToTextIndex(String field) { + if (fullTextIdx == null) { + fullTextIdx = new IndexDescriptor(QueryIndexType.FULLTEXT); + + indexes.put(null, fullTextIdx); + } + + fullTextIdx.addField(field, 0, false); + } + + /** + * @return Value class. + */ + public Class valueClass() { + return valCls; + } + + /** + * Sets value class. + * + * @param valCls Value class. + */ + public void valueClass(Class valCls) { + this.valCls = valCls; + } + + /** + * @return Key class. + */ + public Class keyClass() { + return keyCls; + } + + /** + * Set key class. + * + * @param keyCls Key class. + */ + public void keyClass(Class keyCls) { + this.keyCls = keyCls; + } + + /** + * Adds property to the type descriptor. + * + * @param prop Property. + * @param key Property ownership flag (key or not). + * @param failOnDuplicate Fail on duplicate flag. + */ + public void addProperty(ClassProperty prop, boolean key, boolean failOnDuplicate) { + String name = prop.fullName(); + + if (props.put(name, prop) != null && failOnDuplicate) + throw new CacheException("Property with name '" + name + "' already exists."); + + fields.put(name, prop.type()); + + if (key) + keyProps.add(name); + } + + /** + * @return Class properties. + */ + public Map properties() { + return props; + } + + /** + * @return Properties keys. + */ + public Set keyProperties() { + return keyProps; + } + + /** + * @return {@code true} If we need to have a fulltext index on value. + */ + public boolean valueTextIndex() { + return valTextIdx; + } + + /** + * Sets if this value should be text indexed. + * + * @param valTextIdx Flag value. + */ + public void valueTextIndex(boolean valTextIdx) { + this.valTextIdx = valTextIdx; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(TypeDescriptor.class, this); + } +} + diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java index 547531156fc95..1ddd252bb8120 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java @@ -25,6 +25,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -34,6 +35,7 @@ import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.CacheWriteSynchronizationMode; +import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; @@ -94,7 +96,7 @@ protected CacheConfiguration cacheConfiguration(String name, CacheMode mode, Cla cc.setCacheMode(mode); cc.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); cc.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL); - cc.setIndexedTypes(clsK, clsV); + cc.setQueryEntities(Collections.singleton(new QueryEntity(clsK, clsV))); return cc; } From e8a7317787a6b89e793745b37c53a8b6b9939136 Mon Sep 17 00:00:00 2001 From: tledkov-gridgain Date: Thu, 3 Aug 2017 17:48:01 +0300 Subject: [PATCH 2/2] IGNITE-5211 minors --- .../org/apache/ignite/configuration/CacheConfiguration.java | 4 ---- 1 file changed, 4 deletions(-) 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 866b88080e0e9..bbd40cb8d9c3a 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 @@ -18,10 +18,6 @@ package org.apache.ignite.configuration; import java.io.Serializable; -import java.lang.reflect.AccessibleObject; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections;