Repository: cassandra
Updated Branches:
  refs/heads/trunk a7b138ad6 -> b7d1d447b


Add static column support to SASI index

patch by doanduyhai and xedin; reviewed by xedin for CASSANDRA-11183


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

Branch: refs/heads/trunk
Commit: b7d1d447bb52ed912fdd1199086cce34a2d82c45
Parents: a7b138a
Author: Pavel Yaskevich <xe...@apache.org>
Authored: Mon Apr 4 01:05:04 2016 -0700
Committer: Pavel Yaskevich <xe...@apache.org>
Committed: Mon Apr 4 11:43:37 2016 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/db/ColumnIndex.java    |   8 +-
 .../cassandra/index/sasi/SASIIndexBuilder.java  |   3 +
 .../cassandra/index/sasi/conf/ColumnIndex.java  |   8 ++
 .../cassandra/index/sasi/plan/Operation.java    |  26 ++--
 .../cassandra/index/sasi/plan/QueryPlan.java    |   4 +-
 .../unit/org/apache/cassandra/SchemaLoader.java |  38 ++++++
 .../validation/entities/SecondaryIndexTest.java |  10 +-
 .../cassandra/index/sasi/SASIIndexTest.java     | 102 ++++++++++++++-
 .../index/sasi/plan/OperationTest.java          | 125 ++++++++++++++-----
 10 files changed, 272 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 13e9325..d576b8e 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.6
+ * Add static column support to SASI index (CASSANDRA-11183)
  * Support EQ/PREFIX queries in SASI CONTAINS mode without tokenization 
(CASSANDRA-11434)
  * Support LIKE operator in prepared statements (CASSANDRA-11456)
  * Add a command to see if a Materialized View has finished building 
(CASSANDRA-9967)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/src/java/org/apache/cassandra/db/ColumnIndex.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ColumnIndex.java 
b/src/java/org/apache/cassandra/db/ColumnIndex.java
index 930fc05..1942052 100644
--- a/src/java/org/apache/cassandra/db/ColumnIndex.java
+++ b/src/java/org/apache/cassandra/db/ColumnIndex.java
@@ -109,7 +109,13 @@ public class ColumnIndex
             
ByteBufferUtil.writeWithShortLength(iterator.partitionKey().getKey(), writer);
             
DeletionTime.serializer.serialize(iterator.partitionLevelDeletion(), writer);
             if (header.hasStatic())
-                
UnfilteredSerializer.serializer.serializeStaticRow(iterator.staticRow(), 
header, writer, version);
+            {
+                Row staticRow = iterator.staticRow();
+
+                UnfilteredSerializer.serializer.serializeStaticRow(staticRow, 
header, writer, version);
+                if (!observers.isEmpty())
+                    observers.forEach((o) -> 
o.nextUnfilteredCluster(staticRow));
+            }
         }
 
         public ColumnIndex build() throws IOException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/src/java/org/apache/cassandra/index/sasi/SASIIndexBuilder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/index/sasi/SASIIndexBuilder.java 
b/src/java/org/apache/cassandra/index/sasi/SASIIndexBuilder.java
index fc5b675..205bf7e 100644
--- a/src/java/org/apache/cassandra/index/sasi/SASIIndexBuilder.java
+++ b/src/java/org/apache/cassandra/index/sasi/SASIIndexBuilder.java
@@ -78,6 +78,9 @@ class SASIIndexBuilder extends SecondaryIndexBuilder
 
                             try (SSTableIdentityIterator partition = new 
SSTableIdentityIterator(sstable, dataFile, key))
                             {
+                                // if the row has statics attached, it has to 
be indexed separately
+                                
indexWriter.nextUnfilteredCluster(partition.staticRow());
+
                                 while (partition.hasNext())
                                     
indexWriter.nextUnfilteredCluster(partition.next());
                             }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/src/java/org/apache/cassandra/index/sasi/conf/ColumnIndex.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/index/sasi/conf/ColumnIndex.java 
