Search in sources :

Example 1 with Row

use of io.cdap.cdap.api.dataset.table.Row in project cdap by caskdata.

the class IndexedTable method incrementAndGet.

/**
 * Increments (atomically) the specified row and columns by the specified amounts, and returns the new values.
 * Note that performing this operation on an indexed column will generally have a negative impact on performance,
 * since up to three writes will need to be performed for every increment (one removing the index for the previous,
 * pre-increment value, one adding the index for the incremented value, and one for the increment itself).
 *
 * @see Table#incrementAndGet(byte[], byte[][], long[])
 */
@ReadWrite
@Override
public Row incrementAndGet(byte[] row, byte[][] columns, long[] amounts) {
    if (columns.length != amounts.length) {
        throw new IllegalArgumentException("Size of columns and amounts arguments must match");
    }
    Row existingRow = table.get(row, columns);
    byte[][] updatedValues = new byte[columns.length][];
    NavigableMap<byte[], byte[]> result = new TreeMap<>(Bytes.BYTES_COMPARATOR);
    for (int i = 0; i < columns.length; i++) {
        long existingValue = 0L;
        byte[] existingBytes = existingRow.get(columns[i]);
        if (existingBytes != null) {
            if (existingBytes.length != Bytes.SIZEOF_LONG) {
                throw new NumberFormatException("Attempted to increment a value that is not convertible to long," + " row: " + Bytes.toStringBinary(row) + " column: " + Bytes.toStringBinary(columns[i]));
            }
            existingValue = Bytes.toLong(existingBytes);
            if (indexedColumns.contains(columns[i])) {
                index.delete(createIndexKey(row, columns[i], existingBytes), IDX_COL);
            }
        }
        updatedValues[i] = Bytes.toBytes(existingValue + amounts[i]);
        result.put(columns[i], updatedValues[i]);
        if (indexedColumns.contains(columns[i])) {
            index.put(createIndexKey(row, columns[i], updatedValues[i]), IDX_COL, row);
        }
    }
    table.put(row, columns, updatedValues);
    return new Result(row, result);
}
Also used : Row(io.cdap.cdap.api.dataset.table.Row) TreeMap(java.util.TreeMap) Result(io.cdap.cdap.api.dataset.table.Result) ReadWrite(io.cdap.cdap.api.annotation.ReadWrite)

Example 2 with Row

use of io.cdap.cdap.api.dataset.table.Row in project cdap by caskdata.

the class IndexedTable method delete.

@WriteOnly
@Override
public void delete(byte[] row, byte[][] columns) {
    Row existingRow = table.get(row, columns);
    if (existingRow.isEmpty()) {
        // no row to delete
        return;
    }
    // delete all index entries
    deleteIndexEntries(existingRow);
    // delete the row's columns
    table.delete(row, columns);
}
Also used : Row(io.cdap.cdap.api.dataset.table.Row) WriteOnly(io.cdap.cdap.api.annotation.WriteOnly)

Example 3 with Row

use of io.cdap.cdap.api.dataset.table.Row in project cdap by caskdata.

the class KeyValueTable method scan.

/**
 * Scans table.
 * @param startRow start row inclusive. {@code null} means start from first row of the table
 * @param stopRow stop row exclusive. {@code null} means scan all rows to the end of the table
 * @return {@link io.cdap.cdap.api.dataset.lib.CloseableIterator} of
 * {@link KeyValue KeyValue&lt;byte[], byte[]&gt;}
 */
public CloseableIterator<KeyValue<byte[], byte[]>> scan(byte[] startRow, byte[] stopRow) {
    final Scanner scanner = table.scan(startRow, stopRow);
    return new AbstractCloseableIterator<KeyValue<byte[], byte[]>>() {

        private boolean closed = false;

        @Override
        protected KeyValue<byte[], byte[]> computeNext() {
            if (closed) {
                return endOfData();
            }
            Row next = scanner.next();
            if (next != null) {
                return new KeyValue<>(next.getRow(), next.get(KEY_COLUMN));
            }
            close();
            return null;
        }

        @Override
        public void close() {
            scanner.close();
            endOfData();
            closed = true;
        }
    };
}
Also used : Scanner(io.cdap.cdap.api.dataset.table.Scanner) RecordScanner(io.cdap.cdap.api.data.batch.RecordScanner) Row(io.cdap.cdap.api.dataset.table.Row)

Example 4 with Row

