Skip to content

Commit d141c3d

Browse files
authored
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()
1 parent 687335b commit d141c3d

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/BigtableDataClient.java

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
*/
1616
package com.google.cloud.bigtable.data.v2;
1717

18+
import static com.google.cloud.bigtable.data.v2.models.Filters.FILTERS;
19+
20+
import com.google.api.core.ApiFunction;
1821
import com.google.api.core.ApiFuture;
22+
import com.google.api.core.ApiFutures;
1923
import com.google.api.core.BetaApi;
2024
import com.google.api.core.InternalApi;
2125
import com.google.api.gax.batching.Batcher;
@@ -36,6 +40,7 @@
3640
import com.google.cloud.bigtable.data.v2.models.RowMutation;
3741
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
3842
import com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub;
43+
import com.google.common.util.concurrent.MoreExecutors;
3944
import com.google.protobuf.ByteString;
4045
import java.io.IOException;
4146
import java.util.List;
@@ -159,6 +164,135 @@ public static BigtableDataClient create(BigtableDataSettings settings) throws IO
159164
this.stub = stub;
160165
}
161166

167+
/**
168+
* Confirms synchronously if given row key exists or not.
169+
*
170+
* <p>Sample code:
171+
*
172+
* <pre>{@code
173+
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
174+
* String tableId = "[TABLE]";
175+
* String key = "key";
176+
*
177+
* boolean isRowPresent = bigtableDataClient.exists(tableId, key);
178+
*
179+
* // Do something with result, for example, display a message
180+
* if(isRowPresent) {
181+
* System.out.println(key + " is present");
182+
* }
183+
* } catch(ApiException e) {
184+
* e.printStackTrace();
185+
* }
186+
* }</pre>
187+
*
188+
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
189+
*/
190+
public boolean exists(String tableId, String rowKey) {
191+
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
192+
}
193+
194+
/**
195+
* Confirms synchronously if given row key exists or not.
196+
*
197+
* <p>Sample code:
198+
*
199+
* <pre>{@code
200+
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
201+
* String tableId = "[TABLE]";
202+
* ByteString key = ByteString.copyFromUtf8("key");
203+
*
204+
* boolean isRowPresent = bigtableDataClient.exists(tableId, key);
205+
*
206+
* // Do something with result, for example, display a message
207+
* if(isRowPresent) {
208+
* System.out.println(key.toStringUtf8() + " is present");
209+
* }
210+
* } catch(ApiException e) {
211+
* e.printStackTrace();
212+
* }
213+
* }</pre>
214+
*
215+
* @throws com.google.api.gax.rpc.ApiException when a serverside error occurs
216+
*/
217+
public boolean exists(String tableId, ByteString rowKey) {
218+
return ApiExceptions.callAndTranslateApiException(existsAsync(tableId, rowKey));
219+
}
220+
221+
/**
222+
* Confirms asynchronously if given row key exists or not.
223+
*
224+
* <p>Sample code:
225+
*
226+
* <pre>{@code
227+
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
228+
* String tableId = "[TABLE]";
229+
* final String key = "key";
230+
*
231+
* ApiFuture<Boolean> futureResult = bigtableDataClient.existsAsync(tableId, key);
232+
*
233+
* ApiFutures.addCallback(futureResult, new ApiFutureCallback<Boolean>() {
234+
* public void onFailure(Throwable t) {
235+
* t.printStackTrace();
236+
* }
237+
* public void onSuccess(Boolean isRowPresent) {
238+
* if(isRowPresent) {
239+
* System.out.println(key + " is present");
240+
* }
241+
* }
242+
* }, MoreExecutors.directExecutor());
243+
* }
244+
* }</pre>
245+
*/
246+
public ApiFuture<Boolean> existsAsync(String tableId, String rowKey) {
247+
return existsAsync(tableId, ByteString.copyFromUtf8(rowKey));
248+
}
249+
250+
/**
251+
* Confirms asynchronously if given row key exists or not.
252+
*
253+
* <p>Sample code:
254+
*
255+
* <pre>{@code
256+
* try (BigtableDataClient bigtableDataClient = BigtableDataClient.create("[PROJECT]", "[INSTANCE]")) {
257+
* String tableId = "[TABLE]";
258+
* final ByteString key = ByteString.copyFromUtf8("key");
259+
*
260+
* ApiFuture<Boolean> futureResult = bigtableDataClient.existsAsync(tableId, key);
261+
*
262+
* ApiFutures.addCallback(futureResult, new ApiFutureCallback<Boolean>() {
263+
* public void onFailure(Throwable t) {
264+
* t.printStackTrace();
265+
* }
266+
* public void onSuccess(Boolean isRowPresent) {
267+
* if(isRowPresent) {
268+
* System.out.println(key.toStringUtf8() + " is present");
269+
* }
270+
* }
271+
* }, MoreExecutors.directExecutor());
272+
* }
273+
* }</pre>
274+
*/
275+
public ApiFuture<Boolean> existsAsync(String tableId, ByteString rowKey) {
276+
Query query =
277+
Query.create(tableId)
278+
.rowKey(rowKey)
279+
.filter(
280+
FILTERS
281+
.chain()
282+
.filter(FILTERS.limit().cellsPerRow(1))
283+
.filter(FILTERS.value().strip()));
284+
ApiFuture<Row> resultFuture = stub.readRowCallable().futureCall(query);
285+
return ApiFutures.transform(
286+
resultFuture,
287+
new ApiFunction<Row, Boolean>() {
288+
@Override
289+
public Boolean apply(Row row) {
290+
return row != null;
291+
}
292+
},
293+
MoreExecutors.directExecutor());
294+
}
295+
162296
/**
163297
* Convenience method for synchronously reading a single row. If the row does not exist, the value
164298
* will be null.

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/BigtableDataClientTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,57 @@ public void proxyCloseTest() throws Exception {
9292
Mockito.verify(mockStub).close();
9393
}
9494

95+
@Test
96+
public void existsTest() {
97+
Query expectedQuery =
98+
Query.create("fake-table")
99+
.rowKey("fake-row-key")
100+
.filter(
101+
FILTERS
102+
.chain()
103+
.filter(FILTERS.limit().cellsPerRow(1))
104+
.filter(FILTERS.value().strip()));
105+
Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.<RowCell>of());
106+
Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
107+
.thenReturn(ApiFutures.immediateFuture(row))
108+
.thenReturn(ApiFutures.<Row>immediateFuture(null));
109+
110+
boolean result = bigtableDataClient.exists("fake-table", "fake-row-key");
111+
boolean anotherResult =
112+
bigtableDataClient.exists("fake-table", ByteString.copyFromUtf8("fake-row-key"));
113+
114+
assertThat(result).isTrue();
115+
assertThat(anotherResult).isFalse();
116+
117+
Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
118+
}
119+
120+
@Test
121+
public void existsAsyncTest() throws Exception {
122+
Query expectedQuery =
123+
Query.create("fake-table")
124+
.rowKey("fake-row-key")
125+
.filter(
126+
FILTERS
127+
.chain()
128+
.filter(FILTERS.limit().cellsPerRow(1))
129+
.filter(FILTERS.value().strip()));
130+
Row row = Row.create(ByteString.copyFromUtf8("fake-row-key"), ImmutableList.<RowCell>of());
131+
132+
Mockito.when(mockReadRowCallable.futureCall(expectedQuery))
133+
.thenReturn(ApiFutures.immediateFuture(row))
134+
.thenReturn(ApiFutures.<Row>immediateFuture(null));
135+
136+
ApiFuture<Boolean> result =
137+
bigtableDataClient.existsAsync("fake-table", ByteString.copyFromUtf8("fake-row-key"));
138+
assertThat(result.get()).isTrue();
139+
140+
ApiFuture<Boolean> anotherResult = bigtableDataClient.existsAsync("fake-table", "fake-row-key");
141+
assertThat(anotherResult.get()).isFalse();
142+
143+
Mockito.verify(mockReadRowCallable, Mockito.times(2)).futureCall(expectedQuery);
144+
}
145+
95146
@Test
96147
public void proxyReadRowsCallableTest() {
97148
assertThat(bigtableDataClient.readRowsCallable()).isSameInstanceAs(mockReadRowsCallable);

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,26 @@ public void setUp() {
5757
prefix = UUID.randomUUID().toString();
5858
}
5959

60+
@Test
61+
public void isRowExists() throws Exception {
62+
String rowKey = prefix + "-test-row-key";
63+
String tableId = testEnvRule.env().getTableId();
64+
testEnvRule
65+
.env()
66+
.getDataClient()
67+
.mutateRow(
68+
RowMutation.create(tableId, rowKey)
69+
.setCell(testEnvRule.env().getFamilyId(), "qualifier", "value"));
70+
71+
assertThat(testEnvRule.env().getDataClient().exists(tableId, rowKey)).isTrue();
72+
73+
String nonExistingKey = prefix + "non-existing-key";
74+
assertThat(testEnvRule.env().getDataClient().exists(tableId, nonExistingKey)).isFalse();
75+
76+
// Async
77+
assertThat(testEnvRule.env().getDataClient().existsAsync(tableId, rowKey).get()).isTrue();
78+
}
79+
6080
@Test
6181
public void readEmpty() throws Throwable {
6282
String uniqueKey = prefix + "-readEmpty";

0 commit comments

Comments
 (0)