b/src/java/org/apache/cassandra/index/sasi/conf/ColumnIndex.java
index 8ee94d1..3f268e3 100644
--- a/src/java/org/apache/cassandra/index/sasi/conf/ColumnIndex.java
+++ b/src/java/org/apache/cassandra/index/sasi/conf/ColumnIndex.java
@@ -226,11 +226,19 @@ public class ColumnIndex
 
     public static ByteBuffer getValueOf(ColumnDefinition column, Row row, int 
nowInSecs)
     {
+        if (row == null)
+            return null;
+
         switch (column.kind)
         {
             case CLUSTERING:
                 return row.clustering().get(column.position());
 
+            // treat static cell retrieval the same was as regular
+            // only if row kind is STATIC otherwise return null
+            case STATIC:
+                if (!row.isStatic())
+                    return null;
             case REGULAR:
                 Cell cell = row.getCell(column);
                 return cell == null || !cell.isLive(nowInSecs) ? null : 
cell.value();

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/src/java/org/apache/cassandra/index/sasi/plan/Operation.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/index/sasi/plan/Operation.java 
b/src/java/org/apache/cassandra/index/sasi/plan/Operation.java
index 6f0904c..7c744e1 100644
--- a/src/java/org/apache/cassandra/index/sasi/plan/Operation.java
+++ b/src/java/org/apache/cassandra/index/sasi/plan/Operation.java
@@ -92,7 +92,7 @@ public class Operation extends RangeIterator<Long, Token>
      * and data from the lower level members using depth-first search
      * and bubbling the results back to the top level caller.
      *
-     * Most of the work here is done by {@link #localSatisfiedBy(Unfiltered, 
boolean)}
+     * Most of the work here is done by {@link #localSatisfiedBy(Unfiltered, 
Row, boolean)}
      * see it's comment for details, if there are no local expressions
      * assigned to Operation it will call satisfiedBy(Row) on it's children.
      *
@@ -120,18 +120,20 @@ public class Operation extends RangeIterator<Long, Token>
      * Level #2 computes AND between "first_name" and result of level #3, AND 
(state, country) which is already computed
      * Level #1 does OR between results of AND (first_name) and AND (state, 
country) and returns final result.
      *
-     * @param row The row to check.
+     * @param currentCluster The row cluster to check.
+     * @param staticRow The static row associated with current cluster.
+     * @param allowMissingColumns allow columns value to be null.
      * @return true if give Row satisfied all of the expressions in the tree,
      *         false otherwise.
      */
