IGNITE-5211: Added new constructor: QueryEntity(Class keyCls, Class valCls). This closes #2371. This closes #2388. This closes #2407.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/9da69389 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/9da69389 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/9da69389 Branch: refs/heads/ignite-5872 Commit: 9da69389ed307357391e4eb87ae077751f30a58e Parents: 488a6d2 Author: tledkov-gridgain <tled...@gridgain.com> Authored: Tue Aug 8 12:05:36 2017 +0300 Committer: devozerov <voze...@gridgain.com> Committed: Tue Aug 8 12:05:36 2017 +0300 ---------------------------------------------------------------------- .../org/apache/ignite/cache/QueryEntity.java | 229 +++++++ .../configuration/CacheConfiguration.java | 615 +------------------ .../cache/query/QueryEntityClassProperty.java | 116 ++++ .../cache/query/QueryEntityIndexDescriptor.java | 121 ++++ .../cache/query/QueryEntityTypeDescriptor.java | 219 +++++++ .../h2/sql/AbstractH2CompareQueryTest.java | 4 +- 6 files changed, 693 insertions(+), 611 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/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 index 955e7d2..5180100 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.LinkedHashMap; 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.query.QueryEntityClassProperty; +import org.apache.ignite.internal.processors.cache.query.QueryEntityTypeDescriptor; +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) @@ -111,6 +123,16 @@ public class QueryEntity implements Serializable { } /** + * 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. * * @return Key type. @@ -351,6 +373,213 @@ public class QueryEntity implements Serializable { return this; } + /** + * @param desc Type descriptor. + * @return Type metadata. + */ + private static QueryEntity convert(QueryEntityTypeDescriptor desc) { + QueryEntity entity = new QueryEntity(); + + // Key and val types. + entity.setKeyType(desc.keyClass().getName()); + entity.setValueType(desc.valueClass().getName()); + + for (QueryEntityClassProperty prop : desc.properties().values()) + entity.addQueryField(prop.fullName(), U.box(prop.type()).getName(), prop.alias()); + + entity.setKeyFields(desc.keyProperties()); + + QueryIndex txtIdx = null; + + Collection<QueryIndex> idxs = new ArrayList<>(); + + for (Map.Entry<String, GridQueryIndexDescriptor> 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<String, Boolean> 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 QueryEntityTypeDescriptor processKeyAndValueClasses( + Class<?> keyCls, + Class<?> valCls + ) { + QueryEntityTypeDescriptor d = new QueryEntityTypeDescriptor(); + + 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, QueryEntityTypeDescriptor type, + @Nullable QueryEntityClassProperty 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) { + QueryEntityClassProperty prop = new QueryEntityClassProperty(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, QueryEntityClassProperty prop, QueryEntityTypeDescriptor 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) http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/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 708913a..2b4ec1d 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,21 +18,10 @@ 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.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.Map; -import java.util.Set; -import java.util.TreeSet; import javax.cache.Cache; import javax.cache.CacheException; import javax.cache.configuration.CacheEntryListenerConfiguration; @@ -53,26 +42,17 @@ import org.apache.ignite.cache.CacheRebalanceMode; 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; import org.apache.ignite.internal.binary.BinaryContext; -import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; import org.apache.ignite.internal.processors.query.QueryUtils; -import org.apache.ignite.internal.util.tostring.GridToStringExclude; -import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.T2; 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; @@ -1768,14 +1748,12 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { Class<?> keyCls = newIndexedTypes[i]; Class<?> valCls = newIndexedTypes[i + 1]; - TypeDescriptor desc = processKeyAndValueClasses(keyCls, valCls); - - QueryEntity converted = convert(desc); + QueryEntity newEntity = new QueryEntity(keyCls, valCls); boolean dup = false; for (QueryEntity entity : qryEntities) { - if (F.eq(entity.findValueType(), converted.findValueType())) { + if (F.eq(entity.findValueType(), newEntity.findValueType())) { dup = true; break; @@ -1783,13 +1761,13 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { } if (!dup) - qryEntities.add(converted); + qryEntities.add(newEntity); // 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(newEntity.getKeyType(), affFieldName); if (F.isEmpty(keyCfg)) keyCfg = new CacheKeyConfiguration[] { newKeyCfg }; @@ -2041,77 +2019,6 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { 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<QueryIndex> idxs = new ArrayList<>(); - - for (Map.Entry<String, GridQueryIndexDescriptor> 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<String, Boolean> 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 +2030,6 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { 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<K, V> setStatisticsEnabled(boolean enabled) { @@ -2370,381 +2142,4 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { return "IgniteAllNodesPredicate []"; } } - - /** - * Descriptor of type. - */ - private static class TypeDescriptor { - /** Value field names and types with preserved order. */ - @GridToStringInclude - private final Map<String, Class<?>> fields = new LinkedHashMap<>(); - - /** */ - @GridToStringExclude - private final Map<String, ClassProperty> props = new LinkedHashMap<>(); - - /** */ - @GridToStringInclude - private final Set<String> keyProps = new HashSet<>(); - - /** */ - @GridToStringInclude - private final Map<String, IndexDescriptor> 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<String, GridQueryIndexDescriptor> indexes() { - return Collections.<String, GridQueryIndexDescriptor>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<T2<String, Integer>> fields = new TreeSet<>( - new Comparator<T2<String, Integer>>() { - @Override public int compare(T2<String, Integer> o1, T2<String, Integer> 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<String> 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<String> fields() { - Collection<String> res = new ArrayList<>(fields.size()); - - for (T2<String, Integer> 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; - } - } } http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.java new file mode 100644 index 0000000..e21b73c --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityClassProperty.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.query; + +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 QueryEntityClassProperty { + /** */ + private final Member member; + + /** */ + private QueryEntityClassProperty parent; + + /** */ + private String name; + + /** */ + private String alias; + + /** + * Constructor. + * + * @param member Element. + */ + public QueryEntityClassProperty(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(QueryEntityClassProperty parent) { + this.parent = parent; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(QueryEntityClassProperty.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; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.java new file mode 100644 index 0000000..2fb5837 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityIndexDescriptor.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.query; + +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 QueryEntityIndexDescriptor implements GridQueryIndexDescriptor { + /** Fields sorted by order number. */ + private final Collection<T2<String, Integer>> fields = new TreeSet<>( + new Comparator<T2<String, Integer>>() { + @Override public int compare(T2<String, Integer> o1, T2<String, Integer> 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<String> descendings; + + /** + * @param type Type. + * @param inlineSize Inline size. + */ + QueryEntityIndexDescriptor(QueryIndexType type, int inlineSize) { + assert type != null; + + this.type = type; + this.inlineSize = inlineSize; + } + + /** + * @param type Type. + */ + QueryEntityIndexDescriptor(QueryIndexType type) { + this(type, -1); + } + + /** {@inheritDoc} */ + @Override public String name() { + return null; + } + + /** {@inheritDoc} */ + @Override public Collection<String> fields() { + Collection<String> res = new ArrayList<>(fields.size()); + + for (T2<String, Integer> 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(QueryEntityIndexDescriptor.class, this); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.java new file mode 100644 index 0000000..47ab263 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/QueryEntityTypeDescriptor.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.query; + +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 QueryEntityTypeDescriptor { + /** Value field names and types with preserved order. */ + @GridToStringInclude + private final Map<String, Class<?>> fields = new LinkedHashMap<>(); + + /** */ + @GridToStringExclude + private final Map<String, QueryEntityClassProperty> props = new LinkedHashMap<>(); + + /** */ + @GridToStringInclude + private final Set<String> keyProps = new HashSet<>(); + + /** */ + @GridToStringInclude + private final Map<String, QueryEntityIndexDescriptor> indexes = new HashMap<>(); + + /** */ + private QueryEntityIndexDescriptor fullTextIdx; + + /** */ + private Class<?> keyCls; + + /** */ + private Class<?> valCls; + + /** */ + private boolean valTextIdx; + + /** + * @return Indexes. + */ + public Map<String, GridQueryIndexDescriptor> indexes() { + return Collections.<String, GridQueryIndexDescriptor>unmodifiableMap(indexes); + } + + /** + * Adds index. + * + * @param idxName Index name. + * @param type Index type. + * @param inlineSize Inline size. + * @return Index descriptor. + */ + public QueryEntityIndexDescriptor addIndex(String idxName, QueryIndexType type, int inlineSize) { + QueryEntityIndexDescriptor idx = new QueryEntityIndexDescriptor(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 QueryEntityIndexDescriptor 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) { + QueryEntityIndexDescriptor 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 QueryEntityIndexDescriptor(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(QueryEntityClassProperty 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<String, QueryEntityClassProperty> properties() { + return props; + } + + /** + * @return Properties keys. + */ + public Set<String> 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(QueryEntityTypeDescriptor.class, this); + } +} + http://git-wip-us.apache.org/repos/asf/ignite/blob/9da69389/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/sql/AbstractH2CompareQueryTest.java ---------------------------------------------------------------------- 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 5475311..1ddd252 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.ResultSetMetaData; 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.IgniteCache; 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 @@ public abstract class AbstractH2CompareQueryTest extends GridCommonAbstractTest 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; }