use of io.cdap.cdap.api.dataset.table.Row in project cdap by caskdata.

the class IndexedObjectStore method write.

/**
 * Writes to the dataset, deleting any existing secondaryKey corresponding to the key and updates the indexTable with
 * the secondaryKey that is passed.
 *
 * @param key key for storing the object
 * @param object object to be stored
 * @param secondaryKeys indices that can be used to lookup the object
 */
@WriteOnly
public void write(byte[] key, T object, byte[][] secondaryKeys) {
    writeToObjectStore(key, object);
    // Update the secondaryKeys
    // logic:
    // - Get existing secondary keys
    // - Compute diff between existing secondary keys and new secondary keys
    // - Remove the secondaryKeys that are removed
    // - Add the new keys that are added
    Row row = index.get(getPrefixedPrimaryKey(key));
    Set<byte[]> existingSecondaryKeys = new TreeSet<>(Bytes.BYTES_COMPARATOR);
    if (!row.isEmpty()) {
        existingSecondaryKeys = row.getColumns().keySet();
    }
    Set<byte[]> newSecondaryKeys = new TreeSet<>(Bytes.BYTES_COMPARATOR);
    newSecondaryKeys.addAll(Arrays.asList(secondaryKeys));
    List<byte[]> secondaryKeysDeleted = secondaryKeysToDelete(existingSecondaryKeys, newSecondaryKeys);
    if (secondaryKeysDeleted.size() > 0) {
        deleteSecondaryKeys(key, secondaryKeysDeleted.toArray(new byte[secondaryKeysDeleted.size()][]));
    }
    List<byte[]> secondaryKeysAdded = secondaryKeysToAdd(existingSecondaryKeys, newSecondaryKeys);
    // for each key store the secondaryKey. This will be used while deleting old index values.
    if (secondaryKeysAdded.size() > 0) {
        byte[][] fooValues = new byte[secondaryKeysAdded.size()][];
        Arrays.fill(fooValues, EMPTY_VALUE);
        index.put(getPrefixedPrimaryKey(key), secondaryKeysAdded.toArray(new byte[secondaryKeysAdded.size()][]), fooValues);
    }
    for (byte[] secondaryKey : secondaryKeysAdded) {
        // update the index.
        index.put(secondaryKey, key, EMPTY_VALUE);
    }
}
Also used : TreeSet(java.util.TreeSet) Row(io.cdap.cdap.api.dataset.table.Row) WriteOnly(io.cdap.cdap.api.annotation.WriteOnly)

Example 5 with Row

use of io.cdap.cdap.api.dataset.table.Row in project cdap by caskdata.

the class IndexedTableTest method testIncrementIndexing.

