Skip to content

Commit

Permalink
Bigtable: checkAndMutateRow api implementation (#1333)
Browse files Browse the repository at this point in the history
* checkAndMutateRow api

* fix doc

* fix snippet test

* change snippet test

* address feedback

* update note

* update comment

* apply note to truemutations

* fix comment

* fix snippet test
  • Loading branch information
ajaaym authored and dwsupplee committed Oct 19, 2018
1 parent a9fc4d0 commit 04c80e0
Show file tree
Hide file tree
Showing 8 changed files with 676 additions and 56 deletions.
96 changes: 95 additions & 1 deletion Bigtable/src/DataClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Google\Cloud\Bigtable\Exception\BigtableDataOperationException;
use Google\Cloud\Bigtable\Filter\FilterInterface;
use Google\Cloud\Bigtable\ReadModifyWriteRowRules;
use Google\Cloud\Bigtable\Mutations;
use Google\Cloud\Bigtable\V2\BigtableClient as TableClient;
use Google\Cloud\Bigtable\V2\Row;
use Google\Cloud\Bigtable\V2\RowRange;
Expand Down Expand Up @@ -144,7 +145,7 @@ public function mutateRows(array $rowMutations, array $options = [])
{
$entries = [];
foreach ($rowMutations as $rowMutation) {
$entries[] = $rowMutation->getEntry();
$entries[] = $rowMutation->toProto();
}
$responseStream = $this->bigtableClient->mutateRows($this->tableName, $entries, $options + $this->options);
$rowMutationsFailedResponse = [];
Expand Down Expand Up @@ -436,4 +437,97 @@ public function sampleRowKeys(array $options = [])
];
}
}

/**
* Mutates the specified row atomically based on output of the filter.
*
* Example:
* ```
* use Google\Cloud\Bigtable\Mutations;
*
* $mutations = (new Mutations)->upsert('family', 'qualifier', 'value');
* $result = $dataClient->checkAndMutateRow('rk1', ['trueMutations' => $mutations]);
* ```
*
* ```
* // With predicate filter
* use Google\Cloud\Bigtable\Filter;
* use Google\Cloud\Bigtable\Mutations;
*
* $mutations = (new Mutations)->upsert('family', 'qualifier', 'value');
* $predicateFilter = Filter::qualifier()->exactMatch('cq');
* $options = ['predicateFilter' => $predicateFilter, 'trueMutations' => $mutations];
* $result = $dataClient->checkAndMutateRow('rk1', $options);
* ```
*
* @param string $rowKey The row key to mutate row conditionally.
* @param array $options [optional] {
* Configuration options.
*
* @type FilterInterface $predicateFilter The filter to be applied to the specified
* row. Depending on whether or not any results are yielded, either the
* trueMutations or falseMutations will be executed. If unset, checks that the
* row contains any values at all. Only a single condition can be set, however
* that filter can be {@see Google\Cloud\Bigtable\Filter::chain()} or
* {@see Google\Cloud\Bigtable\Filter::interleave()} which can wrap multiple other
* filters.
* WARNING: {@see Google\Cloud\Bigtable\Filter::condition()} is not supported.
* @type Mutations $trueMutations Mutations to be atomically applied when the predicate
* filter's condition yields at least one cell when applied to the row. Please note
* either `trueMutations` or `falseMutations` must be provided.
* @type Mutations $falseMutations Mutations to be atomically applied when the predicate
* filter's condition does not yield any cells when applied to the row. Please note
* either `trueMutations` or `falseMutations` must be provided.
* }
*
* @return bool Returns true if predicate filter yielded any output, false otherwise.
* @throws ApiException if the remote call fails or operation fails
* @throws \InvalidArgumentException if neither of $trueMutations or $falseMutations is set.
* if $predicateFilter is not instance of {@see Google\Cloud\Bigtable\Filter\FilterInterface}.
* if $trueMutations is set and is not instance of {@see Google\Cloud\Bigtable\Mutations}.
* if $falseMutations is set and is not instance of {@see Google\Cloud\Bigtable\Mutations}.
*/
public function checkAndMutateRow($rowKey, array $options = [])
{
$hasSetMutations = false;

if (isset($options['predicateFilter'])) {
$this->convertToProto($options, 'predicateFilter', FilterInterface::class);
}
if (isset($options['trueMutations'])) {
$this->convertToProto($options, 'trueMutations', Mutations::class);
$hasSetMutations = true;
}
if (isset($options['falseMutations'])) {
$this->convertToProto($options, 'falseMutations', Mutations::class);
$hasSetMutations = true;
}
if (!$hasSetMutations) {
throw new \InvalidArgumentException('checkAndMutateRow must have either trueMutations or falseMutations.');
}

return $this->bigtableClient
->checkAndMutateRow(
$this->tableName,
$rowKey,
$options + $this->options
)
->getPredicateMatched();
}