-    public boolean satisfiedBy(Unfiltered row, boolean allowMissingColumns)
+    public boolean satisfiedBy(Unfiltered currentCluster, Row staticRow, 
boolean allowMissingColumns)
     {
         boolean sideL, sideR;
 
         if (expressions == null || expressions.isEmpty())
         {
-            sideL =  left != null &&  left.satisfiedBy(row, 
allowMissingColumns);
-            sideR = right != null && right.satisfiedBy(row, 
allowMissingColumns);
+            sideL =  left != null &&  left.satisfiedBy(currentCluster, 
staticRow, allowMissingColumns);
+            sideR = right != null && right.satisfiedBy(currentCluster, 
staticRow, allowMissingColumns);
 
             // one of the expressions was skipped
             // because it had no indexes attached
@@ -140,14 +142,14 @@ public class Operation extends RangeIterator<Long, Token>
         }
         else
         {
-            sideL = localSatisfiedBy(row, allowMissingColumns);
+            sideL = localSatisfiedBy(currentCluster, staticRow, 
allowMissingColumns);
 
             // if there is no right it means that this expression
             // is last in the sequence, we can just return result from local 
expressions
             if (right == null)
                 return sideL;
 
-            sideR = right.satisfiedBy(row, allowMissingColumns);
+            sideR = right.satisfiedBy(currentCluster, staticRow, 
allowMissingColumns);
         }
 
 
@@ -190,13 +192,15 @@ public class Operation extends RangeIterator<Long, Token>
      *
      * #4 return accumulator => true (row satisfied all of the conditions)
      *
-     * @param row The row to check.
+     * @param currentCluster The row cluster to check.
+     * @param staticRow The static row associated with current cluster.
+     * @param allowMissingColumns allow columns value to be null.
      * @return true if give Row satisfied all of the analyzed expressions,
      *         false otherwise.
      */
-    private boolean localSatisfiedBy(Unfiltered row, boolean 
allowMissingColumns)
+    private boolean localSatisfiedBy(Unfiltered currentCluster, Row staticRow, 
boolean allowMissingColumns)
     {
-        if (row == null || !row.isRow())
+        if (currentCluster == null || !currentCluster.isRow())
             return false;
 
         final int now = FBUtilities.nowInSeconds();
@@ -208,7 +212,7 @@ public class Operation extends RangeIterator<Long, Token>
             if (column.kind == Kind.PARTITION_KEY)
                 continue;
 
-            ByteBuffer value = ColumnIndex.getValueOf(column, (Row) row, now);
+            ByteBuffer value = ColumnIndex.getValueOf(column, column.kind == 
Kind.STATIC ? staticRow : (Row) currentCluster, now);
             boolean isMissingColumn = value == null;
 
             if (!allowMissingColumns && isMissingColumn)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/src/java/org/apache/cassandra/index/sasi/plan/QueryPlan.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/index/sasi/plan/QueryPlan.java 
b/src/java/org/apache/cassandra/index/sasi/plan/QueryPlan.java
index d34b05a..4410756 100644
--- a/src/java/org/apache/cassandra/index/sasi/plan/QueryPlan.java
+++ b/src/java/org/apache/cassandra/index/sasi/plan/QueryPlan.java
@@ -112,11 +112,13 @@ public class QueryPlan
 
                     try (UnfilteredRowIterator partition = 
controller.getPartition(key, executionController))
                     {
+                        Row staticRow = partition.staticRow();
                         List<Unfiltered> clusters = new ArrayList<>();
+
                         while (partition.hasNext())
                         {
                             Unfiltered row = partition.next();
-                            if (operationTree.satisfiedBy(row, true))
+                            if (operationTree.satisfiedBy(row, staticRow, 
true))
                                 clusters.add(row);
                         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/test/unit/org/apache/cassandra/SchemaLoader.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java 
b/test/unit/org/apache/cassandra/SchemaLoader.java
index e68dd94..992c4d6 100644
--- a/test/unit/org/apache/cassandra/SchemaLoader.java
+++ b/test/unit/org/apache/cassandra/SchemaLoader.java
@@ -605,6 +605,44 @@ public class SchemaLoader
         return cfm;
     }
 
+    public static CFMetaData staticSASICFMD(String ksName, String cfName)
+    {
+        CFMetaData cfm = CFMetaData.Builder.create(ksName, cfName)
+                                           .addPartitionKey("sensor_id", 
Int32Type.instance)
+                                           .addStaticColumn("sensor_type", 
UTF8Type.instance)
+                                           .addClusteringColumn("date", 
LongType.instance)
+                                           .addRegularColumn("value", 
DoubleType.instance)
+                                           .addRegularColumn("variance", 
Int32Type.instance)
+                                           .build();
+
+        Indexes indexes = cfm.getIndexes();
+        indexes = indexes.with(IndexMetadata.fromSchemaMetadata("sensor_type", 
IndexMetadata.Kind.CUSTOM, new HashMap<String, String>()
+        {{
+            put(IndexTarget.CUSTOM_INDEX_OPTION_NAME, 
SASIIndex.class.getName());
+            put(IndexTarget.TARGET_OPTION_NAME, "sensor_type");
+            put("mode", OnDiskIndexBuilder.Mode.PREFIX.toString());
+            put("analyzer_class", 
"org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer");
+            put("case_sensitive", "false");
+        }}));
+
+        indexes = indexes.with(IndexMetadata.fromSchemaMetadata("value", 
IndexMetadata.Kind.CUSTOM, new HashMap<String, String>()
+        {{
+            put(IndexTarget.CUSTOM_INDEX_OPTION_NAME, 
SASIIndex.class.getName());
+            put(IndexTarget.TARGET_OPTION_NAME, "value");
+            put("mode", OnDiskIndexBuilder.Mode.PREFIX.toString());
+        }}));
+
+        indexes = indexes.with(IndexMetadata.fromSchemaMetadata("variance", 
IndexMetadata.Kind.CUSTOM, new HashMap<String, String>()
+        {{
+            put(IndexTarget.CUSTOM_INDEX_OPTION_NAME, 
SASIIndex.class.getName());
+            put(IndexTarget.TARGET_OPTION_NAME, "variance");
+            put("mode", OnDiskIndexBuilder.Mode.PREFIX.toString());
+        }}));
+
+        cfm.indexes(indexes);
+        return cfm;
+    }
+
     public static CompressionParams getCompressionParameters()
     {
         return getCompressionParameters(null);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
----------------------------------------------------------------------
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
 
b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
index e357d74..feea656 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/entities/SecondaryIndexTest.java
@@ -719,16 +719,12 @@ public class SecondaryIndexTest extends CQLTester
         execute("SELECT * FROM %s WHERE v1 LIKE ?", "abc%");
         execute("SELECT * FROM %s WHERE v1 LIKE ?", "abc");
 
-        // contains mode indexes support suffix/contains/matches
-        assertInvalidMessage("c2 LIKE '<term>%' abc is only supported on 
properly indexed columns",
-                             "SELECT * FROM %s WHERE c2 LIKE ?",
-                             "abc%");
+        // contains mode indexes support prefix/suffix/contains/matches
+        execute("SELECT * FROM %s WHERE c2 LIKE ?", "abc%");
         execute("SELECT * FROM %s WHERE c2 LIKE ?", "%abc");
         execute("SELECT * FROM %s WHERE c2 LIKE ?", "%abc%");
         execute("SELECT * FROM %s WHERE c2 LIKE ?", "abc");
-        assertInvalidMessage("v2 LIKE '<term>%' abc is only supported on 
properly indexed columns",
-                             "SELECT * FROM %s WHERE v2 LIKE ?",
-                             "abc%");
+        execute("SELECT * FROM %s WHERE v2 LIKE ?", "abc%");
         execute("SELECT * FROM %s WHERE v2 LIKE ?", "%abc");
         execute("SELECT * FROM %s WHERE v2 LIKE ?", "%abc%");
         execute("SELECT * FROM %s WHERE v2 LIKE ?", "abc");

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/test/unit/org/apache/cassandra/index/sasi/SASIIndexTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/index/sasi/SASIIndexTest.java 
b/test/unit/org/apache/cassandra/index/sasi/SASIIndexTest.java
index c44c48c..115c996 100644
--- a/test/unit/org/apache/cassandra/index/sasi/SASIIndexTest.java
+++ b/test/unit/org/apache/cassandra/index/sasi/SASIIndexTest.java
@@ -82,6 +82,7 @@ public class SASIIndexTest
     private static final String CF_NAME = "test_cf";
     private static final String CLUSTERING_CF_NAME_1 = "clustering_test_cf_1";
     private static final String CLUSTERING_CF_NAME_2 = "clustering_test_cf_2";
+    private static final String STATIC_CF_NAME = "static_sasi_test_cf";
 
     @BeforeClass
     public static void loadSchema() throws ConfigurationException
@@ -92,7 +93,8 @@ public class SASIIndexTest
                                                                      
KeyspaceParams.simpleTransient(1),
                                                                      
Tables.of(SchemaLoader.sasiCFMD(KS_NAME, CF_NAME),
                                                                                
SchemaLoader.clusteringSASICFMD(KS_NAME, CLUSTERING_CF_NAME_1),
-                                                                               
SchemaLoader.clusteringSASICFMD(KS_NAME, CLUSTERING_CF_NAME_2, "location"))));
+                                                                               
SchemaLoader.clusteringSASICFMD(KS_NAME, CLUSTERING_CF_NAME_2, "location"),
+                                                                               
SchemaLoader.staticSASICFMD(KS_NAME, STATIC_CF_NAME))));
     }
 
     @After
