Skip to content

Commit 9e2b07d

Browse files
committed
Merge pull request #1321 from dhermes/bigtable-row-filter-8
Implementing Bigtable TimestampRangeFilter.
2 parents c216a4e + 52e1867 commit 9e2b07d

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed

gcloud/bigtable/row.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""User friendly container for Google Cloud Bigtable Row."""
1616

1717

18+
from gcloud._helpers import _microseconds_from_datetime
1819
from gcloud._helpers import _to_bytes
1920
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
2021

@@ -252,6 +253,74 @@ def to_pb(self):
252253
return data_pb2.RowFilter(column_qualifier_regex_filter=self.regex)
253254

254255

256+
class TimestampRange(object):
257+
"""Range of time with inclusive lower and exclusive upper bounds.
258+
259+
:type start: :class:`datetime.datetime`
260+
:param start: (Optional) The (inclusive) lower bound of the timestamp
261+
range. If omitted, defaults to Unix epoch.
262+
263+
:type end: :class:`datetime.datetime`
264+
:param end: (Optional) The (exclusive) upper bound of the timestamp
265+
range. If omitted, no upper bound is used.
266+
"""
267+
268+
def __init__(self, start=None, end=None):
269+
self.start = start
270+
self.end = end
271+
272+
def __eq__(self, other):
273+
if not isinstance(other, self.__class__):
274+
return False
275+
return (other.start == self.start and
276+
other.end == self.end)
277+
278+
def __ne__(self, other):
279+
return not self.__eq__(other)
280+
281+
def to_pb(self):
282+
"""Converts the :class:`TimestampRange` to a protobuf.
283+
284+
:rtype: :class:`.data_pb2.TimestampRange`
285+
:returns: The converted current object.
286+
"""
287+
timestamp_range_kwargs = {}
288+
if self.start is not None:
289+
timestamp_range_kwargs['start_timestamp_micros'] = (
290+
_microseconds_from_datetime(self.start))
291+
if self.end is not None:
292+
timestamp_range_kwargs['end_timestamp_micros'] = (
293+
_microseconds_from_datetime(self.end))
294+
return data_pb2.TimestampRange(**timestamp_range_kwargs)
295+
296+
297+
class TimestampRangeFilter(RowFilter):
298+
"""Row filter that limits cells to a range of time.
299+
300+
:type range_: :class:`TimestampRange`
301+
:param range_: Range of time that cells should match against.
302+
"""
303+
304+
def __init__(self, range_):
305+
self.range_ = range_
306+
307+
def __eq__(self, other):
308+
if not isinstance(other, self.__class__):
309+
return False
310+
return other.range_ == self.range_
311+
312+
def to_pb(self):
313+
"""Converts the row filter to a protobuf.
314+
315+
First converts the ``range_`` on the current object to a protobuf and
316+
then uses it in the ``timestamp_range_filter`` field.
317+
318+
:rtype: :class:`.data_pb2.RowFilter`
319+
:returns: The converted current object.
320+
"""
321+
return data_pb2.RowFilter(timestamp_range_filter=self.range_.to_pb())
322+
323+
255324
class ColumnRangeFilter(RowFilter):
256325
"""A row filter to restrict to a range of columns.
257326

gcloud/bigtable/test_row.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,120 @@ def test_to_pb_exclusive_end(self):
398398
self.assertEqual(row_filter.to_pb(), expected_pb)
399399

400400