private function convertToProto(array &$options, $key, $expectedType)
{
if (!$options[$key] instanceof $expectedType) {
throw new \InvalidArgumentException(
sprintf(
'Expected %s to be of type \'%s\', instead got \'%s\'.',
$key,
$expectedType,
gettype($options[$key])
)
);
}

$options[$key] = $options[$key]->toProto();
}
}
145 changes: 145 additions & 0 deletions Bigtable/src/Mutations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php
/**
* Copyright 2018, Google LLC All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Bigtable;

use Google\Cloud\Bigtable\V2\Mutation;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromColumn;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromFamily;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromRow;
use Google\Cloud\Bigtable\V2\Mutation\SetCell;
use Google\Cloud\Bigtable\V2\TimestampRange;

/**
* Represents a Mutation to perform data operation on Bigtable table.
* This is used to insert,update, delete operation on row in Bigtable table.
*/
class Mutations
{
/**
* Creates Insert/Update mutation for a row.
*
* @param string $family Family name of the row.
* @param string $qualifier Column qualifier of the row.
* @param string $value Value of the column qualifier.
* @param string|int $timeStamp [optional] A timestamp value, in microseconds.
* Use the value `-1` to utilize the current Bigtable server time.
* **Defaults to** the current local system time.
*
* @return Mutations returns current Mutations object.
*/
public function upsert($family, $qualifier, $value, $timeStamp = null)
{
$mutationSetCell = (new SetCell)
->setFamilyName($family)
->setColumnQualifier($qualifier)
->setValue($value);
if ($timeStamp === null) {
$mutationSetCell->setTimestampMicros(
// gives milli second
round($this->microtime() * 1000)
// multiply by 1000 to get micro
* 1000
);
} else {
$mutationSetCell->setTimestampMicros($timeStamp);
}
$this->mutations[] = (new Mutation)->setSetCell($mutationSetCell);
return $this;
}

/**
* Creates delete from family mutation for a row.
*
* @param string $family Family name of the row.
*
* @return Mutations returns current Mutations object.
*/
public function deleteFromFamily($family)
{
$this->mutations[] = (new Mutation)
->setDeleteFromFamily(
(new DeleteFromFamily)->setFamilyName($family)
);
return $this;
}

/**
* Creates delete from column mutation for a row.
*
* @param string $family Family name of the row.
* @param string $qualifier Column qualifier of the row.
* @param array $timeRange [optional] Array of values value, in microseconds to
* delete from column, keyed by `start` and `end` representing time range
* window.
* `start` **Defaults to** 0
* `end` **Defaults to** infinity
* @return Mutations returns current Mutations object.
*/
public function deleteFromColumn($family, $qualifier, array $timeRange = [])
{
$deleteFromColumn = (new DeleteFromColumn)
->setFamilyName($family)
->setColumnQualifier($qualifier);
if (!empty($timeRange)) {
$timestampRange = new TimestampRange;
if (isset($timeRange['start'])) {
$timestampRange->setStartTimestampMicros($timeRange['start']);
}
if (isset($timeRange['end'])) {
$timestampRange->setEndTimestampMicros($timeRange['end']);
}
$deleteFromColumn->setTimeRange($timestampRange);
}
$this->mutations[] = (new Mutation)->setDeleteFromColumn($deleteFromColumn);
return $this;
}

/**
* Creates delete row mutation for a row.
*
* @return Mutations returns current Mutations object.
*/
public function deleteRow()
{
$this->mutations[] = (new Mutation)->setDeleteFromRow(new DeleteFromRow);
return $this;
}

/**
* Returns current unix timestamp with microseconds. This method exists for
* testing purposes.
*
* @return float
*/
protected function microtime()
{
return microtime(true);
}

/**
* Returns protobuf representation of Mutations.
*
* @internal
* @access private
* @return array returns array of protobuf representation of Mutations.
*/
public function toProto()
{
return $this->mutations;
}
}
57 changes: 10 additions & 47 deletions Bigtable/src/RowMutation.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,29 @@