@@ -1726,6 +1728,104 @@ public class SASIIndexTest
     }
 
     @Test
+    public void testStaticIndex() throws Exception
+    {
+        testStaticIndex(false);
+        cleanupData();
+        testStaticIndex(true);
+    }
+
+    public void testStaticIndex(boolean shouldFlush) throws Exception
+    {
+        ColumnFamilyStore store = 
Keyspace.open(KS_NAME).getColumnFamilyStore(STATIC_CF_NAME);
+
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s (sensor_id,sensor_type) 
VALUES(?, ?)", 1, "TEMPERATURE");
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 1, 20160401L, 24.46, 2);
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 1, 20160402L, 25.62, 5);
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 1, 20160403L, 24.96, 4);
+
+        if (shouldFlush)
+            store.forceBlockingFlush();
+
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s (sensor_id,sensor_type) 
VALUES(?, ?)", 2, "PRESSURE");
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 2, 20160401L, 1.03, 9);
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 2, 20160402L, 1.04, 7);
+        executeCQL(STATIC_CF_NAME, "INSERT INTO %s.%s 
(sensor_id,date,value,variance) VALUES(?, ?, ?, ?)", 2, 20160403L, 1.01, 4);
+
+        if (shouldFlush)
+            store.forceBlockingFlush();
+
+        UntypedResultSet results;
+
+        // Prefix search on static column only
+        results = executeCQL(STATIC_CF_NAME ,"SELECT * FROM %s.%s WHERE 
sensor_type LIKE 'temp%%'");
+        Assert.assertNotNull(results);
+        Assert.assertEquals(3, results.size());
+
+        Iterator<UntypedResultSet.Row> iterator = results.iterator();
+
+        UntypedResultSet.Row row1 = iterator.next();
+        Assert.assertEquals(20160401L, row1.getLong("date"));
+        Assert.assertEquals(24.46, row1.getDouble("value"));
+        Assert.assertEquals(2, row1.getInt("variance"));
+
+
+        UntypedResultSet.Row row2 = iterator.next();
+        Assert.assertEquals(20160402L, row2.getLong("date"));
+        Assert.assertEquals(25.62, row2.getDouble("value"));
+        Assert.assertEquals(5, row2.getInt("variance"));
+
+        UntypedResultSet.Row row3 = iterator.next();
+        Assert.assertEquals(20160403L, row3.getLong("date"));
+        Assert.assertEquals(24.96, row3.getDouble("value"));
+        Assert.assertEquals(4, row3.getInt("variance"));
+
+
+        // Combined static and non static filtering
+        results = executeCQL(STATIC_CF_NAME ,"SELECT * FROM %s.%s WHERE 
sensor_type=? AND value >= ? AND value <= ? AND variance=? ALLOW FILTERING",
+                             "pressure", 1.02, 1.05, 7);
+        Assert.assertNotNull(results);
+        Assert.assertEquals(1, results.size());
+
+        row1 = results.one();
+        Assert.assertEquals(20160402L, row1.getLong("date"));
+        Assert.assertEquals(1.04, row1.getDouble("value"));
+        Assert.assertEquals(7, row1.getInt("variance"));
+
+        // Only non statc columns filtering
+        results = executeCQL(STATIC_CF_NAME ,"SELECT * FROM %s.%s WHERE value 
>= ? AND variance <= ? ALLOW FILTERING", 1.02, 7);
+        Assert.assertNotNull(results);
+        Assert.assertEquals(4, results.size());
+
+        iterator = results.iterator();
+
+        row1 = iterator.next();
+        Assert.assertEquals("TEMPERATURE", row1.getString("sensor_type"));
+        Assert.assertEquals(20160401L, row1.getLong("date"));
+        Assert.assertEquals(24.46, row1.getDouble("value"));
+        Assert.assertEquals(2, row1.getInt("variance"));
+
+
+        row2 = iterator.next();
+        Assert.assertEquals("TEMPERATURE", row2.getString("sensor_type"));
+        Assert.assertEquals(20160402L, row2.getLong("date"));
+        Assert.assertEquals(25.62, row2.getDouble("value"));
+        Assert.assertEquals(5, row2.getInt("variance"));
+
+        row3 = iterator.next();
+        Assert.assertEquals("TEMPERATURE", row3.getString("sensor_type"));
+        Assert.assertEquals(20160403L, row3.getLong("date"));
+        Assert.assertEquals(24.96, row3.getDouble("value"));
+        Assert.assertEquals(4, row3.getInt("variance"));
+
+        UntypedResultSet.Row row4 = iterator.next();
+        Assert.assertEquals("PRESSURE", row4.getString("sensor_type"));
+        Assert.assertEquals(20160402L, row4.getLong("date"));
+        Assert.assertEquals(1.04, row4.getDouble("value"));
+        Assert.assertEquals(7, row4.getInt("variance"));
+    }
+
+    @Test
     public void testInvalidIndexOptions()
     {
         ColumnFamilyStore store = 
Keyspace.open(KS_NAME).getColumnFamilyStore(CF_NAME);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b7d1d447/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java 
b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
index 52cee3b..e388cd4 100644
--- a/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
+++ b/test/unit/org/apache/cassandra/index/sasi/plan/OperationTest.java
@@ -50,9 +50,11 @@ public class OperationTest extends SchemaLoader
     private static final String KS_NAME = "sasi";
     private static final String CF_NAME = "test_cf";
     private static final String CLUSTERING_CF_NAME = "clustering_test_cf";
+    private static final String STATIC_CF_NAME = "static_sasi_test_cf";
 
     private static ColumnFamilyStore BACKEND;
     private static ColumnFamilyStore CLUSTERING_BACKEND;
+    private static ColumnFamilyStore STATIC_BACKEND;
 
     @BeforeClass
     public static void loadSchema() throws ConfigurationException
@@ -62,10 +64,12 @@ public class OperationTest extends SchemaLoader
         MigrationManager.announceNewKeyspace(KeyspaceMetadata.create(KS_NAME,
                                                                      
KeyspaceParams.simpleTransient(1),
                                                                      
Tables.of(SchemaLoader.sasiCFMD(KS_NAME, CF_NAME),
-                                                                               
SchemaLoader.clusteringSASICFMD(KS_NAME, CLUSTERING_CF_NAME))));
+                                                                               
SchemaLoader.clusteringSASICFMD(KS_NAME, CLUSTERING_CF_NAME),
+                                                                               
SchemaLoader.staticSASICFMD(KS_NAME, STATIC_CF_NAME))));
 
         BACKEND = Keyspace.open(KS_NAME).getColumnFamilyStore(CF_NAME);
         CLUSTERING_BACKEND = 