401+
class TestTimestampRange(unittest2.TestCase):
402+
403+
def _getTargetClass(self):
404+
from gcloud.bigtable.row import TimestampRange
405+
return TimestampRange
406+
407+
def _makeOne(self, *args, **kwargs):
408+
return self._getTargetClass()(*args, **kwargs)
409+
410+
def test_constructor(self):
411+
start = object()
412+
end = object()
413+
time_range = self._makeOne(start=start, end=end)
414+
self.assertTrue(time_range.start is start)
415+
self.assertTrue(time_range.end is end)
416+
417+
def test___eq__(self):
418+
start = object()
419+
end = object()
420+
time_range1 = self._makeOne(start=start, end=end)
421+
time_range2 = self._makeOne(start=start, end=end)
422+
self.assertEqual(time_range1, time_range2)
423+
424+
def test___eq__type_differ(self):
425+
start = object()
426+
end = object()
427+
time_range1 = self._makeOne(start=start, end=end)
428+
time_range2 = object()
429+
self.assertNotEqual(time_range1, time_range2)
430+
431+
def test___ne__same_value(self):
432+
start = object()
433+
end = object()
434+
time_range1 = self._makeOne(start=start, end=end)
435+
time_range2 = self._makeOne(start=start, end=end)
436+
comparison_val = (time_range1 != time_range2)
437+
self.assertFalse(comparison_val)
438+
439+
def _to_pb_helper(self, start_micros=None, end_micros=None):
440+
import datetime
441+
from gcloud._helpers import _EPOCH
442+
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
443+
444+
pb_kwargs = {}
445+
446+
start = None
447+
if start_micros is not None:
448+
start = _EPOCH + datetime.timedelta(microseconds=start_micros)
449+
pb_kwargs['start_timestamp_micros'] = start_micros
450+
end = None
451+
if end_micros is not None:
452+
end = _EPOCH + datetime.timedelta(microseconds=end_micros)
453+
pb_kwargs['end_timestamp_micros'] = end_micros
454+
time_range = self._makeOne(start=start, end=end)
455+
456+
expected_pb = data_pb2.TimestampRange(**pb_kwargs)
457+
self.assertEqual(time_range.to_pb(), expected_pb)
458+
459+
def test_to_pb(self):
460+
# Makes sure already milliseconds granularity
461+
start_micros = 30871000
462+
end_micros = 12939371000
463+
self._to_pb_helper(start_micros=start_micros,
464+
end_micros=end_micros)
465+
466+
def test_to_pb_start_only(self):
467+
# Makes sure already milliseconds granularity
468+
start_micros = 30871000
469+
self._to_pb_helper(start_micros=start_micros)
470+
471+
def test_to_pb_end_only(self):
472+
# Makes sure already milliseconds granularity
473+
end_micros = 12939371000
474+
self._to_pb_helper(end_micros=end_micros)
475+
476+
477+
class TestTimestampRangeFilter(unittest2.TestCase):
478+
479+
def _getTargetClass(self):
480+
from gcloud.bigtable.row import TimestampRangeFilter
481+
return TimestampRangeFilter
482+
483+
def _makeOne(self, *args, **kwargs):
484+
return self._getTargetClass()(*args, **kwargs)
485+
486+
def test_constructor(self):
487+
range_ = object()
488+
row_filter = self._makeOne(range_)
489+
self.assertTrue(row_filter.range_ is range_)
490+
491+
def test___eq__type_differ(self):
492+
range_ = object()
493+
row_filter1 = self._makeOne(range_)
494+
row_filter2 = object()
495+
self.assertNotEqual(row_filter1, row_filter2)
496+
497+
def test___eq__same_value(self):
498+
range_ = object()
499+
row_filter1 = self._makeOne(range_)
500+
row_filter2 = self._makeOne(range_)
501+
self.assertEqual(row_filter1, row_filter2)
502+
503+
def test_to_pb(self):
504+
from gcloud.bigtable._generated import bigtable_data_pb2 as data_pb2
505+
from gcloud.bigtable.row import TimestampRange
506+
507+
range_ = TimestampRange()
508+
row_filter = self._makeOne(range_)
509+
pb_val = row_filter.to_pb()
510+
expected_pb = data_pb2.RowFilter(
511+
timestamp_range_filter=data_pb2.TimestampRange())
512+
self.assertEqual(pb_val, expected_pb)
513+
514+
401515
class TestValueRegexFilter(unittest2.TestCase):
402516

403517
def _getTargetClass(self):

0 commit comments

Comments
 (0)