namespace Google\Cloud\Bigtable;

use Google\Cloud\Bigtable\Mutations;
use Google\Cloud\Bigtable\V2\MutateRowsRequest\Entry;
use Google\Cloud\Bigtable\V2\Mutation;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromColumn;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromFamily;
use Google\Cloud\Bigtable\V2\Mutation\DeleteFromRow;
use Google\Cloud\Bigtable\V2\Mutation\SetCell;
use Google\Cloud\Bigtable\V2\TimestampRange;

/**
* Represents a RowMutation to perform data operation on Bigtable table.
* This is used to insert,update, delete operation on row in Bigtable table.
*/
class RowMutation
{

/**
* @var string
*/
private $rowKey;

/**
* @var array Mutation
* @var Mutations
*/
private $mutations = [];
private $mutations;

public function __construct($rowKey, array $options = [])
{
$this->rowKey = $rowKey;
$this->mutations = new Mutations($options);
}

/**
Expand All @@ -69,23 +64,7 @@ public function getRowKey()
*/
public function upsert($family, $qualifier, $value, $timeStamp = null)
{
$mutation = new Mutation;
$mutationSetCell = new SetCell;
$mutationSetCell->setFamilyName($family)
->setColumnQualifier($qualifier)
->setValue($value);
if ($timeStamp === null) {
$mutationSetCell->setTimestampMicros(
// gives milli second
round(microtime(true) * 1000)
// multiply by 1000 to get micro
* 1000
);
} else {
$mutationSetCell->setTimestampMicros($timeStamp);
}
$mutation->setSetCell($mutationSetCell);
$this->mutations[] = $mutation;
$this->mutations->upsert($family, $qualifier, $value, $timeStamp);
return $this;
}

Expand All @@ -98,11 +77,7 @@ public function upsert($family, $qualifier, $value, $timeStamp = null)
*/
public function deleteFromFamily($family)
{
$mutation = new Mutation;
$deleteFromFamily = new DeleteFromFamily;
$deleteFromFamily->setFamilyName($family);
$mutation->setDeleteFromFamily($deleteFromFamily);
$this->mutations[] = $mutation;
$this->mutations->deleteFromFamily($family);
return $this;
}

Expand All @@ -117,17 +92,7 @@ public function deleteFromFamily($family)
*/
public function deleteFromColumn($family, $qualifier, array $timeRange = [])
{
$mutation = new Mutation;
$deleteFromColumn = new DeleteFromColumn;
$deleteFromColumn->setFamilyName($family)->setColumnQualifier($qualifier);
if (!empty($timeRange)) {
$timestampRange = new TimestampRange;
$timestampRange->setStartTimestampMicros($timeRange['start']);
$timestampRange->setEndTimestampMicros($timeRange['end']);
$deleteFromColumn->setTimeRange($timestampRange);
}
$mutation->setDeleteFromColumn($deleteFromColumn);
$this->mutations[] = $mutation;
$this->mutations->deleteFromColumn($family, $qualifier, $timeRange);
return $this;
}

Expand All @@ -138,9 +103,7 @@ public function deleteFromColumn($family, $qualifier, array $timeRange = [])
*/
public function deleteRow()
{
$mutation = new Mutation;
$mutation->setDeleteFromRow(new DeleteFromRow);
$this->mutations[] = $mutation;
$this->mutations->deleteRow();
return $this;
}

Expand All @@ -149,11 +112,11 @@ public function deleteRow()
*
* @return Entry Entry for Row.
*/
public function getEntry()
public function toProto()
{
$mutateRowsRequestEntry = new Entry;
$mutateRowsRequestEntry->setRowKey($this->rowKey);
$mutateRowsRequestEntry->setMutations($this->mutations);
$mutateRowsRequestEntry->setMutations($this->mutations->toProto());
return $mutateRowsRequestEntry;
}
}
Loading

0 comments on commit 04c80e0

Please sign in to comment.