Keyspace.open(KS_NAME).getColumnFamilyStore(CLUSTERING_CF_NAME);
+        STATIC_BACKEND = 
Keyspace.open(KS_NAME).getColumnFamilyStore(STATIC_CF_NAME);
     }
 
     private QueryController controller;
@@ -277,17 +281,18 @@ public class OperationTest extends SchemaLoader
         Operation op = builder.complete();
 
         Unfiltered row = buildRow(buildCell(age, 
Int32Type.instance.decompose(6), System.currentTimeMillis()));
+        Row staticRow = buildRow(Clustering.STATIC_CLUSTERING);
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(5), 
System.currentTimeMillis()));
 
         // and reject incorrect value
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(6), 
System.currentTimeMillis()));
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         // range with exclusions - age != 5 AND age > 1 AND age != 6 AND age 
<= 10
         builder = new Operation.Builder(OperationType.AND, controller,
@@ -302,7 +307,7 @@ public class OperationTest extends SchemaLoader
         {
             row = buildRow(buildCell(age, Int32Type.instance.decompose(i), 
System.currentTimeMillis()));
 
-            boolean result = op.satisfiedBy(row, false);
+            boolean result = op.satisfiedBy(row, staticRow, false);
             Assert.assertTrue(exclusions.contains(i) != result);
         }
 
@@ -318,7 +323,7 @@ public class OperationTest extends SchemaLoader
         {
             row = buildRow(buildCell(age, Int32Type.instance.decompose(i), 
System.currentTimeMillis()));
 
-            boolean result = op.satisfiedBy(row, false);
+            boolean result = op.satisfiedBy(row, staticRow, false);
             Assert.assertTrue(exclusions.contains(i) != result);
         }
 
@@ -341,7 +346,7 @@ public class OperationTest extends SchemaLoader
         {
             row = buildRow(buildCell(age, Int32Type.instance.decompose(i), 
System.currentTimeMillis()));
 
-            boolean result = op.satisfiedBy(row, false);
+            boolean result = op.satisfiedBy(row, staticRow, false);
             Assert.assertTrue(exclusions.contains(i) != result);
         }
 
@@ -355,17 +360,17 @@ public class OperationTest extends SchemaLoader
         row = buildRow(buildCell(age, Int32Type.instance.decompose(6), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(11L), System.currentTimeMillis()));
 
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(5), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(22L), System.currentTimeMillis()));
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(5), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(9L), System.currentTimeMillis()));
 
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         // operation with internal expressions and right child
         builder = new Operation.Builder(OperationType.OR, controller,
@@ -378,25 +383,26 @@ public class OperationTest extends SchemaLoader
         row = buildRow(buildCell(age, Int32Type.instance.decompose(5), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(9L), System.currentTimeMillis()));
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(20), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(11L), System.currentTimeMillis()));
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(buildCell(age, Int32Type.instance.decompose(0), 
System.currentTimeMillis()),
                                   buildCell(timestamp, 
LongType.instance.decompose(9L), System.currentTimeMillis()));
 
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         // and for desert let's try out null and deleted rows etc.
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(age, Operator.EQ, 
Int32Type.instance.decompose(30)));
         op = builder.complete();
 
