Skip to content

Commit 2c223e7

Browse files
committed
Merge pull request #1527 from dhermes/happybase-virtual-table
Adding virtual implementations for remaining HappyBase Table methods.
2 parents 8c7a232 + 7504929 commit 2c223e7

File tree

3 files changed

+348
-1
lines changed

3 files changed

+348
-1
lines changed

gcloud/bigtable/happybase/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class Connection(object):
9797
9898
The arguments ``host``, ``port``, ``compat``, ``transport`` and
9999
``protocol`` are allowed (as keyword arguments) for compatibility with
100-
HappyBase. However, they will not be used in anyway, and will cause a
100+
HappyBase. However, they will not be used in any way, and will cause a
101101
warning if passed.
102102
103103
:type timeout: int

gcloud/bigtable/happybase/table.py

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from gcloud.bigtable.column_family import GCRuleIntersection
2424
from gcloud.bigtable.column_family import MaxAgeGCRule
2525
from gcloud.bigtable.column_family import MaxVersionsGCRule
26+
from gcloud.bigtable.happybase.batch import _WAL_SENTINEL
2627
from gcloud.bigtable.table import Table as _LowLevelTable
2728

2829

@@ -130,6 +131,296 @@ def regions(self):
130131
raise NotImplementedError('The Cloud Bigtable API does not have a '
131132
'concept of splitting a table into regions.')
132133

