Repository: cassandra Updated Branches: refs/heads/cassandra-2.1 684b4f96f -> 5117a5d5b
Fix issues w/ CONTAINS (KEY) queries on 2ary indexes Patch by Benjamin Lerer; reviewed by Tyler Hobbs for CASSANDRA-8147 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/52a701f2 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/52a701f2 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/52a701f2 Branch: refs/heads/cassandra-2.1 Commit: 52a701f295490cf10850b2a6ad8fb3fdcbc57211 Parents: 684b4f9 Author: blerer <b_le...@hotmail.com> Authored: Tue Nov 4 10:16:57 2014 -0600 Committer: Tyler Hobbs <ty...@datastax.com> Committed: Tue Nov 4 10:16:57 2014 -0600 ---------------------------------------------------------------------- CHANGES.txt | 2 + pylib/cqlshlib/cql3handling.py | 4 +- .../org/apache/cassandra/config/CFMetaData.java | 2 +- .../cassandra/config/ColumnDefinition.java | 12 +++++ .../org/apache/cassandra/cql3/Relation.java | 57 ++++++++++++++++---- .../cql3/statements/CreateIndexStatement.java | 6 ++- .../cql3/statements/SelectStatement.java | 2 +- .../apache/cassandra/db/IndexExpression.java | 44 +++++++++++++++ .../cassandra/db/index/SecondaryIndex.java | 23 +++++++- .../db/index/SecondaryIndexManager.java | 2 +- .../db/index/SecondaryIndexSearcher.java | 7 +-- .../db/index/composites/CompositesIndex.java | 9 ++-- .../CompositesIndexOnCollectionKey.java | 7 +++ .../CompositesIndexOnCollectionValue.java | 6 +++ .../cassandra/db/marshal/CollectionType.java | 9 ++++ .../cassandra/cql3/ContainsRelationTest.java | 44 +++++++++++++++ 16 files changed, 211 insertions(+), 25 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index 37b3f83..7fb32fe 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,6 @@ 2.1.2 + * Fix issues with CONTAINS (KEY) queries on secondary indexes + (CASSANDRA-8147) * Fix read-rate tracking of sstables for some queries (CASSANDRA-8239) * Fix default timestamp in QueryOptions (CASSANDRA-8246) * Set socket timeout when reading remote version (CASSANDRA-8188) http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/pylib/cqlshlib/cql3handling.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index 8d2fec5..e12e7e1 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -599,10 +599,10 @@ syntax_rules += r''' ; <whereClause> ::= <relation> ( "AND" <relation> )* ; -<relation> ::= [rel_lhs]=<cident> ( "=" | "<" | ">" | "<=" | ">=" | "CONTAINS" ) <term> +<relation> ::= [rel_lhs]=<cident> ( "=" | "<" | ">" | "<=" | ">=" | "CONTAINS" ( "KEY" )? ) <term> | token="TOKEN" "(" [rel_tokname]=<cident> ( "," [rel_tokname]=<cident> )* - ")" ("=" | "<" | ">" | "<=" | ">=" | "CONTAINS") <tokenDefinition> + ")" ("=" | "<" | ">" | "<=" | ">=") <tokenDefinition> | [rel_lhs]=<cident> "IN" "(" <term> ( "," <term> )* ")" ; <selectClause> ::= "DISTINCT"? <selector> ("AS" <cident>)? ("," <selector> ("AS" <cident>)?)* http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/config/CFMetaData.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java index b5784ed..d986c40 100644 --- a/src/java/org/apache/cassandra/config/CFMetaData.java +++ b/src/java/org/apache/cassandra/config/CFMetaData.java @@ -1540,7 +1540,7 @@ public final class CFMetaData if (c.getIndexType() == IndexType.CUSTOM) { - if (c.getIndexOptions() == null || !c.getIndexOptions().containsKey(SecondaryIndex.CUSTOM_INDEX_OPTION_NAME)) + if (c.getIndexOptions() == null || !c.hasIndexOption(SecondaryIndex.CUSTOM_INDEX_OPTION_NAME)) throw new ConfigurationException("Required index option missing: " + SecondaryIndex.CUSTOM_INDEX_OPTION_NAME); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/config/ColumnDefinition.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/config/ColumnDefinition.java b/src/java/org/apache/cassandra/config/ColumnDefinition.java index cbb3e75..ff66162 100644 --- a/src/java/org/apache/cassandra/config/ColumnDefinition.java +++ b/src/java/org/apache/cassandra/config/ColumnDefinition.java @@ -476,4 +476,16 @@ public class ColumnDefinition extends ColumnSpecification { return indexOptions; } + + /** + * Checks if the index option with the specified name has been specified. + * + * @param name index option name + * @return <code>true</code> if the index option with the specified name has been specified, <code>false</code> + * otherwise. + */ + public boolean hasIndexOption(String name) + { + return indexOptions.containsKey(name); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/cql3/Relation.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/Relation.java b/src/java/org/apache/cassandra/cql3/Relation.java index 42373c3..c6a7c65 100644 --- a/src/java/org/apache/cassandra/cql3/Relation.java +++ b/src/java/org/apache/cassandra/cql3/Relation.java @@ -17,6 +17,10 @@ */ package org.apache.cassandra.cql3; +import org.apache.cassandra.config.ColumnDefinition; +import org.apache.cassandra.db.index.SecondaryIndex; +import org.apache.cassandra.db.marshal.CollectionType; + public abstract class Relation { @@ -24,19 +28,50 @@ public abstract class Relation { public static enum Type { - EQ, LT, LTE, GTE, GT, IN, CONTAINS, CONTAINS_KEY, NEQ; - - public boolean allowsIndexQuery() + EQ { - switch (this) + public boolean allowsIndexQueryOn(ColumnDefinition columnDef) { - case EQ: - case CONTAINS: - case CONTAINS_KEY: - return true; - default: - return false; + return columnDef.isIndexed(); } + }, + LT, + LTE, + GTE, + GT, + IN, + CONTAINS + { + public boolean allowsIndexQueryOn(ColumnDefinition columnDef) + { + return columnDef.isIndexed() + && columnDef.type.isCollection() + && (!((CollectionType<?>) columnDef.type).isMap() + || columnDef.hasIndexOption(SecondaryIndex.INDEX_VALUES_OPTION_NAME)); + } + }, + CONTAINS_KEY + { + public boolean allowsIndexQueryOn(ColumnDefinition columnDef) + { + return columnDef.isIndexed() + && columnDef.type.isCollection() + && (!((CollectionType<?>) columnDef.type).isMap() + || columnDef.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME)); + } + }, + NEQ; + + /** + * Checks if this relation type allow index queries on the specified column + * + * @param columnDef the column definition. + * @return <code>true</code> if this relation type allow index queries on the specified column, + * <code>false</code> otherwise. + */ + public boolean allowsIndexQueryOn(ColumnDefinition columnDef) + { + return false; } @Override @@ -56,6 +91,8 @@ public abstract class Relation { return ">="; case NEQ: return "!="; + case CONTAINS_KEY: + return "CONTAINS KEY"; default: return this.name(); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java index 4809187..fc5c4d1 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java @@ -29,6 +29,7 @@ import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.config.IndexType; import org.apache.cassandra.config.Schema; +import org.apache.cassandra.db.index.SecondaryIndex; import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.cql3.*; @@ -82,7 +83,7 @@ public class CreateIndexStatement extends SchemaAlteringStatement if (cd.getIndexType() != null) { - boolean previousIsKeys = cd.getIndexOptions().containsKey("index_keys"); + boolean previousIsKeys = cd.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME); if (isMap && target.isCollectionKeys != previousIsKeys) { String msg = "Cannot create index on %s %s, an index on %s %s already exists and indexing " @@ -137,7 +138,8 @@ public class CreateIndexStatement extends SchemaAlteringStatement // to also index map keys, so we record that this is the values we index to make our // lives easier then. if (cd.type.isCollection()) - options = ImmutableMap.of(target.isCollectionKeys ? "index_keys" : "index_values", ""); + options = ImmutableMap.of(target.isCollectionKeys ? SecondaryIndex.INDEX_KEYS_OPTION_NAME + : SecondaryIndex.INDEX_VALUES_OPTION_NAME, ""); cd.setIndexType(IndexType.COMPOSITES, options); } else http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java index 3d57f4c..3354c0b 100644 --- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java @@ -1496,7 +1496,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache handleUnrecognizedEntity(entity, relation); stmt.restrictedColumns.add(def); - if (def.isIndexed() && relation.operator().allowsIndexQuery()) + if (relation.operator().allowsIndexQueryOn(def)) return new boolean[]{true, def.kind == ColumnDefinition.Kind.CLUSTERING_COLUMN}; return new boolean[]{false, false}; } http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/IndexExpression.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/IndexExpression.java b/src/java/org/apache/cassandra/db/IndexExpression.java index b57890a..910bfbc 100644 --- a/src/java/org/apache/cassandra/db/IndexExpression.java +++ b/src/java/org/apache/cassandra/db/IndexExpression.java @@ -76,6 +76,50 @@ public class IndexExpression return false; } } + + @Override + public String toString() + { + switch (this) + { + case EQ: + return "="; + case LT: + return "<"; + case LTE: + return "<="; + case GT: + return ">"; + case GTE: + return ">="; + case CONTAINS_KEY: + return "CONTAINS KEY"; + default: + return this.name(); + } + } + } + + /** + * Checks if the operator of this <code>IndexExpression</code> is a <code>CONTAINS</code> operator. + * + * @return <code>true</code> if the operator of this <code>IndexExpression</code> is a <code>CONTAINS</code> + * operator, <code>false</code> otherwise. + */ + public boolean isContains() + { + return Operator.CONTAINS == operator; + } + + /** + * Checks if the operator of this <code>IndexExpression</code> is a <code>CONTAINS_KEY</code> operator. + * + * @return <code>true</code> if the operator of this <code>IndexExpression</code> is a <code>CONTAINS_KEY</code> + * operator, <code>false</code> otherwise. + */ + public boolean isContainsKey() + { + return Operator.CONTAINS_KEY == operator; } @Override http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/SecondaryIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndex.java b/src/java/org/apache/cassandra/db/index/SecondaryIndex.java index 4d50fa6..e3ed73c 100644 --- a/src/java/org/apache/cassandra/db/index/SecondaryIndex.java +++ b/src/java/org/apache/cassandra/db/index/SecondaryIndex.java @@ -32,11 +32,14 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.config.ColumnDefinition; + import org.apache.cassandra.db.BufferDecoratedKey; import org.apache.cassandra.db.Cell; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.DecoratedKey; +import org.apache.cassandra.db.IndexExpression; import org.apache.cassandra.db.SystemKeyspace; + import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; @@ -45,6 +48,7 @@ import org.apache.cassandra.db.index.composites.CompositesIndex; import org.apache.cassandra.db.index.keys.KeysIndex; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.BytesType; +import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.LocalByPartionerType; import org.apache.cassandra.dht.LocalToken; import org.apache.cassandra.exceptions.ConfigurationException; @@ -52,7 +56,8 @@ import org.apache.cassandra.io.sstable.ReducingKeyIterator; import org.apache.cassandra.io.sstable.SSTableReader; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.memory.MemtableAllocator; + +import com.google.common.collect.Iterables; /** * Abstract base class for different types of secondary indexes. @@ -65,6 +70,16 @@ public abstract class SecondaryIndex public static final String CUSTOM_INDEX_OPTION_NAME = "class_name"; + /** + * The name of the option used to specify that the index is on the collection keys. + */ + public static final String INDEX_KEYS_OPTION_NAME = "index_keys"; + + /** + * The name of the option used to specify that the index is on the collection values. + */ + public static final String INDEX_VALUES_OPTION_NAME = "index_values"; + public static final AbstractType<?> keyComparator = StorageService.getPartitioner().preservesOrder() ? BytesType.instance : new LocalByPartionerType(StorageService.getPartitioner()); @@ -281,6 +296,12 @@ public abstract class SecondaryIndex } } + /** Returns true if the index supports lookups for the given operator, false otherwise. */ + public boolean supportsOperator(IndexExpression.Operator operator) + { + return operator == IndexExpression.Operator.EQ; + } + /** * Returns the decoratedKey for a column value * @param value column value http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java index c2d481b..976bbb8 100644 --- a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java +++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java @@ -528,7 +528,7 @@ public class SecondaryIndexManager { SecondaryIndex index = getIndexForColumn(ix.column); - if (index == null) + if (index == null || !index.supportsOperator(ix.operator)) continue; Set<ByteBuffer> columns = groupByIndexType.get(index.indexTypeForGrouping()); http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java index 1239c29..b9ccd8e 100644 --- a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java +++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java @@ -54,11 +54,11 @@ public abstract class SecondaryIndexSearcher { for (IndexExpression expression : clause) { - if (!columns.contains(expression.column) || !expression.operator.allowsIndexQuery()) + if (!columns.contains(expression.column)) continue; SecondaryIndex index = indexManager.getIndexForColumn(expression.column); - if (index != null && index.getIndexCfs() != null) + if (index != null && index.getIndexCfs() != null && index.supportsOperator(expression.operator)) return true; } return false; @@ -88,8 +88,9 @@ public abstract class SecondaryIndexSearcher continue; SecondaryIndex index = indexManager.getIndexForColumn(expression.column); - if (index == null || index.getIndexCfs() == null || !expression.operator.allowsIndexQuery()) + if (index == null || index.getIndexCfs() == null || !index.supportsOperator(expression.operator)) continue; + int columns = index.getIndexCfs().getMeanColumns(); candidates.put(index, columns); if (columns < bestMeanCount) http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java index f69f716..410ea83 100644 --- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java +++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java @@ -30,6 +30,7 @@ import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.index.AbstractSimplePerColumnSecondaryIndex; +import org.apache.cassandra.db.index.SecondaryIndex; import org.apache.cassandra.db.index.SecondaryIndexManager; import org.apache.cassandra.db.index.SecondaryIndexSearcher; import org.apache.cassandra.db.marshal.AbstractType; @@ -66,7 +67,7 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn case SET: return new CompositesIndexOnCollectionKey(); case MAP: - return cfDef.getIndexOptions().containsKey("index_keys") + return cfDef.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME) ? new CompositesIndexOnCollectionKey() : new CompositesIndexOnCollectionValue(); } @@ -98,7 +99,7 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn case SET: return CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef); case MAP: - return cfDef.getIndexOptions().containsKey("index_keys") + return cfDef.hasIndexOption(SecondaryIndex.INDEX_KEYS_OPTION_NAME) ? CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef) : CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef); } @@ -159,8 +160,8 @@ public abstract class CompositesIndex extends AbstractSimplePerColumnSecondaryIn if (columnDef.type.isCollection()) { - options.remove("index_values"); - options.remove("index_keys"); + options.remove(SecondaryIndex.INDEX_VALUES_OPTION_NAME); + options.remove(SecondaryIndex.INDEX_KEYS_OPTION_NAME); } if (!options.isEmpty()) http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionKey.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionKey.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionKey.java index c252546..1067a94 100644 --- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionKey.java +++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionKey.java @@ -89,6 +89,13 @@ public class CompositesIndexOnCollectionKey extends CompositesIndex } @Override + public boolean supportsOperator(IndexExpression.Operator operator) + { + return operator == IndexExpression.Operator.CONTAINS_KEY || + operator == IndexExpression.Operator.CONTAINS && columnDef.type instanceof SetType; + } + + @Override public boolean indexes(CellName name) { // We index if the CQL3 column name is the one of the collection we index http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java index 7a8c552..5b4aa64 100644 --- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java +++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndexOnCollectionValue.java @@ -95,6 +95,12 @@ public class CompositesIndexOnCollectionValue extends CompositesIndex } @Override + public boolean supportsOperator(IndexExpression.Operator operator) + { + return operator == IndexExpression.Operator.CONTAINS; + } + + @Override public boolean indexes(CellName name) { AbstractType<?> comp = baseCfs.metadata.getColumnDefinitionComparator(columnDef); http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/src/java/org/apache/cassandra/db/marshal/CollectionType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/CollectionType.java b/src/java/org/apache/cassandra/db/marshal/CollectionType.java index 8a5fe5c..e63f2a5 100644 --- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java @@ -115,6 +115,15 @@ public abstract class CollectionType<T> extends AbstractType<T> return true; } + /** + * Checks if this collection is Map. + * @return <code>true</code> if this collection is a Map, <code>false</code> otherwise. + */ + public boolean isMap() + { + return kind == Kind.MAP; + } + protected List<Cell> enforceLimit(List<Cell> cells, int version) { if (version >= 3 || cells.size() <= MAX_ELEMENTS) http://git-wip-us.apache.org/repos/asf/cassandra/blob/52a701f2/test/unit/org/apache/cassandra/cql3/ContainsRelationTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/cql3/ContainsRelationTest.java b/test/unit/org/apache/cassandra/cql3/ContainsRelationTest.java index f854ec6..335636b 100644 --- a/test/unit/org/apache/cassandra/cql3/ContainsRelationTest.java +++ b/test/unit/org/apache/cassandra/cql3/ContainsRelationTest.java @@ -204,4 +204,48 @@ public class ContainsRelationTest extends CQLTester row(0, 1, 1, set(3, 4, 5)) ); } + + @Test + public void testContainsKeyAndContainsWithIndexOnMapKey() throws Throwable + { + createTable("CREATE TABLE %s (account text, id int, categories map<text,text>, PRIMARY KEY (account, id))"); + createIndex("CREATE INDEX ON %s(keys(categories))"); + + execute("INSERT INTO %s (account, id , categories) VALUES (?, ?, ?)", "test", 5, map("lmn", "foo")); + execute("INSERT INTO %s (account, id , categories) VALUES (?, ?, ?)", "test", 6, map("lmn", "foo2")); + + assertInvalid("SELECT * FROM %s WHERE account = ? AND categories CONTAINS ?", "test", "foo"); + + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS KEY ?", "test", "lmn"), + row("test", 5, map("lmn", "foo")), + row("test", 6, map("lmn", "foo2"))); + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS KEY ? AND categories CONTAINS ? ALLOW FILTERING", + "test", "lmn", "foo"), + row("test", 5, map("lmn", "foo"))); + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS ? AND categories CONTAINS KEY ? ALLOW FILTERING", + "test", "foo", "lmn"), + row("test", 5, map("lmn", "foo"))); + } + + @Test + public void testContainsKeyAndContainsWithIndexOnMapValue() throws Throwable + { + createTable("CREATE TABLE %s (account text, id int, categories map<text,text>, PRIMARY KEY (account, id))"); + createIndex("CREATE INDEX ON %s(categories)"); + + execute("INSERT INTO %s (account, id , categories) VALUES (?, ?, ?)", "test", 5, map("lmn", "foo")); + execute("INSERT INTO %s (account, id , categories) VALUES (?, ?, ?)", "test", 6, map("lmn2", "foo")); + + assertInvalid("SELECT * FROM %s WHERE account = ? AND categories CONTAINS KEY ?", "test", "lmn"); + + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS ?", "test", "foo"), + row("test", 5, map("lmn", "foo")), + row("test", 6, map("lmn2", "foo"))); + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS KEY ? AND categories CONTAINS ? ALLOW FILTERING", + "test", "lmn", "foo"), + row("test", 5, map("lmn", "foo"))); + assertRows(execute("SELECT * FROM %s WHERE account = ? AND categories CONTAINS ? AND categories CONTAINS KEY ? ALLOW FILTERING", + "test", "foo", "lmn"), + row("test", 5, map("lmn", "foo"))); + } }