@Test
public void testIncrementIndexing() throws Exception {
    DatasetId incrTabInstance = DatasetFrameworkTestUtil.NAMESPACE_ID.dataset("incrtab");
    dsFrameworkUtil.createInstance("indexedTable", incrTabInstance, DatasetProperties.builder().add(IndexedTable.INDEX_COLUMNS_CONF_KEY, "idx1,idx2,idx3").build());
    final IndexedTable iTable = dsFrameworkUtil.getInstance(incrTabInstance);
    final byte[] idxCol1 = Bytes.toBytes("idx1");
    final byte[] idxCol2 = Bytes.toBytes("idx2");
    final byte[] idxCol3 = Bytes.toBytes("idx3");
    final byte[] row1 = Bytes.toBytes("row1");
    try {
        TransactionExecutor tx = dsFrameworkUtil.newTransactionExecutor(iTable);
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                long result = iTable.incrementAndGet(row1, idxCol1, 1);
                assertEquals(1L, result);
            }
        });
        final byte[] oneBytes = Bytes.toBytes(1L);
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                try (Scanner scanner = iTable.readByIndex(idxCol1, oneBytes)) {
                    Row row = scanner.next();
                    TableAssert.assertRow(row, row1, new byte[][] { idxCol1 }, new byte[][] { oneBytes });
                    assertEmpty(scanner);
                }
            }
        });
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                long result = iTable.incrementAndGet(row1, idxCol1, 1);
                assertEquals(2L, result);
            }
        });
        final byte[] twoBytes = Bytes.toBytes(2L);
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                // previous index by value 1 should be gone
                Scanner scanner = iTable.readByIndex(idxCol1, oneBytes);
                try {
                    assertEmpty(scanner);
                } finally {
                    scanner.close();
                }
                // should now be indexed by value 2
                scanner = iTable.readByIndex(idxCol1, twoBytes);
                try {
                    Row row = scanner.next();
                    TableAssert.assertRow(row, row1, new byte[][] { idxCol1 }, new byte[][] { twoBytes });
                    assertEmpty(scanner);
                } finally {
                    scanner.close();
                }
            }
        });
        final byte[] threeBytes = Bytes.toBytes(3L);
        final byte[][] idxCols = new byte[][] { idxCol1, idxCol2, idxCol3 };
        final byte[][] expectedValues = new byte[][] { threeBytes, oneBytes, oneBytes };
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                Row result = iTable.incrementAndGet(row1, idxCols, new long[] { 1, 1, 1 });
                assertNotNull(result);
                TableAssert.assertColumns(result, idxCols, expectedValues);
            }
        });
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                Scanner scanner = iTable.readByIndex(idxCol1, threeBytes);
                try {
                    Row row = scanner.next();
                    TableAssert.assertRow(row, row1, idxCols, expectedValues);
                    assertEmpty(scanner);
                } finally {
                    scanner.close();
                }
                scanner = iTable.readByIndex(idxCol2, oneBytes);
                try {
                    Row row = scanner.next();
                    TableAssert.assertRow(row, row1, idxCols, expectedValues);
                    assertEmpty(scanner);
                } finally {
                    scanner.close();
                }
                scanner = iTable.readByIndex(idxCol3, oneBytes);
                try {
                    Row row = scanner.next();
                    TableAssert.assertRow(row, row1, idxCols, expectedValues);
                    assertEmpty(scanner);
                } finally {
                    scanner.close();
                }
            }
        });
        final byte[] row2 = Bytes.toBytes("row2");
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                // read-less increment on an indexed column should throw an exception
                try {
                    iTable.increment(row2, idxCol1, 1L);
                    fail("Expected IllegalArgumentException performing increment on indexed column");
                } catch (IllegalArgumentException iae) {
                // expected
                }
                // read-less increment on a non-indexed column should succeed
                iTable.increment(row2, valCol, 1L);
                byte[] result = iTable.get(row2, valCol);
                assertArrayEquals(oneBytes, result);
            }
        });
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                iTable.put(row2, valCol, valA);
            }
        });
        // increment against a column with non-long value should fail
        tx.execute(new TransactionExecutor.Subroutine() {

            @Override
            public void apply() throws Exception {
                try {
                    iTable.incrementAndGet(row2, valCol, 1L);
                    fail("Expected NumberFormatException from increment on a column with non-long value");
                } catch (NumberFormatException nfe) {
                // expected
                }
            }
        });
    } finally {
        dsFrameworkUtil.deleteInstance(incrTabInstance);
    }
}
Also used : Scanner(io.cdap.cdap.api.dataset.table.Scanner) TransactionExecutor(org.apache.tephra.TransactionExecutor) Row(io.cdap.cdap.api.dataset.table.Row) DatasetId(io.cdap.cdap.proto.id.DatasetId) Test(org.junit.Test)

Aggregations

Row (io.cdap.cdap.api.dataset.table.Row)80 Scanner (io.cdap.cdap.api.dataset.table.Scanner)40 Test (org.junit.Test)23 Table (io.cdap.cdap.api.dataset.table.Table)15 Put (io.cdap.cdap.api.dataset.table.Put)13 ArrayList (java.util.ArrayList)13 TransactionExecutor (org.apache.tephra.TransactionExecutor)13 Get (io.cdap.cdap.api.dataset.table.Get)12 HashMap (java.util.HashMap)9 Schema (io.cdap.cdap.api.data.schema.Schema)8 MDSKey (io.cdap.cdap.data2.dataset2.lib.table.MDSKey)8 Transaction (org.apache.tephra.Transaction)8 TransactionAware (org.apache.tephra.TransactionAware)8 IOException (java.io.IOException)7 DatasetAdmin (io.cdap.cdap.api.dataset.DatasetAdmin)6 Map (java.util.Map)6 WriteOnly (io.cdap.cdap.api.annotation.WriteOnly)5 StructuredRecord (io.cdap.cdap.api.data.format.StructuredRecord)5 DimensionValue (io.cdap.cdap.api.dataset.lib.cube.DimensionValue)5 HBaseTable (io.cdap.cdap.data2.dataset2.lib.table.hbase.HBaseTable)5