google-cloud-bigtable 2.30.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. google/cloud/bigtable/__init__.py +25 -0
  2. google/cloud/bigtable/app_profile.py +377 -0
  3. google/cloud/bigtable/backup.py +490 -0
  4. google/cloud/bigtable/batcher.py +414 -0
  5. google/cloud/bigtable/client.py +475 -0
  6. google/cloud/bigtable/cluster.py +541 -0
  7. google/cloud/bigtable/column_family.py +362 -0
  8. google/cloud/bigtable/data/__init__.py +103 -0
  9. google/cloud/bigtable/data/_async/__init__.py +25 -0
  10. google/cloud/bigtable/data/_async/_mutate_rows.py +226 -0
  11. google/cloud/bigtable/data/_async/_read_rows.py +363 -0
  12. google/cloud/bigtable/data/_async/client.py +1522 -0
  13. google/cloud/bigtable/data/_async/mutations_batcher.py +534 -0
  14. google/cloud/bigtable/data/_cross_sync/__init__.py +20 -0
  15. google/cloud/bigtable/data/_cross_sync/_decorators.py +441 -0
  16. google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +64 -0
  17. google/cloud/bigtable/data/_cross_sync/cross_sync.py +334 -0
  18. google/cloud/bigtable/data/_helpers.py +249 -0
  19. google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +182 -0
  20. google/cloud/bigtable/data/_sync_autogen/_read_rows.py +304 -0
  21. google/cloud/bigtable/data/_sync_autogen/client.py +1293 -0
  22. google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +449 -0
  23. google/cloud/bigtable/data/exceptions.py +340 -0
  24. google/cloud/bigtable/data/execute_query/__init__.py +43 -0
  25. google/cloud/bigtable/data/execute_query/_async/__init__.py +13 -0
  26. google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +291 -0
  27. google/cloud/bigtable/data/execute_query/_byte_cursor.py +123 -0
  28. google/cloud/bigtable/data/execute_query/_checksum.py +43 -0
  29. google/cloud/bigtable/data/execute_query/_parameters_formatting.py +155 -0
  30. google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +134 -0
  31. google/cloud/bigtable/data/execute_query/_reader.py +122 -0
  32. google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +239 -0
  33. google/cloud/bigtable/data/execute_query/metadata.py +399 -0
  34. google/cloud/bigtable/data/execute_query/values.py +123 -0
  35. google/cloud/bigtable/data/mutations.py +380 -0
  36. google/cloud/bigtable/data/read_modify_write_rules.py +112 -0
  37. google/cloud/bigtable/data/read_rows_query.py +536 -0
  38. google/cloud/bigtable/data/row.py +535 -0
  39. google/cloud/bigtable/data/row_filters.py +968 -0
  40. google/cloud/bigtable/encryption_info.py +64 -0
  41. google/cloud/bigtable/enums.py +223 -0
  42. google/cloud/bigtable/error.py +64 -0
  43. google/cloud/bigtable/gapic_version.py +16 -0
  44. google/cloud/bigtable/helpers.py +31 -0
  45. google/cloud/bigtable/instance.py +789 -0
  46. google/cloud/bigtable/policy.py +255 -0
  47. google/cloud/bigtable/row.py +1267 -0
  48. google/cloud/bigtable/row_data.py +380 -0
  49. google/cloud/bigtable/row_filters.py +838 -0
  50. google/cloud/bigtable/row_merger.py +250 -0
  51. google/cloud/bigtable/row_set.py +213 -0
  52. google/cloud/bigtable/table.py +1409 -0
  53. google/cloud/bigtable_admin/__init__.py +410 -0
  54. google/cloud/bigtable_admin/gapic_version.py +16 -0
  55. google/cloud/bigtable_admin/py.typed +2 -0
  56. google/cloud/bigtable_admin_v2/__init__.py +250 -0
  57. google/cloud/bigtable_admin_v2/gapic_metadata.json +962 -0
  58. google/cloud/bigtable_admin_v2/gapic_version.py +16 -0
  59. google/cloud/bigtable_admin_v2/py.typed +2 -0
  60. google/cloud/bigtable_admin_v2/services/__init__.py +15 -0
  61. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +22 -0
  62. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +3465 -0
  63. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +3944 -0
  64. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +681 -0
  65. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +40 -0
  66. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +751 -0
  67. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +1249 -0
  68. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +1579 -0
  69. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +6820 -0
  70. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +1746 -0
  71. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +22 -0
  72. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +3472 -0
  73. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +3949 -0
  74. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +669 -0
  75. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +40 -0
  76. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +708 -0
  77. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +1257 -0
  78. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +1549 -0
  79. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +6609 -0
  80. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +1714 -0
  81. google/cloud/bigtable_admin_v2/types/__init__.py +248 -0
  82. google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +1364 -0
  83. google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +1715 -0
  84. google/cloud/bigtable_admin_v2/types/common.py +81 -0
  85. google/cloud/bigtable_admin_v2/types/instance.py +819 -0
  86. google/cloud/bigtable_admin_v2/types/table.py +1028 -0
  87. google/cloud/bigtable_admin_v2/types/types.py +776 -0
  88. google/cloud/bigtable_v2/__init__.py +136 -0
  89. google/cloud/bigtable_v2/gapic_metadata.json +193 -0
  90. google/cloud/bigtable_v2/gapic_version.py +16 -0
  91. google/cloud/bigtable_v2/py.typed +2 -0
  92. google/cloud/bigtable_v2/services/__init__.py +15 -0
  93. google/cloud/bigtable_v2/services/bigtable/__init__.py +22 -0
  94. google/cloud/bigtable_v2/services/bigtable/async_client.py +1741 -0
  95. google/cloud/bigtable_v2/services/bigtable/client.py +2194 -0
  96. google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +38 -0
  97. google/cloud/bigtable_v2/services/bigtable/transports/base.py +343 -0
  98. google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +660 -0
  99. google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +762 -0
  100. google/cloud/bigtable_v2/services/bigtable/transports/rest.py +2489 -0
  101. google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +711 -0
  102. google/cloud/bigtable_v2/types/__init__.py +138 -0
  103. google/cloud/bigtable_v2/types/bigtable.py +1531 -0
  104. google/cloud/bigtable_v2/types/data.py +1612 -0
  105. google/cloud/bigtable_v2/types/feature_flags.py +119 -0
  106. google/cloud/bigtable_v2/types/request_stats.py +171 -0
  107. google/cloud/bigtable_v2/types/response_params.py +64 -0
  108. google/cloud/bigtable_v2/types/types.py +579 -0
  109. google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_admin_v2_keywords.py +233 -0
  110. google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_v2_keywords.py +186 -0
  111. google_cloud_bigtable-2.30.0.dist-info/LICENSE +202 -0
  112. google_cloud_bigtable-2.30.0.dist-info/METADATA +154 -0
  113. google_cloud_bigtable-2.30.0.dist-info/RECORD +115 -0
  114. google_cloud_bigtable-2.30.0.dist-info/WHEEL +5 -0
  115. google_cloud_bigtable-2.30.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1267 @@
