Updated Branches:
  refs/heads/trunk d63d07b92 -> c2294aa21

Secondary indexing of map keys

patch by slebresne; reviewed by iamaleksey for CASSANDRA-6383


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

Branch: refs/heads/trunk
Commit: c2294aa21eb6310b6d5c05d6d9ff505f59b376c2
Parents: d63d07b
Author: Sylvain Lebresne <[email protected]>
Authored: Thu Dec 19 14:52:13 2013 +0100
Committer: Sylvain Lebresne <[email protected]>
Committed: Wed Jan 8 14:34:19 2014 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +-
 src/java/org/apache/cassandra/cql3/Cql.g        | 12 +++++-
 .../cql3/statements/CreateIndexStatement.java   | 36 ++++++++++++-----
 .../cassandra/cql3/statements/IndexTarget.java  | 42 ++++++++++++++++++++
 .../db/index/composites/CompositesIndex.java    | 11 ++++-
 5 files changed, 88 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 24722b8..58a0906 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -15,7 +15,7 @@
  * User-defined types for CQL3 (CASSANDRA-5590)
  * Use of o.a.c.metrics in nodetool (CASSANDRA-5871, 6406)
  * Batch read from OTC's queue and cleanup (CASSANDRA-1632)
- * Secondary index support for collections (CASSANDRA-4511)
+ * Secondary index support for collections (CASSANDRA-4511, 6383)
  * SSTable metadata(Stats.db) format change (CASSANDRA-6356)
  * Push composites support in the storage engine
    (CASSANDRA-5417, CASSANDRA-6520)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g 
b/src/java/org/apache/cassandra/cql3/Cql.g
index f9f66df..11291b6 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -552,12 +552,18 @@ createIndexStatement returns [CreateIndexStatement expr]
         boolean ifNotExists = false;
     }
     : K_CREATE (K_CUSTOM { props.isCustom = true; })? K_INDEX (K_IF K_NOT 
K_EXISTS { ifNotExists = true; } )?
-        (idxName=IDENT)? K_ON cf=columnFamilyName '(' id=cident ')'
+        (idxName=IDENT)? K_ON cf=columnFamilyName '(' id=indexIdent ')'
         (K_USING cls=STRING_LITERAL { props.customClass = $cls.text; })?
         (K_WITH properties[props])?
       { $expr = new CreateIndexStatement(cf, $idxName.text, id, props, 
ifNotExists); }
     ;
 
+indexIdent returns [IndexTarget id]
+    : c=cident                { $id = IndexTarget.of(c); }
+    | K_KEYS '(' c=cident ')' { $id = IndexTarget.keysOf(c); }
+    ;
+
+
 /**
  * CREATE TRIGGER triggerName ON columnFamily USING 'triggerClass';
  */
@@ -958,7 +964,7 @@ relation[List<Relation> clauses]
         { $clauses.add(new Relation(name, Relation.Type.IN, marker)); }
     | name=cident K_IN { Relation rel = Relation.createInRelation($name.id); }
        '(' ( f1=term { rel.addInValue(f1); } (',' fN=term { 
rel.addInValue(fN); } )* )? ')' { $clauses.add(rel); }
-    | name=cident K_CONTAINS { Relation.Type rt = Relation.Type.CONTAINS; } /* 
(K_KEY { rt = Relation.Type.CONTAINS_KEY })? */
+    | name=cident K_CONTAINS { Relation.Type rt = Relation.Type.CONTAINS; } 
(K_KEY { rt = Relation.Type.CONTAINS_KEY; })?
         t=term { $clauses.add(new Relation(name, rt, t)); }
     | '(' relation[$clauses] ')'
     ;
