Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: add row exists api (#190)
* feat: add row exists api

Adding a new Row exists() API in `BigtableDataClient`

* rephrased java doc

* addressed feedback comment to add FILTERS.value().strip()
  • Loading branch information
rahulKQL committed Feb 5, 2020
1 parent 687335b commit d141c3d
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 0 deletions.
Expand Up @@ -15,7 +15,11 @@
*/
package com.google.cloud.bigtable.data.v2;

import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS;

import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.api.gax.batching.Batcher;
Expand All @@ -36,6 +40,7 @@
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.List;
Expand Down Expand Up @@ -159,6 +164,135 @@ public static BigtableDataClient create(BigtableDataSettings settings) throws IO
this.stub = stub;
}

/**
* Confirms synchronously if given row key exists or not.
*
* <p>Sample code:
*
* <pre>{@code
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
* String tableId = "[TABLE]";
* String key = "key";
*
* boolean isRowPresent = bigtableDataClient.exists(tableId, key);
*
* // Do something with result, for example, display a message
* if(isRowPresent) {
* System.out.println(key + " is present");
* }
* } catch(ApiException e) {
* e.printStackTrace();
* }
* }</pre>
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
*/
public boolean exists(String tableId, String rowKey) {
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
}

/**
* Confirms synchronously if given row key exists or not.
*
* <p>Sample code:
*
* <pre>{@code
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
* String tableId = "[TABLE]";
* ByteString key = ByteString.copyFromUtf8("key");
*
* boolean isRowPresent = bigtableDataClient.exists(tableId, key);
*
* // Do something with result, for example, display a message
* if(isRowPresent) {
* System.out.println(key.toStringUtf8() + " is present");
* }
* } catch(ApiException e) {
* e.printStackTrace();
* }
* }</pre>
*
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
*/
public boolean exists(String tableId, ByteString rowKey) {
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
}

/**
* Confirms asynchronously if given row key exists or not.
*
* <p>Sample code:
*
* <pre>{@code
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
* String tableId = "[TABLE]";
* final String key = "key";
*
* ApiFuture<Boolean> futureResult = bigtableDataClient.existsAsync(tableId, key);
*
* ApiFutures.addCallback(futureResult, new ApiFutureCallback<Boolean>() {
* public void onFailure(Throwable t) {
* t.printStackTrace();
* }
* public void onSuccess(Boolean isRowPresent) {
* if(isRowPresent) {
* System.out.println(key + " is present");
* }
* }
* }, MoreExecutors.directExecutor());
* }
* }</pre>
*/
public ApiFuture<Boolean> existsAsync(String tableId, String rowKey) {
return existsAsync(tableId, ByteString.copyFromUtf8(rowKey));
}

/**
* Confirms asynchronously if given row key exists or not.
*
* <p>Sample code:
*
* <pre>{@code
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
* String tableId = "[TABLE]";
* final ByteString key = ByteString.copyFromUtf8("key");
*
* ApiFuture<Boolean> futureResult = bigtableDataClient.existsAsync(tableId, key);
*
* ApiFutures.addCallback(futureResult, new ApiFutureCallback<Boolean>() {
* public void onFailure(Throwable t) {
* t.printStackTrace();
* }
* public void onSuccess(Boolean isRowPresent) {
* if(isRowPresent) {
* System.out.println(key.toStringUtf8() + " is present");
* }
* }
* }, MoreExecutors.directExecutor());
* }
* }</pre>
*/
public ApiFuture<Boolean> existsAsync(String tableId, ByteString rowKey) {
Query query =
Query.create(tableId)
.rowKey(rowKey)
.filter(
FILTERS
.chain()
.filter(FILTERS.limit().cellsPerRow(1))
.filter(FILTERS.value().strip()));
ApiFuture<Row> resultFuture = stub.readRowCallable().futureCall(query);
return ApiFutures.transform(
resultFuture,
new ApiFunction<Row, Boolean>() {
@Override
public Boolean apply(Row row) {
return row != null;
}
},
MoreExecutors.directExecutor());
}

/**
* Convenience method for synchronously reading a single row. If the row does not exist, the value
* will be null.
Expand Down
Expand Up @@ -92,6 +92,57 @@ public void proxyCloseTest() throws Exception {
Mockito.verify(mockStub).close();
}

@Test
public void existsTest() {
Query expectedQuery =
Query.create("fake-table")
.rowKey("fake-row-key")
.filter(
FILTERS
.chain()
.filter(FILTERS.limit().cellsPerRow(1))
.filter(FILTERS.value().strip()));
Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.<RowCell>of());
Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
.thenReturn(ApiFutures.immediateFuture(row))
.thenReturn(ApiFutures.<Row>immediateFuture(null));

boolean result = bigtableDataClient.exists("fake-table", "fake-row-key");
boolean anotherResult =
bigtableDataClient.exists("fake-table", ByteString.copyFromUtf8("fake-row-key"));

assertThat(result).isTrue();
assertThat(anotherResult).isFalse();

Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
}

@Test
public void existsAsyncTest() throws Exception {
Query expectedQuery =
Query.create("fake-table")
.rowKey("fake-row-key")
.filter(
FILTERS
.chain()
.filter(FILTERS.limit().cellsPerRow(1))
.filter(FILTERS.value().strip()));
Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.<RowCell>of());

Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
.thenReturn(ApiFutures.immediateFuture(row))
.thenReturn(ApiFutures.<Row>immediateFuture(null));

ApiFuture<Boolean> result =
bigtableDataClient.existsAsync("fake-table", ByteString.copyFromUtf8("fake-row-key"));
assertThat(result.get()).isTrue();

ApiFuture<Boolean> anotherResult = bigtableDataClient.existsAsync("fake-table", "fake-row-key");
assertThat(anotherResult.get()).isFalse();

Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
}

@Test
public void proxyReadRowsCallableTest() {
assertThat(bigtableDataClient.readRowsCallable()).isSameInstanceAs(mockReadRowsCallable);
Expand Down
Expand Up @@ -57,6 +57,26 @@ public void setUp() {
prefix = UUID.randomUUID().toString();
}

@Test
public void isRowExists() throws Exception {
String rowKey = prefix + "-test-row-key";
String tableId = testEnvRule.env().getTableId();
testEnvRule
.env()
.getDataClient()
.mutateRow(
RowMutation.create(tableId, rowKey)
.setCell(testEnvRule.env().getFamilyId(), "qualifier", "value"));

assertThat(testEnvRule.env().getDataClient().exists(tableId, rowKey)).isTrue();

String nonExistingKey = prefix + "non-existing-key";
assertThat(testEnvRule.env().getDataClient().exists(tableId, nonExistingKey)).isFalse();

// Async
assertThat(testEnvRule.env().getDataClient().existsAsync(tableId, rowKey).get()).isTrue();
}

@Test
public void readEmpty() throws Throwable {
String uniqueKey = prefix + "-readEmpty";
Expand Down

0 comments on commit d141c3d

Please sign in to comment.