1
+ # Copyright 2015 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """User-friendly container for Google Cloud Bigtable Row."""
16
+
17
+
18
+ import struct
19
+
20
+ from google.cloud._helpers import _datetime_from_microseconds # type: ignore
21
+ from google.cloud._helpers import _microseconds_from_datetime # type: ignore
22
+ from google.cloud._helpers import _to_bytes # type: ignore
23
+ from google.cloud.bigtable_v2.types import data as data_v2_pb2
24
+
25
+
26
+ _PACK_I64 = struct.Struct(">q").pack
27
+
28
+ MAX_MUTATIONS = 100000
29
+ """The maximum number of mutations that a row can accumulate."""
30
+
31
+ _MISSING_COLUMN_FAMILY = "Column family {} is not among the cells stored in this row."
32
+ _MISSING_COLUMN = (
33
+ "Column {} is not among the cells stored in this row in the column family {}."
34
+ )
35
+ _MISSING_INDEX = (
36
+ "Index {!r} is not valid for the cells stored in this row for column {} "
37
+ "in the column family {}. There are {} such cells."
38
+ )
39
+
40
+
41
+ class Row(object):
42
+ """Base representation of a Google Cloud Bigtable Row.
43
+
44
+ This class has three subclasses corresponding to the three
45
+ RPC methods for sending row mutations:
46
+
47
+ * :class:`DirectRow` for ``MutateRow``
48
+ * :class:`ConditionalRow` for ``CheckAndMutateRow``
49
+ * :class:`AppendRow` for ``ReadModifyWriteRow``
50
+
51
+ :type row_key: bytes
52
+ :param row_key: The key for the current row.
53
+
54
+ :type table: :class:`Table <google.cloud.bigtable.table.Table>`
55
+ :param table: (Optional) The table that owns the row.
56
+ """
57
+
58
+ def __init__(self, row_key, table=None):
59
+ self._row_key = _to_bytes(row_key)
60
+ self._table = table
61
+
62
+ @property
63
+ def row_key(self):
64
+ """Row key.
65
+
66
+ For example:
67
+
68
+ .. literalinclude:: snippets_table.py
69
+ :start-after: [START bigtable_api_row_row_key]
70
+ :end-before: [END bigtable_api_row_row_key]
71
+ :dedent: 4
72
+
73
+ :rtype: bytes
74
+ :returns: The key for the current row.
75
+ """
76
+ return self._row_key
77
+
78
+ @property
79
+ def table(self):
80
+ """Row table.
81
+
82
+ For example:
83
+
84
+ .. literalinclude:: snippets_table.py
85
+ :start-after: [START bigtable_api_row_table]
86
+ :end-before: [END bigtable_api_row_table]
87
+ :dedent: 4
88
+
89
+ :rtype: table: :class:`Table <google.cloud.bigtable.table.Table>`
90
+ :returns: table: The table that owns the row.
91
+ """
92
+ return self._table
93
+
94
+
95
+ class _SetDeleteRow(Row):
96
+ """Row helper for setting or deleting cell values.
97
+
98
+ Implements helper methods to add mutations to set or delete cell contents:
99
+
100
+ * :meth:`set_cell`
101
+ * :meth:`delete`
102
+ * :meth:`delete_cell`
103
+ * :meth:`delete_cells`
104
+
105
+ :type row_key: bytes
106
+ :param row_key: The key for the current row.
107
+
108
+ :type table: :class:`Table <google.cloud.bigtable.table.Table>`
109
+ :param table: The table that owns the row.
110
+ """
111
+
112
+ ALL_COLUMNS = object()
113
+ """Sentinel value used to indicate all columns in a column family."""
114
+
115
+ def _get_mutations(self, state=None):
116
+ """Gets the list of mutations for a given state.
117
+
118
+ This method intended to be implemented by subclasses.
119
+
120
+ ``state`` may not need to be used by all subclasses.
121
+
122
+ :type state: bool
123
+ :param state: The state that the mutation should be
124
+ applied in.
125
+
126
+ :raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
127
+ always.
128
+ """
129
+ raise NotImplementedError
130
+
131
+ def _set_cell(self, column_family_id, column, value, timestamp=None, state=None):
132
+ """Helper for :meth:`set_cell`
133
+
134
+ Adds a mutation to set the value in a specific cell.
135
+
136
+ ``state`` is unused by :class:`DirectRow` but is used by
137
+ subclasses.
138
+
139
+ :type column_family_id: str
140
+ :param column_family_id: The column family that contains the column.
141
+ Must be of the form
142
+ ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
143
+
144
+ :type column: bytes
145
+ :param column: The column within the column family where the cell
146
+ is located.
147
+
148
+ :type value: bytes or :class:`int`
149
+ :param value: The value to set in the cell. If an integer is used,
150
+ will be interpreted as a 64-bit big-endian signed
151
+ integer (8 bytes).
152
+
153
+ :type timestamp: :class:`datetime.datetime`
154
+ :param timestamp: (Optional) The timestamp of the operation.
155
+
156
+ :type state: bool
157
+ :param state: (Optional) The state that is passed along to
158
+ :meth:`_get_mutations`.
159
+ """
160
+ column = _to_bytes(column)
161
+ if isinstance(value, int):
162
+ value = _PACK_I64(value)
163
+ value = _to_bytes(value)
164
+ if timestamp is None:
165
+ # Use -1 for current Bigtable server time.
166
+ timestamp_micros = -1
167
+ else:
168
+ timestamp_micros = _microseconds_from_datetime(timestamp)
169
+ # Truncate to millisecond granularity.
170
+ timestamp_micros -= timestamp_micros % 1000
171
+
172
+ mutation_val = data_v2_pb2.Mutation.SetCell(
173
+ family_name=column_family_id,
174
+ column_qualifier=column,
175
+ timestamp_micros=timestamp_micros,
176
+ value=value,
177
+ )
178
+ mutation_pb = data_v2_pb2.Mutation(set_cell=mutation_val)
179
+ self._get_mutations(state).append(mutation_pb)
180
+
181
+ def _delete(self, state=None):
182
+ """Helper for :meth:`delete`
183
+
184
+ Adds a delete mutation (for the entire row) to the accumulated
185
+ mutations.
186
+
187
+ ``state`` is unused by :class:`DirectRow` but is used by
188
+ subclasses.
189
+
190
+ :type state: bool
191
+ :param state: (Optional) The state that is passed along to
192
+ :meth:`_get_mutations`.
193
+ """
194
+ mutation_val = data_v2_pb2.Mutation.DeleteFromRow()
195
+ mutation_pb = data_v2_pb2.Mutation(delete_from_row=mutation_val)
196
+ self._get_mutations(state).append(mutation_pb)
197
+
198
+ def _delete_cells(self, column_family_id, columns, time_range=None, state=None):
199
+ """Helper for :meth:`delete_cell` and :meth:`delete_cells`.
200
+
201
+ ``state`` is unused by :class:`DirectRow` but is used by
202
+ subclasses.
203
+
204
+ :type column_family_id: str
205
+ :param column_family_id: The column family that contains the column
206
+ or columns with cells being deleted. Must be
207
+ of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
208
+
209
+ :type columns: :class:`list` of :class:`str` /
210
+ :func:`unicode <unicode>`, or :class:`object`
211
+ :param columns: The columns within the column family that will have
212
+ cells deleted. If :attr:`ALL_COLUMNS` is used then
213
+ the entire column family will be deleted from the row.
214
+
215
+ :type time_range: :class:`TimestampRange`
216
+ :param time_range: (Optional) The range of time within which cells
217
+ should be deleted.
218
+
219
+ :type state: bool
220
+ :param state: (Optional) The state that is passed along to
221
+ :meth:`_get_mutations`.
222
+ """
223
+ mutations_list = self._get_mutations(state)
224
+ if columns is self.ALL_COLUMNS:
225
+ mutation_val = data_v2_pb2.Mutation.DeleteFromFamily(
226
+ family_name=column_family_id
227
+ )
228
+ mutation_pb = data_v2_pb2.Mutation(delete_from_family=mutation_val)
229
+ mutations_list.append(mutation_pb)
230
+ else:
231
+ delete_kwargs = {}
232
+ if time_range is not None:
233
+ delete_kwargs["time_range"] = time_range.to_pb()
234
+
235
+ to_append = []
236
+ for column in columns:
237
+ column = _to_bytes(column)
238
+ # time_range will never change if present, but the rest of
239
+ # delete_kwargs will
240
+ delete_kwargs.update(
241
+ family_name=column_family_id, column_qualifier=column
242
+ )
243
+ mutation_val = data_v2_pb2.Mutation.DeleteFromColumn(**delete_kwargs)
244
+ mutation_pb = data_v2_pb2.Mutation(delete_from_column=mutation_val)
245
+ to_append.append(mutation_pb)
246
+
247
+ # We don't add the mutations until all columns have been
248
+ # processed without error.
249
+ mutations_list.extend(to_append)
250
+
251
+
252
+ class DirectRow(_SetDeleteRow):
253
+ """Google Cloud Bigtable Row for sending "direct" mutations.
254
+
255
+ These mutations directly set or delete cell contents:
256
+
257
+ * :meth:`set_cell`
258
+ * :meth:`delete`
259
+ * :meth:`delete_cell`
260
+ * :meth:`delete_cells`
261
+
262
+ These methods can be used directly::
263
+
264
+ >>> row = table.row(b'row-key1')
265
+ >>> row.set_cell(u'fam', b'col1', b'cell-val')
266
+ >>> row.delete_cell(u'fam', b'col2')
267
+
268
+ .. note::
269
+
270
+ A :class:`DirectRow` accumulates mutations locally via the
271
+ :meth:`set_cell`, :meth:`delete`, :meth:`delete_cell` and
272
+ :meth:`delete_cells` methods. To actually send these mutations to the
273
+ Google Cloud Bigtable API, you must call :meth:`commit`.
274
+
275
+ :type row_key: bytes
276
+ :param row_key: The key for the current row.
277
+
278
+ :type table: :class:`Table <google.cloud.bigtable.table.Table>`
279
+ :param table: (Optional) The table that owns the row. This is
280
+ used for the :meth: `commit` only. Alternatively,
281
+ DirectRows can be persisted via
282
+ :meth:`~google.cloud.bigtable.table.Table.mutate_rows`.
283
+ """
284
+
285
+ def __init__(self, row_key, table=None):
286
+ super(DirectRow, self).__init__(row_key, table)
287
+ self._pb_mutations = []
288
+
289
+ def _get_mutations(self, state=None): # pylint: disable=unused-argument
290
+ """Gets the list of mutations for a given state.
291
+
292
+ ``state`` is unused by :class:`DirectRow` but is used by
293
+ subclasses.
294
+
295
+ :type state: bool
296
+ :param state: The state that the mutation should be
297
+ applied in.
298
+
299
+ :rtype: list
300
+ :returns: The list to add new mutations to (for the current state).
301
+ """
302
+ return self._pb_mutations
303
+
304
+ def get_mutations_size(self):
305
+ """Gets the total mutations size for current row
306
+
307
+ For example:
308
+
309
+ .. literalinclude:: snippets_table.py
310
+ :start-after: [START bigtable_api_row_get_mutations_size]
311
+ :end-before: [END bigtable_api_row_get_mutations_size]
312
+ :dedent: 4
313
+ """
314
+
315
+ mutation_size = 0
316
+ for mutation in self._get_mutations():
317
+ mutation_size += mutation._pb.ByteSize()
318
+
319
+ return mutation_size
320
+
321
+ def set_cell(self, column_family_id, column, value, timestamp=None):
322
+ """Sets a value in this row.
323
+
324
+ The cell is determined by the ``row_key`` of this :class:`DirectRow`
325
+ and the ``column``. The ``column`` must be in an existing
326
+ :class:`.ColumnFamily` (as determined by ``column_family_id``).
327
+
328
+ .. note::
329
+
330
+ This method adds a mutation to the accumulated mutations on this
331
+ row, but does not make an API request. To actually
332
+ send an API request (with the mutations) to the Google Cloud
333
+ Bigtable API, call :meth:`commit`.
334
+
335
+ For example:
336
+
337
+ .. literalinclude:: snippets_table.py
338
+ :start-after: [START bigtable_api_row_set_cell]
339
+ :end-before: [END bigtable_api_row_set_cell]
340
+ :dedent: 4
341
+
342
+ :type column_family_id: str
343
+ :param column_family_id: The column family that contains the column.
344
+ Must be of the form
345
+ ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
346
+
347
+ :type column: bytes
348
+ :param column: The column within the column family where the cell
349
+ is located.
350
+
351
+ :type value: bytes or :class:`int`
352
+ :param value: The value to set in the cell. If an integer is used,
353
+ will be interpreted as a 64-bit big-endian signed
354
+ integer (8 bytes).
355
+
356
+ :type timestamp: :class:`datetime.datetime`
357
+ :param timestamp: (Optional) The timestamp of the operation.
358
+ """
359
+ self._set_cell(column_family_id, column, value, timestamp=timestamp, state=None)
360
+
361
+ def delete(self):
362
+ """Deletes this row from the table.
363
+
364
+ .. note::
365
+
366
+ This method adds a mutation to the accumulated mutations on this
367
+ row, but does not make an API request. To actually
368
+ send an API request (with the mutations) to the Google Cloud
369
+ Bigtable API, call :meth:`commit`.
370
+
371
+ For example:
372
+
373
+ .. literalinclude:: snippets_table.py
374
+ :start-after: [START bigtable_api_row_delete]
375
+ :end-before: [END bigtable_api_row_delete]
376
+ :dedent: 4
377
+ """
378
+ self._delete(state=None)
379
+
380
+ def delete_cell(self, column_family_id, column, time_range=None):
381
+ """Deletes cell in this row.
382
+
383
+ .. note::
384
+
385
+ This method adds a mutation to the accumulated mutations on this
386
+ row, but does not make an API request. To actually
387
+ send an API request (with the mutations) to the Google Cloud
388
+ Bigtable API, call :meth:`commit`.
389
+
390
+ For example:
391
+
392
+ .. literalinclude:: snippets_table.py
393
+ :start-after: [START bigtable_api_row_delete_cell]
394
+ :end-before: [END bigtable_api_row_delete_cell]
395
+ :dedent: 4
396
+
397
+ :type column_family_id: str
398
+ :param column_family_id: The column family that contains the column
399
+ or columns with cells being deleted. Must be
400
+ of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
401
+
402
+ :type column: bytes
403
+ :param column: The column within the column family that will have a
404
+ cell deleted.
405
+
406
+ :type time_range: :class:`TimestampRange`
407
+ :param time_range: (Optional) The range of time within which cells
408
+ should be deleted.
409
+ """
410
+ self._delete_cells(
411
+ column_family_id, [column], time_range=time_range, state=None
412
+ )
413
+
414
+ def delete_cells(self, column_family_id, columns, time_range=None):
415
+ """Deletes cells in this row.
416
+
417
+ .. note::
418
+
419
+ This method adds a mutation to the accumulated mutations on this
420
+ row, but does not make an API request. To actually
421
+ send an API request (with the mutations) to the Google Cloud
422
+ Bigtable API, call :meth:`commit`.
423
+
424
+ For example:
425
+
426
+ .. literalinclude:: snippets_table.py
427
+ :start-after: [START bigtable_api_row_delete_cells]
428
+ :end-before: [END bigtable_api_row_delete_cells]
429
+ :dedent: 4
430
+
431
+ :type column_family_id: str
432
+ :param column_family_id: The column family that contains the column
433
+ or columns with cells being deleted. Must be
434
+ of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
435
+
436
+ :type columns: :class:`list` of :class:`str` /
437
+ :func:`unicode <unicode>`, or :class:`object`
438
+ :param columns: The columns within the column family that will have
439
+ cells deleted. If :attr:`ALL_COLUMNS` is used then
440
+ the entire column family will be deleted from the row.
441
+
442
+ :type time_range: :class:`TimestampRange`
443
+ :param time_range: (Optional) The range of time within which cells
444
+ should be deleted.
445
+ """
446
+ self._delete_cells(column_family_id, columns, time_range=time_range, state=None)
447
+
448
+ def commit(self):
449
+ """Makes a ``MutateRow`` API request.
450
+
451
+ If no mutations have been created in the row, no request is made.
452
+
453
+ Mutations are applied atomically and in order, meaning that earlier
454
+ mutations can be masked / negated by later ones. Cells already present
455
+ in the row are left unchanged unless explicitly changed by a mutation.
456
+
457
+ After committing the accumulated mutations, resets the local
458
+ mutations to an empty list.
459
+
460
+ For example:
461
+
462
+ .. literalinclude:: snippets_table.py
463
+ :start-after: [START bigtable_api_row_commit]
464
+ :end-before: [END bigtable_api_row_commit]
465
+ :dedent: 4
466
+
467
+ :rtype: :class:`~google.rpc.status_pb2.Status`
468
+ :returns: A response status (`google.rpc.status_pb2.Status`)
469
+ representing success or failure of the row committed.
470
+ :raises: :exc:`~.table.TooManyMutationsError` if the number of
471
+ mutations is greater than 100,000.
472
+ """
473
+ response = self._table.mutate_rows([self])
474
+
475
+ self.clear()
476
+
477
+ return response[0]
478
+
479
+ def clear(self):
480
+ """Removes all currently accumulated mutations on the current row.
481
+
482
+ For example:
483
+
484
+ .. literalinclude:: snippets_table.py
485
+ :start-after: [START bigtable_api_row_clear]
486
+ :end-before: [END bigtable_api_row_clear]
487
+ :dedent: 4
488
+ """
489
+ del self._pb_mutations[:]
490
+
491
+
492
+ class ConditionalRow(_SetDeleteRow):
493
+ """Google Cloud Bigtable Row for sending mutations conditionally.
494
+
495
+ Each mutation has an associated state: :data:`True` or :data:`False`.
496
+ When :meth:`commit`-ed, the mutations for the :data:`True`
497
+ state will be applied if the filter matches any cells in
498
+ the row, otherwise the :data:`False` state will be applied.
499
+
500
+ A :class:`ConditionalRow` accumulates mutations in the same way a
501
+ :class:`DirectRow` does:
502
+
503
+ * :meth:`set_cell`
504
+ * :meth:`delete`
505
+ * :meth:`delete_cell`
506
+ * :meth:`delete_cells`
507
+
508
+ with the only change the extra ``state`` parameter::
509
+
510
+ >>> row_cond = table.row(b'row-key2', filter_=row_filter)
511
+ >>> row_cond.set_cell(u'fam', b'col', b'cell-val', state=True)
512
+ >>> row_cond.delete_cell(u'fam', b'col', state=False)
513
+
514
+ .. note::
515
+
516
+ As with :class:`DirectRow`, to actually send these mutations to the
517
+ Google Cloud Bigtable API, you must call :meth:`commit`.
518
+
519
+ :type row_key: bytes
520
+ :param row_key: The key for the current row.
521
+
522
+ :type table: :class:`Table <google.cloud.bigtable.table.Table>`
523
+ :param table: The table that owns the row.
524
+
525
+ :type filter_: :class:`.RowFilter`
526
+ :param filter_: Filter to be used for conditional mutations.
527
+ """
528
+
529
+ def __init__(self, row_key, table, filter_):
530
+ super(ConditionalRow, self).__init__(row_key, table)
531
+ self._filter = filter_
532
+ self._true_pb_mutations = []
533
+ self._false_pb_mutations = []
534
+
535
+ def _get_mutations(self, state=None):
536
+ """Gets the list of mutations for a given state.
537
+
538
+ Over-ridden so that the state can be used in:
539
+
540
+ * :meth:`set_cell`
541
+ * :meth:`delete`
542
+ * :meth:`delete_cell`
543
+ * :meth:`delete_cells`
544
+
545
+ :type state: bool
546
+ :param state: The state that the mutation should be
547
+ applied in.
548
+
549
+ :rtype: list
550
+ :returns: The list to add new mutations to (for the current state).
551
+ """
552
+ if state:
553
+ return self._true_pb_mutations
554
+ else:
555
+ return self._false_pb_mutations
556
+
557
+ def commit(self):
558
+ """Makes a ``CheckAndMutateRow`` API request.
559
+
560
+ If no mutations have been created in the row, no request is made.
561
+
562
+ The mutations will be applied conditionally, based on whether the
563
+ filter matches any cells in the :class:`ConditionalRow` or not. (Each
564
+ method which adds a mutation has a ``state`` parameter for this
565
+ purpose.)
566
+
567
+ Mutations are applied atomically and in order, meaning that earlier
568
+ mutations can be masked / negated by later ones. Cells already present
569
+ in the row are left unchanged unless explicitly changed by a mutation.
570
+
571
+ After committing the accumulated mutations, resets the local
572
+ mutations.
573
+
574
+ For example:
575
+
576
+ .. literalinclude:: snippets_table.py
577
+ :start-after: [START bigtable_api_row_commit]
578
+ :end-before: [END bigtable_api_row_commit]
579
+ :dedent: 4
580
+
581
+ :rtype: bool
582
+ :returns: Flag indicating if the filter was matched (which also
583
+ indicates which set of mutations were applied by the server).
584
+ :raises: :class:`ValueError <exceptions.ValueError>` if the number of
585
+ mutations exceeds the :data:`MAX_MUTATIONS`.
586
+ """
587
+ true_mutations = self._get_mutations(state=True)
588
+ false_mutations = self._get_mutations(state=False)
589
+ num_true_mutations = len(true_mutations)
590
+ num_false_mutations = len(false_mutations)
591
+ if num_true_mutations == 0 and num_false_mutations == 0:
592
+ return
593
+ if num_true_mutations > MAX_MUTATIONS or num_false_mutations > MAX_MUTATIONS:
594
+ raise ValueError(
595
+ "Exceed the maximum allowable mutations (%d). Had %s true "
596
+ "mutations and %d false mutations."
597
+ % (MAX_MUTATIONS, num_true_mutations, num_false_mutations)
598
+ )
599
+
600
+ data_client = self._table._instance._client.table_data_client
601
+ resp = data_client.check_and_mutate_row(
602
+ table_name=self._table.name,
603
+ row_key=self._row_key,
604
+ predicate_filter=self._filter.to_pb(),
605
+ app_profile_id=self._table._app_profile_id,
606
+ true_mutations=true_mutations,
607
+ false_mutations=false_mutations,
608
+ )
609
+ self.clear()
610
+ return resp.predicate_matched
611
+
612
+ # pylint: disable=arguments-differ
613
+ def set_cell(self, column_family_id, column, value, timestamp=None, state=True):
614
+ """Sets a value in this row.
615
+
616
+ The cell is determined by the ``row_key`` of this
617
+ :class:`ConditionalRow` and the ``column``. The ``column`` must be in
618
+ an existing :class:`.ColumnFamily` (as determined by
619
+ ``column_family_id``).
620
+
621
+ .. note::
622
+
623
+ This method adds a mutation to the accumulated mutations on this
624
+ row, but does not make an API request. To actually
625
+ send an API request (with the mutations) to the Google Cloud
626
+ Bigtable API, call :meth:`commit`.
627
+
628
+ For example:
629
+
630
+ .. literalinclude:: snippets_table.py
631
+ :start-after: [START bigtable_api_row_set_cell]
632
+ :end-before: [END bigtable_api_row_set_cell]
633
+ :dedent: 4
634
+
635
+ :type column_family_id: str
636
+ :param column_family_id: The column family that contains the column.
637
+ Must be of the form
638
+ ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
639
+
640
+ :type column: bytes
641
+ :param column: The column within the column family where the cell
642
+ is located.
643
+
644
+ :type value: bytes or :class:`int`
645
+ :param value: The value to set in the cell. If an integer is used,
646
+ will be interpreted as a 64-bit big-endian signed
647
+ integer (8 bytes).
648
+
649
+ :type timestamp: :class:`datetime.datetime`
650
+ :param timestamp: (Optional) The timestamp of the operation.
651
+
652
+ :type state: bool
653
+ :param state: (Optional) The state that the mutation should be
654
+ applied in. Defaults to :data:`True`.
655
+ """
656
+ self._set_cell(
657
+ column_family_id, column, value, timestamp=timestamp, state=state
658
+ )
659
+
660
+ def delete(self, state=True):
661
+ """Deletes this row from the table.
662
+
663
+ .. note::
664
+
665
+ This method adds a mutation to the accumulated mutations on this
666
+ row, but does not make an API request. To actually
667
+ send an API request (with the mutations) to the Google Cloud
668
+ Bigtable API, call :meth:`commit`.
669
+
670
+ For example:
671
+
672
+ .. literalinclude:: snippets_table.py
673
+ :start-after: [START bigtable_api_row_delete]
674
+ :end-before: [END bigtable_api_row_delete]
675
+ :dedent: 4
676
+
677
+ :type state: bool
678
+ :param state: (Optional) The state that the mutation should be
679
+ applied in. Defaults to :data:`True`.
680
+ """
681
+ self._delete(state=state)
682
+
683
+ def delete_cell(self, column_family_id, column, time_range=None, state=True):
684
+ """Deletes cell in this row.
685
+
686
+ .. note::
687
+
688
+ This method adds a mutation to the accumulated mutations on this
689
+ row, but does not make an API request. To actually
690
+ send an API request (with the mutations) to the Google Cloud
691
+ Bigtable API, call :meth:`commit`.
692
+
693
+ For example:
694
+
695
+ .. literalinclude:: snippets_table.py
696
+ :start-after: [START bigtable_api_row_delete_cell]
697
+ :end-before: [END bigtable_api_row_delete_cell]
698
+ :dedent: 4
699
+
700
+ :type column_family_id: str
701
+ :param column_family_id: The column family that contains the column
702
+ or columns with cells being deleted. Must be
703
+ of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
704
+
705
+ :type column: bytes
706
+ :param column: The column within the column family that will have a
707
+ cell deleted.
708
+
709
+ :type time_range: :class:`TimestampRange`
710
+ :param time_range: (Optional) The range of time within which cells
711
+ should be deleted.
712
+
713
+ :type state: bool
714
+ :param state: (Optional) The state that the mutation should be
715
+ applied in. Defaults to :data:`True`.
716
+ """
717
+ self._delete_cells(
718
+ column_family_id, [column], time_range=time_range, state=state
719
+ )
720
+
721
+ def delete_cells(self, column_family_id, columns, time_range=None, state=True):
722
+ """Deletes cells in this row.
723
+
724
+ .. note::
725
+
726
+ This method adds a mutation to the accumulated mutations on this
727
+ row, but does not make an API request. To actually
728
+ send an API request (with the mutations) to the Google Cloud
729
+ Bigtable API, call :meth:`commit`.
730
+
731
+ For example:
732
+
733
+ .. literalinclude:: snippets_table.py
734
+ :start-after: [START bigtable_api_row_delete_cells]
735
+ :end-before: [END bigtable_api_row_delete_cells]
736
+ :dedent: 4
737
+
738
+ :type column_family_id: str
739
+ :param column_family_id: The column family that contains the column
740
+ or columns with cells being deleted. Must be
741
+ of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
742
+
743
+ :type columns: :class:`list` of :class:`str` /
744
+ :func:`unicode <unicode>`, or :class:`object`
745
+ :param columns: The columns within the column family that will have
746
+ cells deleted. If :attr:`ALL_COLUMNS` is used then the
747
+ entire column family will be deleted from the row.
748
+
749
+ :type time_range: :class:`TimestampRange`
750
+ :param time_range: (Optional) The range of time within which cells
751
+ should be deleted.
752
+
753
+ :type state: bool
754
+ :param state: (Optional) The state that the mutation should be
755
+ applied in. Defaults to :data:`True`.
756
+ """
757
+ self._delete_cells(
758
+ column_family_id, columns, time_range=time_range, state=state
759
+ )
760
+
761
+ # pylint: enable=arguments-differ
762
+
763
+ def clear(self):
764
+ """Removes all currently accumulated mutations on the current row.
765
+
766
+ For example:
767
+
768
+ .. literalinclude:: snippets_table.py
769
+ :start-after: [START bigtable_api_row_clear]
770
+ :end-before: [END bigtable_api_row_clear]
771
+ :dedent: 4
772
+ """
773
+ del self._true_pb_mutations[:]
774
+ del self._false_pb_mutations[:]
775
+
776
+
777
+ class AppendRow(Row):
778
+ """Google Cloud Bigtable Row for sending append mutations.
779
+
780
+ These mutations are intended to augment the value of an existing cell
781
+ and uses the methods:
782
+
783
+ * :meth:`append_cell_value`
784
+ * :meth:`increment_cell_value`
785
+
786
+ The first works by appending bytes and the second by incrementing an
787
+ integer (stored in the cell as 8 bytes). In either case, if the
788
+ cell is empty, assumes the default empty value (empty string for
789
+ bytes or 0 for integer).
790
+
791
+ :type row_key: bytes
792
+ :param row_key: The key for the current row.
793
+
794
+ :type table: :class:`Table <google.cloud.bigtable.table.Table>`
795
+ :param table: The table that owns the row.
796
+ """
797
+
798
+ def __init__(self, row_key, table):
799
+ super(AppendRow, self).__init__(row_key, table)
800
+ self._rule_pb_list = []
801
+
802
+ def clear(self):
803
+ """Removes all currently accumulated modifications on current row.
804
+
805
+ For example:
806
+
807
+ .. literalinclude:: snippets_table.py
808
+ :start-after: [START bigtable_api_row_clear]
809
+ :end-before: [END bigtable_api_row_clear]
810
+ :dedent: 4
811
+ """
812
+ del self._rule_pb_list[:]
813
+
814
+ def append_cell_value(self, column_family_id, column, value):
815
+ """Appends a value to an existing cell.
816
+
817
+ .. note::
818
+
819
+ This method adds a read-modify rule protobuf to the accumulated
820
+ read-modify rules on this row, but does not make an API
821
+ request. To actually send an API request (with the rules) to the
822
+ Google Cloud Bigtable API, call :meth:`commit`.
823
+
824
+ For example:
825
+
826
+ .. literalinclude:: snippets_table.py
827
+ :start-after: [START bigtable_api_row_append_cell_value]
828
+ :end-before: [END bigtable_api_row_append_cell_value]
829
+ :dedent: 4
830
+
831
+ :type column_family_id: str
832
+ :param column_family_id: The column family that contains the column.
833
+ Must be of the form
834
+ ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
835
+
836
+ :type column: bytes
837
+ :param column: The column within the column family where the cell
838
+ is located.
839
+
840
+ :type value: bytes
841
+ :param value: The value to append to the existing value in the cell. If
842
+ the targeted cell is unset, it will be treated as
843
+ containing the empty string.
844
+ """
845
+ column = _to_bytes(column)
846
+ value = _to_bytes(value)
847
+ rule_pb = data_v2_pb2.ReadModifyWriteRule(
848
+ family_name=column_family_id, column_qualifier=column, append_value=value
849
+ )
850
+ self._rule_pb_list.append(rule_pb)
851
+
852
+ def increment_cell_value(self, column_family_id, column, int_value):
853
+ """Increments a value in an existing cell.
854
+
855
+ Assumes the value in the cell is stored as a 64 bit integer
856
+ serialized to bytes.
857
+
858
+ .. note::
859
+
860
+ This method adds a read-modify rule protobuf to the accumulated
861
+ read-modify rules on this row, but does not make an API
862
+ request. To actually send an API request (with the rules) to the
863
+ Google Cloud Bigtable API, call :meth:`commit`.
864
+
865
+ For example:
866
+
867
+ .. literalinclude:: snippets_table.py
868
+ :start-after: [START bigtable_api_row_increment_cell_value]
869
+ :end-before: [END bigtable_api_row_increment_cell_value]
870
+ :dedent: 4
871
+
872
+ :type column_family_id: str
873
+ :param column_family_id: The column family that contains the column.
874
+ Must be of the form
875
+ ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
876
+
877
+ :type column: bytes
878
+ :param column: The column within the column family where the cell
879
+ is located.
880
+
881
+ :type int_value: int
882
+ :param int_value: The value to increment the existing value in the cell
883
+ by. If the targeted cell is unset, it will be treated
884
+ as containing a zero. Otherwise, the targeted cell
885
+ must contain an 8-byte value (interpreted as a 64-bit
886
+ big-endian signed integer), or the entire request
887
+ will fail.
888
+ """
889
+ column = _to_bytes(column)
890
+ rule_pb = data_v2_pb2.ReadModifyWriteRule(
891
+ family_name=column_family_id,
892
+ column_qualifier=column,
893
+ increment_amount=int_value,
894
+ )
895
+ self._rule_pb_list.append(rule_pb)
896
+
897
+ def commit(self):
898
+ """Makes a ``ReadModifyWriteRow`` API request.
899
+
900
+ This commits modifications made by :meth:`append_cell_value` and
901
+ :meth:`increment_cell_value`. If no modifications were made, makes
902
+ no API request and just returns ``{}``.
903
+
904
+ Modifies a row atomically, reading the latest existing
905
+ timestamp / value from the specified columns and writing a new value by
906
+ appending / incrementing. The new cell created uses either the current
907
+ server time or the highest timestamp of a cell in that column (if it
908
+ exceeds the server time).
909
+
910
+ After committing the accumulated mutations, resets the local mutations.
911
+
912
+ For example:
913
+
914
+ .. literalinclude:: snippets_table.py
915
+ :start-after: [START bigtable_api_row_commit]
916
+ :end-before: [END bigtable_api_row_commit]
917
+ :dedent: 4
918
+
919
+ :rtype: dict
920
+ :returns: The new contents of all modified cells. Returned as a
921
+ dictionary of column families, each of which holds a
922
+ dictionary of columns. Each column contains a list of cells
923
+ modified. Each cell is represented with a two-tuple with the
924
+ value (in bytes) and the timestamp for the cell.
925
+ :raises: :class:`ValueError <exceptions.ValueError>` if the number of
926
+ mutations exceeds the :data:`MAX_MUTATIONS`.
927
+ """
928
+ num_mutations = len(self._rule_pb_list)
929
+ if num_mutations == 0:
930
+ return {}
931
+ if num_mutations > MAX_MUTATIONS:
932
+ raise ValueError(
933
+ "%d total append mutations exceed the maximum "
934
+ "allowable %d." % (num_mutations, MAX_MUTATIONS)
935
+ )
936
+
937
+ data_client = self._table._instance._client.table_data_client
938
+ row_response = data_client.read_modify_write_row(
939
+ table_name=self._table.name,
940
+ row_key=self._row_key,
941
+ rules=self._rule_pb_list,
942
+ app_profile_id=self._table._app_profile_id,
943
+ )
944
+
945
+ # Reset modifications after commit-ing request.
946
+ self.clear()
947
+
948
+ # NOTE: We expect row_response.key == self._row_key but don't check.
949
+ return _parse_rmw_row_response(row_response)
950
+
951
+
952
+ def _parse_rmw_row_response(row_response):
953
+ """Parses the response to a ``ReadModifyWriteRow`` request.
954
+
955
+ :type row_response: :class:`.data_v2_pb2.Row`
956
+ :param row_response: The response row (with only modified cells) from a
957
+ ``ReadModifyWriteRow`` request.
958
+
959
+ :rtype: dict
960
+ :returns: The new contents of all modified cells. Returned as a
961
+ dictionary of column families, each of which holds a
962
+ dictionary of columns. Each column contains a list of cells
963
+ modified. Each cell is represented with a two-tuple with the
964
+ value (in bytes) and the timestamp for the cell. For example:
965
+
966
+ .. code:: python
967
+
968
+ {
969
+ u'col-fam-id': {
970
+ b'col-name1': [
971
+ (b'cell-val', datetime.datetime(...)),
972
+ (b'cell-val-newer', datetime.datetime(...)),
973
+ ],
974
+ b'col-name2': [
975
+ (b'altcol-cell-val', datetime.datetime(...)),
976
+ ],
977
+ },
978
+ u'col-fam-id2': {
979
+ b'col-name3-but-other-fam': [
980
+ (b'foo', datetime.datetime(...)),
981
+ ],
982
+ },
983
+ }
984
+ """
985
+ result = {}
986
+ for column_family in row_response.row.families:
987
+ column_family_id, curr_family = _parse_family_pb(column_family)
988
+ result[column_family_id] = curr_family
989
+ return result
990
+
991
+
992
+ def _parse_family_pb(family_pb):
993
+ """Parses a Family protobuf into a dictionary.
994
+
995
+ :type family_pb: :class:`._generated.data_pb2.Family`
996
+ :param family_pb: A protobuf
997
+
998
+ :rtype: tuple
999
+ :returns: A string and dictionary. The string is the name of the
1000
+ column family and the dictionary has column names (within the
1001
+ family) as keys and cell lists as values. Each cell is
1002
+ represented with a two-tuple with the value (in bytes) and the
1003
+ timestamp for the cell. For example:
1004
+
1005
+ .. code:: python
1006
+
1007
+ {
1008
+ b'col-name1': [
1009
+ (b'cell-val', datetime.datetime(...)),
1010
+ (b'cell-val-newer', datetime.datetime(...)),
1011
+ ],
1012
+ b'col-name2': [
1013
+ (b'altcol-cell-val', datetime.datetime(...)),
1014
+ ],
1015
+ }
1016
+ """
1017
+ result = {}
1018
+ for column in family_pb.columns:
1019
+ result[column.qualifier] = cells = []
1020
+ for cell in column.cells:
1021
+ val_pair = (cell.value, _datetime_from_microseconds(cell.timestamp_micros))
1022
+ cells.append(val_pair)
1023
+
1024
+ return family_pb.name, result
1025
+
1026
+
1027
+ class PartialRowData(object):
1028
+ """Representation of partial row in a Google Cloud Bigtable Table.
1029
+
1030
+ These are expected to be updated directly from a
1031
+ :class:`._generated.bigtable_service_messages_pb2.ReadRowsResponse`
1032
+
1033
+ :type row_key: bytes
1034
+ :param row_key: The key for the row holding the (partial) data.
1035
+ """
1036
+
1037
+ def __init__(self, row_key):
1038
+ self._row_key = row_key
1039
+ self._cells = {}
1040
+
1041
+ def __eq__(self, other):
1042
+ if not isinstance(other, self.__class__):
1043
+ return NotImplemented
1044
+ return other._row_key == self._row_key and other._cells == self._cells
1045
+
1046
+ def __ne__(self, other):
1047
+ return not self == other
1048
+
1049
+ def to_dict(self):
1050
+ """Convert the cells to a dictionary.
1051
+
1052
+ This is intended to be used with HappyBase, so the column family and
1053
+ column qualiers are combined (with ``:``).
1054
+
1055
+ :rtype: dict
1056
+ :returns: Dictionary containing all the data in the cells of this row.
1057
+ """
1058
+ result = {}
1059
+ for column_family_id, columns in self._cells.items():
1060
+ for column_qual, cells in columns.items():
1061
+ key = _to_bytes(column_family_id) + b":" + _to_bytes(column_qual)
1062
+ result[key] = cells
1063
+ return result
1064
+
1065
+ @property
1066
+ def cells(self):
1067
+ """Property returning all the cells accumulated on this partial row.
1068
+
1069
+ For example:
1070
+
1071
+ .. literalinclude:: snippets_table.py
1072
+ :start-after: [START bigtable_api_row_data_cells]
1073
+ :end-before: [END bigtable_api_row_data_cells]
1074
+ :dedent: 4
1075
+
1076
+ :rtype: dict
1077
+ :returns: Dictionary of the :class:`Cell` objects accumulated. This
1078
+ dictionary has two-levels of keys (first for column families
1079
+ and second for column names/qualifiers within a family). For
1080
+ a given column, a list of :class:`Cell` objects is stored.
1081
+ """
1082
+ return self._cells
1083
+
1084
+ @property
1085
+ def row_key(self):
1086
+ """Getter for the current (partial) row's key.
1087
+
1088
+ :rtype: bytes
1089
+ :returns: The current (partial) row's key.
1090
+ """
1091
+ return self._row_key
1092
+
1093
+ def find_cells(self, column_family_id, column):
1094
+ """Get a time series of cells stored on this instance.
1095
+
1096
+ For example:
1097
+
1098
+ .. literalinclude:: snippets_table.py
1099
+ :start-after: [START bigtable_api_row_find_cells]
1100
+ :end-before: [END bigtable_api_row_find_cells]
1101
+ :dedent: 4
1102
+
1103
+ Args:
1104
+ column_family_id (str): The ID of the column family. Must be of the
1105
+ form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
1106
+ column (bytes): The column within the column family where the cells
1107
+ are located.
1108
+
1109
+ Returns:
1110
+ List[~google.cloud.bigtable.row_data.Cell]: The cells stored in the
1111
+ specified column.
1112
+
1113
+ Raises:
1114
+ KeyError: If ``column_family_id`` is not among the cells stored
1115
+ in this row.
1116
+ KeyError: If ``column`` is not among the cells stored in this row
1117
+ for the given ``column_family_id``.
1118
+ """
1119
+ try:
1120
+ column_family = self._cells[column_family_id]
1121
+ except KeyError:
1122
+ raise KeyError(_MISSING_COLUMN_FAMILY.format(column_family_id))
1123
+
1124
+ try:
1125
+ cells = column_family[column]
1126
+ except KeyError:
1127
+ raise KeyError(_MISSING_COLUMN.format(column, column_family_id))
1128
+
1129
+ return cells
1130
+
1131
+ def cell_value(self, column_family_id, column, index=0):
1132
+ """Get a single cell value stored on this instance.
1133
+
1134
+ For example:
1135
+
1136
+ .. literalinclude:: snippets_table.py
1137
+ :start-after: [START bigtable_api_row_cell_value]
1138
+ :end-before: [END bigtable_api_row_cell_value]
1139
+ :dedent: 4
1140
+
1141
+ Args:
1142
+ column_family_id (str): The ID of the column family. Must be of the
1143
+ form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
1144
+ column (bytes): The column within the column family where the cell
1145
+ is located.
1146
+ index (Optional[int]): The offset within the series of values. If
1147
+ not specified, will return the first cell.
1148
+
1149
+ Returns:
1150
+ ~google.cloud.bigtable.row_data.Cell value: The cell value stored
1151
+ in the specified column and specified index.
1152
+
1153
+ Raises:
1154
+ KeyError: If ``column_family_id`` is not among the cells stored
1155
+ in this row.
1156
+ KeyError: If ``column`` is not among the cells stored in this row
1157
+ for the given ``column_family_id``.
1158
+ IndexError: If ``index`` cannot be found within the cells stored
1159
+ in this row for the given ``column_family_id``, ``column``
1160
+ pair.
1161
+ """
1162
+ cells = self.find_cells(column_family_id, column)
1163
+
1164
+ try:
1165
+ cell = cells[index]
1166
+ except (TypeError, IndexError):
1167
+ num_cells = len(cells)
1168
+ msg = _MISSING_INDEX.format(index, column, column_family_id, num_cells)
1169
+ raise IndexError(msg)
1170
+
1171
+ return cell.value
1172
+
1173
+ def cell_values(self, column_family_id, column, max_count=None):
1174
+ """Get a time series of cells stored on this instance.
1175
+
1176
+ For example:
1177
+
1178
+ .. literalinclude:: snippets_table.py
1179
+ :start-after: [START bigtable_api_row_cell_values]
1180
+ :end-before: [END bigtable_api_row_cell_values]
1181
+ :dedent: 4
1182
+
1183
+ Args:
1184
+ column_family_id (str): The ID of the column family. Must be of the
1185
+ form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
1186
+ column (bytes): The column within the column family where the cells
1187
+ are located.
1188
+ max_count (int): The maximum number of cells to use.
1189
+
1190
+ Returns:
1191
+ A generator which provides: cell.value, cell.timestamp_micros
1192
+ for each cell in the list of cells
1193
+
1194
+ Raises:
1195
+ KeyError: If ``column_family_id`` is not among the cells stored
1196
+ in this row.
1197
+ KeyError: If ``column`` is not among the cells stored in this row
1198
+ for the given ``column_family_id``.
1199
+ """
1200
+ cells = self.find_cells(column_family_id, column)
1201
+ if max_count is None:
1202
+ max_count = len(cells)
1203
+
1204
+ for index, cell in enumerate(cells):
1205
+ if index == max_count:
1206
+ break
1207
+
1208
+ yield cell.value, cell.timestamp_micros
1209
+
1210
+
1211
+ class Cell(object):
1212
+ """Representation of a Google Cloud Bigtable Cell.
1213
+
1214
+ :type value: bytes
1215
+ :param value: The value stored in the cell.
1216
+
1217
+ :type timestamp_micros: int
1218
+ :param timestamp_micros: The timestamp_micros when the cell was stored.
1219
+
1220
+ :type labels: list
1221
+ :param labels: (Optional) List of strings. Labels applied to the cell.
1222
+ """
1223
+
1224
+ def __init__(self, value, timestamp_micros, labels=None):
1225
+ self.value = value
1226
+ self.timestamp_micros = timestamp_micros
1227
+ self.labels = list(labels) if labels is not None else []
1228
+
1229
+ @classmethod
1230
+ def from_pb(cls, cell_pb):
1231
+ """Create a new cell from a Cell protobuf.
1232
+
1233
+ :type cell_pb: :class:`._generated.data_pb2.Cell`
1234
+ :param cell_pb: The protobuf to convert.
1235
+
1236
+ :rtype: :class:`Cell`
1237
+ :returns: The cell corresponding to the protobuf.
1238
+ """
1239
+ if cell_pb.labels:
1240
+ return cls(cell_pb.value, cell_pb.timestamp_micros, labels=cell_pb.labels)
1241
+ else:
1242
+ return cls(cell_pb.value, cell_pb.timestamp_micros)
1243
+
1244
+ @property
1245
+ def timestamp(self):
1246
+ return _datetime_from_microseconds(self.timestamp_micros)
1247
+
1248
+ def __eq__(self, other):
1249
+ if not isinstance(other, self.__class__):
1250
+ return NotImplemented
1251
+ return (
1252
+ other.value == self.value
1253
+ and other.timestamp_micros == self.timestamp_micros
1254
+ and other.labels == self.labels
1255
+ )
1256
+
1257
+ def __ne__(self, other):
1258
+ return not self == other
1259
+
1260
+ def __repr__(self):
1261
+ return "<{name} value={value!r} timestamp={timestamp}>".format(
1262
+ name=self.__class__.__name__, value=self.value, timestamp=self.timestamp
1263
+ )
1264
+
1265
+
1266
+ class InvalidChunk(RuntimeError):
1267
+ """Exception raised to invalid chunk data from back-end."""