package org.scale7.cassandra.pelops; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.scale7.cassandra.pelops.Bytes.fromChar; import static org.scale7.cassandra.pelops.Bytes.fromLong; import static org.scale7.cassandra.pelops.Bytes.fromUTF8; import static org.scale7.cassandra.pelops.Bytes.toUTF8; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_COMPARATOR_BYTES; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_COMPARATOR_LONG; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_TYPE_STANDARD; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_TYPE_SUPER; import static org.scale7.cassandra.pelops.ColumnFamilyManager.CFDEF_VALIDATION_CLASS_COUNTER; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import org.apache.cassandra.thrift.CfDef; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ColumnDef; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.CounterColumn; import org.apache.cassandra.thrift.IndexOperator; import org.apache.cassandra.thrift.IndexType; import org.apache.cassandra.thrift.SuperColumn; import org.junit.BeforeClass; import org.junit.Test; import org.scale7.cassandra.pelops.exceptions.InvalidRequestException; import org.scale7.cassandra.pelops.support.AbstractIntegrationTest; import com.google.common.collect.Lists; /** * Tests the {@link org.scale7.cassandra.pelops.Selector} class. */ public class SelectorIntegrationTest extends AbstractIntegrationTest { public static final String CF = "SEL_CF"; public static final String CF_KEY_ITERATOR = "SEL_CF_KI"; public static final String CF_INDEXED = "SEL_I_CF"; public static final String CF_COUNTER = "SEL_C_CF"; public static final String SCF = "SEL_SCF"; @BeforeClass public static void setup() throws Exception { setup( Arrays.asList( new CfDef(KEYSPACE, CF_INDEXED) .setColumn_type(CFDEF_TYPE_STANDARD) .setComparator_type(CFDEF_COMPARATOR_BYTES) .setDefault_validation_class(CFDEF_COMPARATOR_BYTES) .setColumn_metadata(Arrays.asList( new ColumnDef(Bytes.fromUTF8("name").getBytes(), CFDEF_COMPARATOR_BYTES) // using default CF validation class (CFDEF_COMPARATOR_BYTES) .setIndex_name("NameIndex") .setIndex_type(IndexType.KEYS), new ColumnDef(Bytes.fromUTF8("age").getBytes(), CFDEF_COMPARATOR_LONG) .setValidation_class(CFDEF_COMPARATOR_LONG) .setIndex_name("AgeIndex") .setIndex_type(IndexType.KEYS))), new CfDef(KEYSPACE, CF) .setColumn_type(CFDEF_TYPE_STANDARD) .setComparator_type(CFDEF_COMPARATOR_BYTES), new CfDef(KEYSPACE, CF_KEY_ITERATOR) .setColumn_type(CFDEF_TYPE_STANDARD) .setComparator_type(CFDEF_COMPARATOR_BYTES), new CfDef(KEYSPACE, CF_COUNTER) .setColumn_type(CFDEF_TYPE_STANDARD) .setComparator_type(CFDEF_COMPARATOR_BYTES) .setDefault_validation_class(CFDEF_VALIDATION_CLASS_COUNTER), new CfDef(KEYSPACE, SCF) .setColumn_type(CFDEF_TYPE_SUPER) .setComparator_type(CFDEF_COMPARATOR_BYTES) .setSubcomparator_type(CFDEF_COMPARATOR_BYTES) ) ); } @Override public void prepareData() throws Exception { Mutator mutator = createMutator(); // prep the column family data for (long i = 0; i < 100; i++) { mutator.writeColumns(CF, fromLong(i), createAlphabetColumns(mutator)); mutator.writeColumns(CF_KEY_ITERATOR, fromLong(i), createAlphabetColumns(mutator)); mutator.writeCounterColumn(CF_COUNTER, fromLong(i), fromLong(i), i); mutator.writeCounterColumn(CF_COUNTER, fromLong(i), fromLong(i + 1), i); // prep the super column family data for (char letter = 'A'; letter <= 'Z'; letter++) { mutator.writeSubColumns(SCF, fromLong(i), fromChar(letter), createAlphabetColumns(mutator)); } } // prep indexed column family data for (long i = 0; i <= 2; i++) { mutator.writeColumn(CF_INDEXED, fromLong(i), mutator.newColumn("name", fromUTF8("name-" + (char)('a' + i)))); mutator.writeColumn(CF_INDEXED, fromLong(i), mutator.newColumn("age", fromLong(i % 2))); } mutator.execute(ConsistencyLevel.ONE); } private static List<Column> createAlphabetColumns(Mutator mutator) { List<Column> columns = new ArrayList<Column>(); for (char letter = 'a'; letter <= 'z'; letter++) { columns.add(mutator.newColumn(fromChar(letter), fromChar(letter))); } return columns; } private static char[] createAlphabet() { char[] letters = new char[26]; int index = 0; for (char letter = 'a'; letter <= 'z'; letter++) { letters[index++] = letter; } return letters; } @Test public void testGetCounterColumnFromRow() { CounterColumn counterColumn = createSelector().getCounterColumnFromRow(CF_COUNTER, fromLong(50), fromLong(50), ConsistencyLevel.QUORUM); assertEquals("Wrong counter column value returned", 50, counterColumn.getValue()); } @Test public void testGetCounterColumnsFromRow() { List<CounterColumn> columns = createSelector().getCounterColumnsFromRow(CF_COUNTER, fromLong(50), false, ConsistencyLevel.QUORUM); assertEquals("Wrong number of columns returned", 2, columns.size()); assertEquals("Wrong counter column value returned", 50, Selector.getCountColumnValue(columns, fromLong(50), null)); assertEquals("Wrong counter column value returned", 50, Selector.getCountColumnValue(columns, fromLong(51), null)); } @Test public void testGetCounterColumnsFromRows() { LinkedHashMap<Bytes, List<CounterColumn>> columnsFromRows = createSelector().getCounterColumnsFromRows(CF_COUNTER, Arrays.asList(fromLong(50), fromLong(51)), false, ConsistencyLevel.QUORUM); assertEquals("Wrong number of rows returned", 2, columnsFromRows.size()); assertEquals("Wrong number of columns returned", 2, columnsFromRows.get(fromLong(50)).size()); assertEquals("Wrong number of columns returned", 2, columnsFromRows.get(fromLong(51)).size()); } @Test public void testGetIndexedColumns() throws Exception { int MAX_ROWS_IN_RESULT = 3; int MAX_COLUMNS_PER_KEY = 3; Map<Bytes, List<Column>> keys = createSelector().getIndexedColumns(CF_INDEXED, Selector.newIndexClause(Bytes.EMPTY, MAX_ROWS_IN_RESULT, Selector.newIndexExpression("age", IndexOperator.EQ, Bytes.fromLong(1))), Selector.newColumnsPredicateAll(true, MAX_COLUMNS_PER_KEY), ConsistencyLevel.ONE); assertEquals("Wrong number of keys returned", 1, keys.size()); Bytes key = keys.keySet().iterator().next(); assertEquals("Wrong number key value returned", 1, key.toLong()); assertEquals("Wrong number of columns in key returned", 2, keys.get(key).size()); Column column = keys.get(key).get(0); assertEquals("Wrong column 'name' value", "name-b", toUTF8(column.getValue())); column = keys.get(key).get(1); assertEquals("Wrong column 'value' value", 1L, Bytes.fromByteArray(column.getValue()).toLong()); } @Test public void testGetIndexedColumnsMoreResults() throws Exception { int MAX_ROWS_IN_RESULT = 3; int MAX_COLUMNS_PER_KEY = 3; Map<Bytes, List<Column>> keys = createSelector().getIndexedColumns(CF_INDEXED, Selector.newIndexClause(Bytes.EMPTY, MAX_ROWS_IN_RESULT, Selector.newIndexExpression("age", IndexOperator.EQ, Bytes.fromLong(0))), Selector.newColumnsPredicateAll(false, MAX_COLUMNS_PER_KEY), ConsistencyLevel.ONE); assertEquals("Wrong number of keys returned", 2, keys.size()); List<Long> keyList = new ArrayList<Long>(); for (Bytes key : keys.keySet()) { keyList.add(key.toLong()); } Collections.sort(keyList); assertEquals("Wrong key value", 0L, keyList.get(0).longValue()); assertEquals("Wrong key value", 2L, keyList.get(1).longValue()); } @Test public void testGetIndexedColumnsNonEq() throws Exception { int MAX_ROWS_IN_RESULT = 3; int MAX_COLUMNS_PER_KEY = 3; try { Map<Bytes, List<Column>> keys = createSelector().getIndexedColumns(CF_INDEXED, Selector.newIndexClause(Bytes.EMPTY, MAX_ROWS_IN_RESULT, Selector.newIndexExpression("age", IndexOperator.LT, Bytes.fromLong(1))), Selector.newColumnsPredicateAll(true, MAX_COLUMNS_PER_KEY), ConsistencyLevel.ONE); assertEquals("Wrong number of keys returned", 1, keys.size()); Bytes key = keys.keySet().iterator().next(); assertEquals("Wrong number key value returned", 0, key.toLong()); assertEquals("Wrong number of columns in key returned", 2, keys.get(key).size()); Column column = keys.get(key).get(0); assertEquals("Wrong column 'name' value", "name-1", toUTF8(column.getValue())); column = keys.get(key).get(1); assertEquals("Wrong column 'value' value", 0L, Bytes.fromByteArray(column.getValue()).toLong()); fail("Other than EQ index operator is supported now."); } catch (InvalidRequestException e) { assertEquals("No indexed columns present in index clause with operator EQ", e.getWhy()); } } @Test public void testGetIndexedColumnsNoMatches() throws Exception { int MAX_ROWS_IN_RESULT = 3; int MAX_COLUMNS_PER_KEY = 3; Map<Bytes, List<Column>> keys = createSelector().getIndexedColumns(CF_INDEXED, Selector.newIndexClause(Bytes.EMPTY, MAX_ROWS_IN_RESULT, Selector.newIndexExpression("age", IndexOperator.EQ, Bytes.fromLong(11))), Selector.newColumnsPredicateAll(true, MAX_COLUMNS_PER_KEY), ConsistencyLevel.ONE); assertEquals("Wrong number of keys returned", 0, keys.size()); } @Test public void testGetPageOfColumnsFromRow() throws Exception { char[] expectedColumns = new char[] { 'a', 'b', 'c', 'd', 'e' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), (Bytes) null, false, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowWithOffset() throws Exception { char[] expectedColumns = new char[] { 'f', 'g', 'h', 'i', 'j' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('e'), false, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowWithNoMatches() throws Exception { char[] expectedColumns = new char[] { }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('z'), false, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowWithOffsetAndInsufficientMatches() throws Exception { char[] expectedColumns = new char[] { 'x', 'y', 'z' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('w'), false, 1000, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowWithOffsetThatDoesNotExist() throws Exception { char[] expectedColumns = new char[] { 'a', 'b', 'c', 'd', 'e' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('`'), false, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowWithOffsetThatDoesNotExistAndInsufficientMatches() throws Exception { List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('`'), false, 1000, ConsistencyLevel.ONE); assertEquals("Wrong number of columns returned", 26, columns.size()); } @Test public void testGetPageOfColumnsFromRowReverse() throws Exception { char[] expectedColumns = new char[] { 'z', 'y', 'x', 'w', 'v' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), (Bytes) null, true, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } @Test public void testGetPageOfColumnsFromRowReverseWithOffset() throws Exception { char[] expectedColumns = new char[] { 'u', 't', 's', 'r', 'q' }; List<Column> columns = createSelector().getPageOfColumnsFromRow(CF, fromLong(25l), fromChar('v'), true, expectedColumns.length, ConsistencyLevel.ONE); verifyColumns(expectedColumns, columns); } private void verifyColumns(char[] expectedColumns, List<Column> columns) { assertEquals("Wrong number of columns returned", expectedColumns.length, columns.size()); for (int i = 0; i < expectedColumns.length; i++) { assertEquals("Wrong column value returned", expectedColumns[i], Bytes.fromByteArray(columns.get(i).getValue()).toChar()); } } @Test public void testGetPageOfSuperColumnsFromRow() throws Exception { char[] expectedColumns = new char[] { 'A', 'B', 'C', 'D', 'E' }; List<SuperColumn> superColumns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(50l), (Bytes) null, false, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, superColumns); } @Test public void testGetPageOfSuperColumnsFromRowWithOffset() throws Exception { char[] expectedColumns = new char[] { 'F', 'G', 'H', 'I', 'J' }; List<SuperColumn> superColumns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(50l), fromChar('E'), false, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, superColumns); } @Test public void testGetPageOfSuperColumnsFromRowWithOffsetAndInsufficientMatches() throws Exception { char[] expectedColumns = new char[] { 'X', 'Y', 'Z' }; List<SuperColumn> superColumns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(50l), fromChar('W'), false, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, superColumns); } @Test public void testGetPageOfSuperColumnsFromRowWithOffsetThatDoesNotExist() throws Exception { char[] expectedColumns = new char[] { 'A', 'B', 'C', 'D', 'E' }; List<SuperColumn> columns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(25l), fromChar('@'), false, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, columns); } @Test public void testGetPageOfSuperColumnsFromRowReverse() throws Exception { char[] expectedColumns = new char[] { 'Z', 'Y', 'X', 'W', 'V' }; List<SuperColumn> superColumns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(50l), (Bytes) null, true, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, superColumns); } @Test public void testGetPageOfSuperColumnsFromRowReverseWithOffset() throws Exception { char[] expectedColumns = new char[] { 'U', 'T', 'S', 'R', 'Q' }; List<SuperColumn> superColumns = createSelector().getPageOfSuperColumnsFromRow(SCF, fromLong(50l), fromChar('V'), true, expectedColumns.length, ConsistencyLevel.ONE); verifySuperColumns(expectedColumns, superColumns); } @Test public void testIterateSuperColumnsFromRow() { for (char letter = 'A'; letter <= 'Z'; letter++) { } Iterator<SuperColumn> iterator = createSelector().iterateSuperColumnsFromRow(SCF, fromLong(50l), null, false, 10, ConsistencyLevel.ONE); char letter = 'A'; int count = 0; while (iterator.hasNext()) { SuperColumn superColumn = iterator.next(); assertEquals("Wrong super column value returned", letter, Bytes.fromByteArray(superColumn.getName()).toChar()); letter++; count++; } assertEquals("Not all super columns were processed", 26, count); } @Test public void testIterateSuperColumnsFromRowUsingOnlyNext() { Iterator<SuperColumn> iterator = createSelector().iterateSuperColumnsFromRow(SCF, fromLong(50l), null, false, 10, ConsistencyLevel.ONE); char letter = 'A'; int count = 0; for (int i = 0; i < 26; i++) { SuperColumn superColumn = iterator.next(); assertEquals("Wrong super column value returned", letter, Bytes.fromByteArray(superColumn.getName()).toChar()); letter++; count++; } assertEquals("Not all super columns were processed", 26, count); try { iterator.next(); fail("The iterator should have thrown a NoSuchElementException exception"); } catch (NoSuchElementException e) { // expected } } @Test public void testIterateColumnsFromRow() { Iterator<Column> iterator = createSelector().iterateColumnsFromRow(CF, fromLong(50l), null, false, 10, ConsistencyLevel.ONE); char letter = 'a'; int count = 0; while (iterator.hasNext()) { Column column = iterator.next(); assertEquals("Wrong column value returned", letter, Bytes.fromByteArray(column.getName()).toChar()); letter++; count++; } assertEquals("Not all columns were processed", 26, count); } @Test public void testIterateColumnsFromRowUsingOnlyNext() { Iterator<Column> iterator = createSelector().iterateColumnsFromRow(CF, fromLong(50l), null, false, 10, ConsistencyLevel.ONE); char letter = 'a'; int count = 0; for (int i = 0; i < 26; i++) { Column column = iterator.next(); assertEquals("Wrong column value returned", letter, Bytes.fromByteArray(column.getName()).toChar()); letter++; count++; } assertEquals("Not all columns were processed", 26, count); try { iterator.next(); fail("The iterator should have thrown a NoSuchElementException exception"); } catch (NoSuchElementException e) { // expected } } @Test public void testIterateColumnsFromRows() { Iterator<Map.Entry<Bytes, List<Column>>> iterator = createSelector().iterateColumnsFromRows(CF_KEY_ITERATOR, 10, ConsistencyLevel.ONE); int count = 0; Set<Bytes> keys = new HashSet<Bytes>(100); while (iterator.hasNext()) { Map.Entry<Bytes, List<Column>> row = iterator.next(); keys.add(row.getKey()); count++; } assertEquals("Not all rows were processed", 100, count); for (long i = 0; i < 100; i++) { final Bytes key = Bytes.fromLong(i); assertTrue(String.format("Key %s was missing from the results", i), keys.contains(key)); } } @Test public void testIterateColumnsFromRowsOnlyUsingNext() { Iterator<Map.Entry<Bytes, List<Column>>> iterator = createSelector().iterateColumnsFromRows(CF_KEY_ITERATOR, 10, ConsistencyLevel.ONE); Set<Bytes> keys = new HashSet<Bytes>(100); for (int i = 0; i < 100; i++) { Map.Entry<Bytes, List<Column>> row = iterator.next(); keys.add(row.getKey()); } for (long i = 0; i < 100; i++) { final Bytes key = Bytes.fromLong(i); assertTrue(String.format("Key %s was missing from the results", i), keys.contains(key)); } } @Test public void testGetColumnsFromRowsOrdering() { List<Bytes> keys = Lists.newArrayList(fromLong(5), fromLong(6), fromLong(8), fromLong(9), fromLong(7)); // out of order on purpose Map<Bytes, List<Column>> columnsFromRows = createSelector().getColumnsFromRows(CF, keys, false, ConsistencyLevel.ONE); // make sure the results are in order of the provided keys int index = 0; for (Bytes bytes : columnsFromRows.keySet()) { assertEquals("Results were not in the order of the provided keys", keys.get(index), bytes); verifyColumns(createAlphabet(), columnsFromRows.get(bytes)); index++; } } @Test public void testGetColumnsFromRowsMissingKey() { final Bytes missingKey = fromLong(Long.MAX_VALUE - 10); List<Bytes> keys = Lists.newArrayList(fromLong(5), fromLong(6), fromLong(8), fromLong(9), fromLong(7), missingKey); // out of order on purpose Map<Bytes, List<Column>> columnsFromRows = createSelector().getColumnsFromRows(CF, keys, false, ConsistencyLevel.ONE); assertTrue("The missing key didn't exist in the results", columnsFromRows.containsKey(missingKey)); assertNotNull("The missing keys value should be an empty list", columnsFromRows.get(missingKey)); assertTrue("The missing keys value should be an empty list", columnsFromRows.get(missingKey).isEmpty()); } @Test public void testGetSubColumnsFromRowsOrdering() { List<Bytes> keys = Lists.newArrayList(fromLong(5), fromLong(6), fromLong(8), fromLong(9), fromLong(7)); // out of order on purpose Map<Bytes, List<Column>> columnsFromRows = createSelector().getSubColumnsFromRows(SCF, keys, fromChar('B'), false, ConsistencyLevel.ONE); // make sure the results are in order of the provided keys int index = 0; for (Bytes bytes : columnsFromRows.keySet()) { assertEquals("Results were not in the order of the provided keys", keys.get(index), bytes); verifyColumns(createAlphabet(), columnsFromRows.get(bytes)); index++; } } @Test public void testGetSubColumnsFromRowsMissingKey() { final Bytes missingKey = fromLong(Long.MAX_VALUE - 10); List<Bytes> keys = Lists.newArrayList(fromLong(5), fromLong(6), fromLong(8), fromLong(9), fromLong(7), missingKey); // out of order on purpose Map<Bytes, List<Column>> columnsFromRows = createSelector().getSubColumnsFromRows(SCF, keys, fromChar('B'), false, ConsistencyLevel.ONE); assertTrue("The missing key didn't exist in the results", columnsFromRows.containsKey(missingKey)); assertNotNull("The missing keys value should be an empty list", columnsFromRows.get(missingKey)); assertTrue("The missing keys value should be an empty list", columnsFromRows.get(missingKey).isEmpty()); } private void verifySuperColumns(char[] expectedColumns, List<SuperColumn> superColumns) { assertEquals("Wrong number of super columns returned", expectedColumns.length, superColumns.size()); for (int i = 0; i < expectedColumns.length; i++) { assertEquals("Wrong super column value returned", expectedColumns[i], Bytes.fromByteArray(superColumns.get(i).getName()).toChar()); } } }