diff options
| author | Wouter Bolsterlee <wouter@bolsterl.ee> | 2017-01-31 10:48:06 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-31 10:48:06 +0100 |
| commit | 33b7700375ba59f1810c30c8cd531577b0718498 (patch) | |
| tree | 0bb4e8b3fc62b70a5ea10ecba9ecd755c947336c | |
| parent | e836657732e5f4cb1d17816455f21fd1414e2820 (diff) | |
| parent | ba76beee9d4e435254813899f82ca72b5d237a91 (diff) | |
| download | happybase-33b7700375ba59f1810c30c8cd531577b0718498.tar.gz | |
Merge pull request #155 from seanpmorgan/reversed-scan
Adding reversed scanner, and new compat mode 0.98
| -rw-r--r-- | happybase/connection.py | 4 | ||||
| -rw-r--r-- | happybase/table.py | 27 | ||||
| -rw-r--r-- | tests/test_api.py | 30 |
3 files changed, 55 insertions, 6 deletions
diff --git a/happybase/connection.py b/happybase/connection.py index 01ab83b..368af07 100644 --- a/happybase/connection.py +++ b/happybase/connection.py @@ -20,7 +20,7 @@ logger = logging.getLogger(__name__) STRING_OR_BINARY = (six.binary_type, six.text_type) -COMPAT_MODES = ('0.90', '0.92', '0.94', '0.96') +COMPAT_MODES = ('0.90', '0.92', '0.94', '0.96', '0.98') THRIFT_TRANSPORTS = dict( buffered=TBufferedTransport, framed=TFramedTransport, @@ -33,7 +33,7 @@ THRIFT_PROTOCOLS = dict( DEFAULT_HOST = 'localhost' DEFAULT_PORT = 9090 DEFAULT_TRANSPORT = 'buffered' -DEFAULT_COMPAT = '0.96' +DEFAULT_COMPAT = '0.98' DEFAULT_PROTOCOL = 'binary' diff --git a/happybase/table.py b/happybase/table.py index c982ef8..d0469e1 100644 --- a/happybase/table.py +++ b/happybase/table.py @@ -222,7 +222,7 @@ class Table(object): def scan(self, row_start=None, row_stop=None, row_prefix=None, columns=None, filter=None, timestamp=None, include_timestamp=False, batch_size=1000, scan_batching=None, - limit=None, sorted_columns=False): + limit=None, sorted_columns=False, reverse=False): """Create a scanner for data in the table. This method returns an iterable that can be used for looping over the @@ -268,6 +268,11 @@ class Table(object): by this scanner will be retrieved in sorted order, and the data will be stored in `OrderedDict` instances. + If `reverse` is `True`, the scanner will perform the scan in reverse. + This means that `row_start` must be lexicographically after `row_stop`. + Note that the start of the range is inclusive, while the end is + exclusive just as in the forward scan. + **Compatibility notes:** * The `filter` argument is only available when using HBase 0.92 @@ -277,6 +282,12 @@ class Table(object): * The `sorted_columns` argument is only available when using HBase 0.96 (or up). + * The `reverse` argument is only available when using HBase 0.98 + (or up). + + .. versionadded:: TODO + `reverse` argument + .. versionadded:: 0.8 `sorted_columns` argument @@ -294,6 +305,7 @@ class Table(object): :param bool scan_batching: server-side scan batching (optional) :param int limit: max number of rows to return :param bool sorted_columns: whether to return sorted columns + :param bool reverse: whether to perform scan in reverse :return: generator yielding the rows matching the scan :rtype: iterable of `(row_key, row_data)` tuples @@ -311,14 +323,22 @@ class Table(object): raise NotImplementedError( "'sorted_columns' is only supported in HBase >= 0.96") + if reverse and self.connection.compat < '0.98': + raise NotImplementedError( + "'reverse' is only supported in HBase >= 0.98") + if row_prefix is not None: if row_start is not None or row_stop is not None: raise TypeError( "'row_prefix' cannot be combined with 'row_start' " "or 'row_stop'") - row_start = row_prefix - row_stop = bytes_increment(row_prefix) + if reverse: + row_start = bytes_increment(row_prefix) + row_stop = row_prefix + else: + row_start = row_prefix + row_stop = bytes_increment(row_prefix) if row_start is None: row_start = '' @@ -376,6 +396,7 @@ class Table(object): filterString=filter, batchSize=scan_batching, sortColumns=sorted_columns, + reversed=reverse, ) scan_id = self.connection.client.scannerOpenWithScan( self.name, scan, {}) diff --git a/tests/test_api.py b/tests/test_api.py index 642bb0d..f5242a5 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -27,7 +27,7 @@ from happybase import Connection, ConnectionPool, NoConnectionsAvailable HAPPYBASE_HOST = os.environ.get('HAPPYBASE_HOST') HAPPYBASE_PORT = os.environ.get('HAPPYBASE_PORT') -HAPPYBASE_COMPAT = os.environ.get('HAPPYBASE_COMPAT', '0.96') +HAPPYBASE_COMPAT = os.environ.get('HAPPYBASE_COMPAT', '0.98') HAPPYBASE_TRANSPORT = os.environ.get('HAPPYBASE_TRANSPORT', 'buffered') KEEP_TABLE = ('HAPPYBASE_NO_CLEANUP' in os.environ) @@ -446,6 +446,34 @@ def test_scan_sorting(): list(row.items())) +def test_scan_reverse(): + + if connection.compat < '0.98': + with assert_raises(NotImplementedError): + list(table.scan(reverse=True)) + return + + with table.batch() as b: + for i in range(2000): + b.put(('row-scan-reverse-%04d' % i).encode('ascii'), + {b'cf1:col1': b'v1', + b'cf1:col2': b'v2'}) + + scan = table.scan(row_prefix=b'row-scan-reverse', reverse=True) + assert_equal(2000, len(list(scan))) + + scan = table.scan(limit=10, reverse=True) + assert_equal(10, len(list(scan))) + + scan = table.scan(row_start=b'row-scan-reverse-1999', + row_stop=b'row-scan-reverse-0000', reverse=True) + key, data = next(scan) + assert_equal(b'row-scan-reverse-1999', key) + + key, data = list(scan)[-1] + assert_equal(b'row-scan-reverse-0001', key) + + def test_scan_filter_and_batch_size(): # See issue #54 and #56 filter = b"SingleColumnValueFilter ('cf1', 'qual1', =, 'binary:val1')" |
