summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWouter Bolsterlee <wouter@bolsterl.ee>2017-01-31 10:48:06 +0100
committerGitHub <noreply@github.com>2017-01-31 10:48:06 +0100
commit33b7700375ba59f1810c30c8cd531577b0718498 (patch)
tree0bb4e8b3fc62b70a5ea10ecba9ecd755c947336c
parente836657732e5f4cb1d17816455f21fd1414e2820 (diff)
parentba76beee9d4e435254813899f82ca72b5d237a91 (diff)
downloadhappybase-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.py4
-rw-r--r--happybase/table.py27
-rw-r--r--tests/test_api.py30
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')"