134+
def row(self, row, columns=None, timestamp=None, include_timestamp=False):
135+
"""Retrieve a single row of data.
136+
137+
Returns the latest cells in each column (or all columns if ``columns``
138+
is not specified). If a ``timestamp`` is set, then **latest** becomes
139+
**latest** up until ``timestamp``.
140+
141+
:type row: str
142+
:param row: Row key for the row we are reading from.
143+
144+
:type columns: list
145+
:param columns: (Optional) Iterable containing column names (as
146+
strings). Each column name can be either
147+
148+
* an entire column family: ``fam`` or ``fam:``
149+
* an single column: ``fam:col``
150+
151+
:type timestamp: int
152+
:param timestamp: (Optional) Timestamp (in milliseconds since the
153+
epoch). If specified, only cells returned before the
154+
the timestamp will be returned.
155+
156+
:type include_timestamp: bool
157+
:param include_timestamp: Flag to indicate if cell timestamps should be
158+
included with the output.
159+
160+
:rtype: dict
161+
:returns: Dictionary containing all the latest column values in
162+
the row.
163+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
164+
always (until the method is implemented).
165+
"""
166+
raise NotImplementedError
167+
168+
def rows(self, rows, columns=None, timestamp=None,
169+
include_timestamp=False):
170+
"""Retrieve multiple rows of data.
171+
172+
All optional arguments behave the same in this method as they do in
173+
:meth:`row`.
174+
175+
:type rows: list
176+
:param rows: Iterable of the row keys for the rows we are reading from.
177+
178+
:type columns: list
179+
:param columns: (Optional) Iterable containing column names (as
180+
strings). Each column name can be either
181+
182+
* an entire column family: ``fam`` or ``fam:``
183+
* an single column: ``fam:col``
184+
185+
:type timestamp: int
186+
:param timestamp: (Optional) Timestamp (in milliseconds since the
187+
epoch). If specified, only cells returned before (or
188+
at) the timestamp will be returned.
189+
190+
:type include_timestamp: bool
191+
:param include_timestamp: Flag to indicate if cell timestamps should be
192+
included with the output.
193+
194+
:rtype: list
195+
:returns: A list of pairs, where the first is the row key and the
196+
second is a dictionary with the filtered values returned.
197+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
198+
always (until the method is implemented).
199+
"""
200+
raise NotImplementedError
201+
202+
def cells(self, row, column, versions=None, timestamp=None,
203+
include_timestamp=False):
204+
"""Retrieve multiple versions of a single cell from the table.
205+
206+
:type row: str
207+
:param row: Row key for the row we are reading from.
208+
209+
:type column: str
210+
:param column: Column we are reading from; of the form ``fam:col``.
211+
212+
:type versions: int
213+
:param versions: (Optional) The maximum number of cells to return. If
214+
not set, returns all cells found.
215+
216+
:type timestamp: int
217+
:param timestamp: (Optional) Timestamp (in milliseconds since the
218+
epoch). If specified, only cells returned before (or
219+
at) the timestamp will be returned.
220+
221+
:type include_timestamp: bool
222+
:param include_timestamp: Flag to indicate if cell timestamps should be
223+
included with the output.
224+
225+
:rtype: list
226+
:returns: List of values in the cell (with timestamps if
227+
``include_timestamp`` is :data:`True`).
228+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
229+
always (until the method is implemented).
230+
"""
231+
raise NotImplementedError
232+
233+
def scan(self, row_start=None, row_stop=None, row_prefix=None,
234+
columns=None, filter=None, timestamp=None,
235+
include_timestamp=False, limit=None, **kwargs):
236+
"""Create a scanner for data in this table.
237+
238+
This method returns a generator that can be used for looping over the
239+
matching rows.
240+
241+
If ``row_prefix`` is specified, only rows with row keys matching the
242+
prefix will be returned. If given, ``row_start`` and ``row_stop``
243+
cannot be used.
244+
245+
.. note::
246+
247+
Both ``row_start`` and ``row_stop`` can be :data:`None` to specify
248+
the start and the end of the table respectively. If both are
249+
omitted, a full table scan is done. Note that this usually results
250+
in severe performance problems.
251+
252+
The arguments ``batch_size``, ``scan_batching`` and ``sorted_columns``
253+
are allowed (as keyword arguments) for compatibility with
254+
HappyBase. However, they will not be used in any way, and will cause a
255+
warning if passed. (The ``batch_size`` determines the number of
256+
results to retrieve per request. The HBase scanner defaults to reading
257+
one record at a time, so this argument allows HappyBase to increase
258+
that number. However, the Cloud Bigtable API uses HTTP/2 streaming so
259+
there is no concept of a batch. The ``sorted_columns`` flag tells
260+
HBase to return columns in order, but Cloud Bigtable doesn't have
261+
this feature.)
262+
263+
:type row_start: str
264+
:param row_start: (Optional) Row key where the scanner should start
265+
(includes ``row_start``). If not specified, reads
266+
from the first key. If the table does not contain
267+
``row_start``, it will start from the next key after
268+
it that **is** contained in the table.
269+
270+
:type row_stop: str
271+
:param row_stop: (Optional) Row key where the scanner should stop
272+
(excludes ``row_stop``). If not specified, reads
273+
until the last key. The table does not have to contain
274+
``row_stop``.
275+
276+
:type row_prefix: str
277+
:param row_prefix: (Optional) Prefix to match row keys.
278+
279+
:type columns: list
280+
:param columns: (Optional) Iterable containing column names (as
281+
strings). Each column name can be either
282+
283+
* an entire column family: ``fam`` or ``fam:``
284+
* an single column: ``fam:col``
285+
286+
:type filter: :class:`.RowFilter`
287+
:param filter: (Optional) An additional filter (beyond column and
288+
row range filters supported here). HappyBase / HBase
289+
users will have used this as an HBase filter string. See
290+
http://hbase.apache.org/0.94/book/thrift.html
291+
for more details on those filters.
292+
293+
:type timestamp: int
294+
:param timestamp: (Optional) Timestamp (in milliseconds since the
295+
epoch). If specified, only cells returned before (or
296+
at) the timestamp will be returned.
297+
298+
:type include_timestamp: bool
299+
:param include_timestamp: Flag to indicate if cell timestamps should be
300+
included with the output.
301+
302+
:type limit: int
303+
:param limit: (Optional) Maximum number of rows to return.
304+
305+
:type kwargs: dict
306+
:param kwargs: Remaining keyword arguments. Provided for HappyBase
307+
compatibility.
308+
309+
:raises: :class:`ValueError <exceptions.ValueError>` if ``batch_size``
310+
or ``scan_batching`` are used, or if ``limit`` is set but
311+
non-positive, or if row prefix is used with row start/stop,
312+
:class:`TypeError <exceptions.TypeError>` if a string
313+
``filter`` is used,
314+
:class:`NotImplementedError <exceptions.NotImplementedError>`
315+
always (until the method is implemented).
316+
"""
317+
raise NotImplementedError
318+
319+
def put(self, row, data, timestamp=None, wal=_WAL_SENTINEL):
320+
"""Insert data into a row in this table.
321+
322+
.. note::
323+
324+
This method will send a request with a single "put" mutation.
325+
In many situations, :meth:`batch` is a more appropriate
326+
method to manipulate data since it helps combine many mutations
327+
into a single request.
328+
329+
:type row: str
330+
:param row: The row key where the mutation will be "put".
331+
332+
:type data: dict
333+
:param data: Dictionary containing the data to be inserted. The keys
334+
are columns names (of the form ``fam:col``) and the values
335+
are strings (bytes) to be stored in those columns.
336+
337+
:type timestamp: int
338+
:param timestamp: (Optional) Timestamp (in milliseconds since the
339+
epoch) that the mutation will be applied at.
340+
341+
:type wal: object
342+
:param wal: Unused parameter (to be passed to a created batch).
343+
Provided for compatibility with HappyBase, but irrelevant
344+
for Cloud Bigtable since it does not have a Write Ahead
345+
Log.
346+
347+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
348+
always (until the method is implemented).
349+
"""
350+
raise NotImplementedError
351+
352+
def delete(self, row, columns=None, timestamp=None, wal=_WAL_SENTINEL):
353+
"""Delete data from a row in this table.
354+
355+
This method deletes the entire ``row`` if ``columns`` is not
356+
specified.
357+
358+
.. note::
359+
360+
This method will send a request with a single delete mutation.
361+
In many situations, :meth:`batch` is a more appropriate
362+
method to manipulate data since it helps combine many mutations
363+
into a single request.
364+
365+
:type row: str
366+
:param row: The row key where the delete will occur.
367+
368+
:type columns: list
369+
:param columns: (Optional) Iterable containing column names (as
370+
strings). Each column name can be either
371+
372+
* an entire column family: ``fam`` or ``fam:``
373+
* an single column: ``fam:col``
374+
375+
:type timestamp: int
376+
:param timestamp: (Optional) Timestamp (in milliseconds since the
377+
epoch) that the mutation will be applied at.
378+
379+
:type wal: object
380+
:param wal: Unused parameter (to be passed to a created batch).
381+
Provided for compatibility with HappyBase, but irrelevant
382+
for Cloud Bigtable since it does not have a Write Ahead
383+
Log.
384+
385+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
386+
always (until the method is implemented).
387+
"""
388+
raise NotImplementedError
389+
390+
def batch(self, timestamp=None, batch_size=None, transaction=False,
391+
wal=_WAL_SENTINEL):
392+
"""Create a new batch operation for this table.
393+
394+
This method returns a new :class:`.Batch` instance that can be used
395+
for mass data manipulation.
396+
397+
:type timestamp: int
398+
:param timestamp: (Optional) Timestamp (in milliseconds since the
399+
epoch) that all mutations will be applied at.
400+
401+
:type batch_size: int
402+
:param batch_size: (Optional) The maximum number of mutations to allow
403+
to accumulate before committing them.
404+
405+
:type transaction: bool
406+
:param transaction: Flag indicating if the mutations should be sent
407+
transactionally or not. If ``transaction=True`` and
408+
an error occurs while a :class:`Batch` is active,
409+
then none of the accumulated mutations will be
410+
committed. If ``batch_size`` is set, the mutation
411+
can't be transactional.
412+
413+
:type wal: object
414+
:param wal: Unused parameter (to be passed to the created batch).
415+
Provided for compatibility with HappyBase, but irrelevant
416+
for Cloud Bigtable since it does not have a Write Ahead
417+
Log.
418+
419+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
420+
always (until the method is implemented).
421+
"""
422+
raise NotImplementedError
423+
133424
def counter_get(self, row, column):
134425
"""Retrieve the current value of a counter column.
135426

gcloud/bigtable/happybase/test_table.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,62 @@ def test_regions(self):
121121
with self.assertRaises(NotImplementedError):
122122
table.regions()
123123

124+
def test_row(self):
125+
name = 'table-name'
126+
connection = None
127+
table = self._makeOne(name, connection)
128+
129+
with self.assertRaises(NotImplementedError):
130+
table.row(None)
131+
132+
def test_rows(self):
133+
name = 'table-name'
134+
connection = None
135+
table = self._makeOne(name, connection)
136+
137+
with self.assertRaises(NotImplementedError):
138+
table.rows(None)
139+
140+
def test_cells(self):
141+
name = 'table-name'
142+
connection = None
143+
table = self._makeOne(name, connection)
144+
145+
with self.assertRaises(NotImplementedError):
146+
table.cells(None, None)
147+
148+
def test_scan(self):
149+
name = 'table-name'
150+
connection = None
151+
table = self._makeOne(name, connection)
152+
153+
with self.assertRaises(NotImplementedError):
154+
table.scan()
155+
156+
def test_put(self):
157+
name = 'table-name'
158+
connection = None
159+
table = self._makeOne(name, connection)
160+
161+
with self.assertRaises(NotImplementedError):
162+
table.put(None, None)
163+
164+
def test_delete(self):
165+
name = 'table-name'
166+
connection = None
167+
table = self._makeOne(name, connection)
168+
169+
with self.assertRaises(NotImplementedError):
170+
table.delete(None)
171+
172+
def test_batch(self):
173+
name = 'table-name'
174+
connection = None
175+
table = self._makeOne(name, connection)
176+
177+
with self.assertRaises(NotImplementedError):
178+
table.batch()
179+
124180
def test_counter_get(self):
125181
klass = self._getTargetClass()
126182
counter_value = 1337

0 commit comments

Comments
 (0)