@@ -1036,6 +1042,7 @@ unreserved_function_keyword returns [String str]
 
 basic_unreserved_keyword returns [String str]
     : k=( K_KEY
+        | K_KEYS
         | K_AS
         | K_CLUSTERING
         | K_COMPACT
@@ -1070,6 +1077,7 @@ K_AS:          A S;
 K_WHERE:       W H E R E;
 K_AND:         A N D;
 K_KEY:         K E Y;
+K_KEYS:        K E Y S;
 K_INSERT:      I N S E R T;
 K_UPDATE:      U P D A T E;
 K_WITH:        W I T H;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/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 56b465e..ca43d20 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@ -29,6 +29,8 @@ 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.*;
 import org.apache.cassandra.service.ClientState;
@@ -42,19 +44,19 @@ public class CreateIndexStatement extends 
SchemaAlteringStatement
     private static final Logger logger = 
LoggerFactory.getLogger(CreateIndexStatement.class);
 
     private final String indexName;
-    private final ColumnIdentifier columnName;
+    private final IndexTarget target;
     private final IndexPropDefs properties;
     private final boolean ifNotExists;
 
     public CreateIndexStatement(CFName name,
                                 String indexName,
-                                ColumnIdentifier columnName,
+                                IndexTarget target,
                                 IndexPropDefs properties,
                                 boolean ifNotExists)
     {
         super(name);
         this.indexName = indexName;
-        this.columnName = columnName;
+        this.target = target;
         this.properties = properties;
         this.ifNotExists = ifNotExists;
     }
@@ -70,13 +72,27 @@ public class CreateIndexStatement extends 
SchemaAlteringStatement
         if (cfm.getDefaultValidator().isCommutative())
             throw new InvalidRequestException("Secondary indexes are not 
supported on counter tables");
 
-        ColumnDefinition cd = cfm.getColumnDefinition(columnName);
+        ColumnDefinition cd = cfm.getColumnDefinition(target.column);
 
         if (cd == null)
-            throw new InvalidRequestException("No column definition found for 
column " + columnName);
+            throw new InvalidRequestException("No column definition found for 
column " + target.column);
+
+        boolean isMap = cd.type instanceof MapType;
+        if (target.isCollectionKeys && !isMap)
+            throw new InvalidRequestException("Cannot create index on keys of 
column " + target + " with non map type");
 
         if (cd.getIndexType() != null)
         {
+            boolean previousIsKeys = 
cd.getIndexOptions().containsKey("index_keys");
+            if (isMap && target.isCollectionKeys != previousIsKeys)
+            {
+                String msg = "Cannot create index on %s %s, an index on %s %s 
already exists and indexing "
+                           + "a map on both keys and values at the same time 
is not currently supported";
+                throw new InvalidRequestException(String.format(msg,
+                                                                target.column, 
target.isCollectionKeys ? "keys" : "values",
+                                                                target.column, 
previousIsKeys ? "keys" : "values"));
+            }
+
             if (ifNotExists)
                 return;
             else
@@ -87,17 +103,17 @@ public class CreateIndexStatement extends 
SchemaAlteringStatement
 
         // TODO: we could lift that limitation
         if (cfm.comparator.isDense() && cd.kind != 
ColumnDefinition.Kind.REGULAR)
-            throw new InvalidRequestException(String.format("Secondary index 
on %s column %s is not yet supported for compact table", cd.kind, columnName));
+            throw new InvalidRequestException(String.format("Secondary index 
on %s column %s is not yet supported for compact table", cd.kind, 
target.column));
 
         if (cd.kind == ColumnDefinition.Kind.PARTITION_KEY && 
cd.isOnAllComponents())
-            throw new InvalidRequestException(String.format("Cannot add 
secondary index to already primarily indexed column %s", columnName));
+            throw new InvalidRequestException(String.format("Cannot add 
secondary index to already primarily indexed column %s", target.column));
     }
 
     public void announceMigration() throws RequestValidationException
     {
-        logger.debug("Updating column {} definition for index {}", columnName, 
indexName);
+        logger.debug("Updating column {} definition for index {}", 
target.column, indexName);
         CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), 
columnFamily()).clone();
-        ColumnDefinition cd = cfm.getColumnDefinition(columnName);
+        ColumnDefinition cd = cfm.getColumnDefinition(target.column);
 
         if (cd.getIndexType() != null && ifNotExists)
             return;
@@ -113,7 +129,7 @@ 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("index_values", "");
+                options = ImmutableMap.of(target.isCollectionKeys ? 
"index_keys" : "index_values", "");
             cd.setIndexType(IndexType.COMPOSITES, options);
         }
         else

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java 
b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
new file mode 100644
index 0000000..58a8c92
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/IndexTarget.java
@@ -0,0 +1,42 @@
+/*
+ * 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.cassandra.cql3.statements;
+
+import org.apache.cassandra.cql3.ColumnIdentifier;
+
+public class IndexTarget
+{
+    public final ColumnIdentifier column;
+    public final boolean isCollectionKeys;
+
+    private IndexTarget(ColumnIdentifier column, boolean isCollectionKeys)
+    {
+        this.column = column;
+        this.isCollectionKeys = isCollectionKeys;
+    }
+
+    public static IndexTarget of(ColumnIdentifier c)
+    {
+        return new IndexTarget(c, false);
+    }
+
+    public static IndexTarget keysOf(ColumnIdentifier c)
+    {
+        return new IndexTarget(c, true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c2294aa2/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 839df26..c92f653 100644
--- a/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
+++ b/src/java/org/apache/cassandra/db/index/composites/CompositesIndex.java
@@ -65,7 +65,9 @@ public abstract class CompositesIndex extends 
AbstractSimplePerColumnSecondaryIn
                 case SET:
                     return new CompositesIndexOnCollectionKey();
                 case MAP:
-                    return new CompositesIndexOnCollectionValue();
+                    return cfDef.getIndexOptions().containsKey("index_keys")
+                         ? new CompositesIndexOnCollectionKey()
+                         : new CompositesIndexOnCollectionValue();
             }
         }
 
@@ -95,7 +97,9 @@ public abstract class CompositesIndex extends 
AbstractSimplePerColumnSecondaryIn
                 case SET:
                     return 
CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef);
                 case MAP:
-                    return 
CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef);
+                    return cfDef.getIndexOptions().containsKey("index_keys")
+                         ? 
CompositesIndexOnCollectionKey.buildIndexComparator(baseMetadata, cfDef)
+                         : 
CompositesIndexOnCollectionValue.buildIndexComparator(baseMetadata, cfDef);
             }
         }
 
@@ -154,7 +158,10 @@ public abstract class CompositesIndex extends 
AbstractSimplePerColumnSecondaryIn
         options.remove("prefix_size");
 
         if (columnDef.type.isCollection())
+        {
             options.remove("index_values");
+            options.remove("index_keys");
+        }
 
         if (!options.isEmpty())
             throw new ConfigurationException("Unknown options provided for 
COMPOSITES index: " + options.keySet());

Reply via email to