-        Assert.assertFalse(op.satisfiedBy(null, false));
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(null, staticRow, false));
+        Assert.assertFalse(op.satisfiedBy(row, null, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         long now = System.currentTimeMillis();
 
@@ -404,15 +410,15 @@ public class OperationTest extends SchemaLoader
                 Row.Deletion.regular(new DeletionTime(now - 10, (int) (now / 
1000))),
                           buildCell(age, Int32Type.instance.decompose(6), 
System.currentTimeMillis()));
 
-        Assert.assertFalse(op.satisfiedBy(row, false));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
 
         row = buildRow(deletedCell(age, System.currentTimeMillis(), 
FBUtilities.nowInSeconds()));
 
-        Assert.assertFalse(op.satisfiedBy(row, true));
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, true));
 
         try
         {
-            Assert.assertFalse(op.satisfiedBy(buildRow(), false));
+            Assert.assertFalse(op.satisfiedBy(buildRow(), staticRow, false));
         }
         catch (IllegalStateException e)
         {
@@ -421,7 +427,7 @@ public class OperationTest extends SchemaLoader
 
         try
         {
-            Assert.assertFalse(op.satisfiedBy(buildRow(), true));
+            Assert.assertFalse(op.satisfiedBy(buildRow(), staticRow, true));
         }
         catch (IllegalStateException e)
         {
@@ -486,22 +492,20 @@ public class OperationTest extends SchemaLoader
     {
         final ColumnDefinition comment = 
getColumn(UTF8Type.instance.decompose("comment"));
 
-        Unfiltered row = buildRow(
-                buildCell(comment,
-                                                          
UTF8Type.instance.decompose("software engineer is working on a project"),
-                                                          
System.currentTimeMillis()));
+        Unfiltered row = 
buildRow(buildCell(comment,UTF8Type.instance.decompose("software engineer is 
working on a project"),System.currentTimeMillis()));
+        Row staticRow = buildRow(Clustering.STATIC_CLUSTERING);
 
         Operation.Builder builder = new Operation.Builder(OperationType.AND, 
controller,
                                             new SimpleExpression(comment, 
Operator.LIKE_CONTAINS, UTF8Type.instance.decompose("eng is a work")));
         Operation op = builder.complete();
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
 
         builder = new Operation.Builder(OperationType.AND, controller,
                                             new SimpleExpression(comment, 
Operator.LIKE_CONTAINS, UTF8Type.instance.decompose("soft works fine")));
         op = builder.complete();
 
-        Assert.assertTrue(op.satisfiedBy(row, false));
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
     }
 
     @Test
@@ -515,51 +519,52 @@ public class OperationTest extends SchemaLoader
         Unfiltered row = 
buildRow(Clustering.make(UTF8Type.instance.fromString("US"), 
Int32Type.instance.decompose(27)),
                                   buildCell(height, 
Int32Type.instance.decompose(182), System.currentTimeMillis()),
                                   buildCell(score, 
DoubleType.instance.decompose(1.0d), System.currentTimeMillis()));
+        Row staticRow = buildRow(Clustering.STATIC_CLUSTERING);
 
         Operation.Builder builder = new Operation.Builder(OperationType.AND, 
controller);
         builder.add(new SimpleExpression(age, Operator.EQ, 
Int32Type.instance.decompose(27)));
         builder.add(new SimpleExpression(height, Operator.EQ, 
Int32Type.instance.decompose(182)));
 
-        Assert.assertTrue(builder.complete().satisfiedBy(row, false));
+        Assert.assertTrue(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
 
         builder.add(new SimpleExpression(age, Operator.EQ, 
Int32Type.instance.decompose(28)));
         builder.add(new SimpleExpression(height, Operator.EQ, 
Int32Type.instance.decompose(182)));
 
-        Assert.assertFalse(builder.complete().satisfiedBy(row, false));
+        Assert.assertFalse(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(location, Operator.EQ, 
UTF8Type.instance.decompose("US")));
         builder.add(new SimpleExpression(age, Operator.GTE, 
Int32Type.instance.decompose(27)));
 
-        Assert.assertTrue(builder.complete().satisfiedBy(row, false));
+        Assert.assertTrue(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(location, Operator.EQ, 
UTF8Type.instance.decompose("BY")));
         builder.add(new SimpleExpression(age, Operator.GTE, 
Int32Type.instance.decompose(28)));
 
-        Assert.assertFalse(builder.complete().satisfiedBy(row, false));
+        Assert.assertFalse(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(location, Operator.EQ, 
UTF8Type.instance.decompose("US")));
         builder.add(new SimpleExpression(age, Operator.LTE, 
Int32Type.instance.decompose(27)));
         builder.add(new SimpleExpression(height, Operator.GTE, 
Int32Type.instance.decompose(182)));
 
-        Assert.assertTrue(builder.complete().satisfiedBy(row, false));
+        Assert.assertTrue(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(location, Operator.EQ, 
UTF8Type.instance.decompose("US")));
         builder.add(new SimpleExpression(height, Operator.GTE, 
Int32Type.instance.decompose(182)));
         builder.add(new SimpleExpression(score, Operator.EQ, 
DoubleType.instance.decompose(1.0d)));
 
-        Assert.assertTrue(builder.complete().satisfiedBy(row, false));
+        Assert.assertTrue(builder.complete().satisfiedBy(row, staticRow, 
false));
 
         builder = new Operation.Builder(OperationType.AND, controller);
         builder.add(new SimpleExpression(height, Operator.GTE, 
Int32Type.instance.decompose(182)));
         builder.add(new SimpleExpression(score, Operator.EQ, 
DoubleType.instance.decompose(1.0d)));
 
-        Assert.assertTrue(builder.complete().satisfiedBy(row, false));
+        Assert.assertTrue(builder.complete().satisfiedBy(row, staticRow, 
false));
     }
 
     private Map<Expression.Op, Expression> convert(Multimap<ColumnDefinition, 
Expression> expressions)
@@ -575,6 +580,62 @@ public class OperationTest extends SchemaLoader
         return converted;
     }
 
+    @Test
+    public void testSatisfiedByWithStatic()
+    {
+        final ColumnDefinition sensorType = getColumn(STATIC_BACKEND, 
UTF8Type.instance.decompose("sensor_type"));
+        final ColumnDefinition value = getColumn(STATIC_BACKEND, 
UTF8Type.instance.decompose("value"));
+
+        Unfiltered row = 
buildRow(Clustering.make(UTF8Type.instance.fromString("date"), 
LongType.instance.decompose(20160401L)),
+                          buildCell(value, 
DoubleType.instance.decompose(24.56), System.currentTimeMillis()));
+        Row staticRow = buildRow(Clustering.STATIC_CLUSTERING,
+                         buildCell(sensorType, 
UTF8Type.instance.decompose("TEMPERATURE"), System.currentTimeMillis()));
+
+        // sensor_type ='TEMPERATURE' AND value = 24.56
+        Operation op = new Operation.Builder(OperationType.AND, controller,
+                                        new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("TEMPERATURE")),
+                                        new SimpleExpression(value, 
Operator.EQ, DoubleType.instance.decompose(24.56))).complete();
+
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
+
+        // sensor_type ='TEMPERATURE' AND value = 30
+        op = new Operation.Builder(OperationType.AND, controller,
+                                             new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("TEMPERATURE")),
+                                             new SimpleExpression(value, 
Operator.EQ, DoubleType.instance.decompose(30.00))).complete();
+
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
+
+        // sensor_type ='PRESSURE' OR value = 24.56
+        op = new Operation.Builder(OperationType.OR, controller,
+                                             new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("TEMPERATURE")),
+                                             new SimpleExpression(value, 
Operator.EQ, DoubleType.instance.decompose(24.56))).complete();
+
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
+
+        // sensor_type ='PRESSURE' OR value = 30
+        op = new Operation.Builder(OperationType.AND, controller,
+                                   new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("PRESSURE")),
+                                   new SimpleExpression(value, Operator.EQ, 
DoubleType.instance.decompose(30.00))).complete();
+
+        Assert.assertFalse(op.satisfiedBy(row, staticRow, false));
+
+        // (sensor_type = 'TEMPERATURE' OR sensor_type = 'PRESSURE') AND value 
= 24.56
+        op = new Operation.Builder(OperationType.OR, controller,
+                                   new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("TEMPERATURE")),
+                                   new SimpleExpression(sensorType, 
Operator.EQ, UTF8Type.instance.decompose("PRESSURE")))
+             .setRight(new Operation.Builder(OperationType.AND, controller,
+                                             new SimpleExpression(value, 
Operator.EQ, DoubleType.instance.decompose(24.56)))).complete();
+
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
+
+        // sensor_type = LIKE 'TEMP%'  AND value = 24.56
+        op = new Operation.Builder(OperationType.AND, controller,
+                                   new SimpleExpression(sensorType, 
Operator.LIKE_PREFIX, UTF8Type.instance.decompose("TEMP")),
+                                   new SimpleExpression(value, Operator.EQ, 
DoubleType.instance.decompose(24.56))).complete();
+
+        Assert.assertTrue(op.satisfiedBy(row, staticRow, false));
+    }
+
     private static class SimpleExpression extends RowFilter.Expression
     {
         SimpleExpression(ColumnDefinition column, Operator operator, 
ByteBuffer value)

Reply via email to