com.tigrisdata.db.client.StandardTigrisAsyncCollection Maven / Gradle / Ivy
/*
* Copyright 2022 Tigris Data, Inc.
*
* 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.
*/
package com.tigrisdata.db.client;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.ListenableFuture;
import com.tigrisdata.db.api.v1.grpc.Api;
import com.tigrisdata.db.api.v1.grpc.TigrisDBGrpc;
import static com.tigrisdata.db.client.Messages.DELETE_FAILED;
import static com.tigrisdata.db.client.Messages.DESCRIBE_COLLECTION_FAILED;
import static com.tigrisdata.db.client.Messages.INSERT_FAILED;
import static com.tigrisdata.db.client.Messages.INSERT_OR_REPLACE_FAILED;
import static com.tigrisdata.db.client.Messages.READ_FAILED;
import static com.tigrisdata.db.client.Messages.UPDATE_FAILED;
import static com.tigrisdata.db.client.TypeConverter.readOneDefaultReadRequestOptions;
import static com.tigrisdata.db.client.TypeConverter.toCollectionDescription;
import static com.tigrisdata.db.client.TypeConverter.toCollectionOptions;
import static com.tigrisdata.db.client.TypeConverter.toDeleteRequest;
import static com.tigrisdata.db.client.TypeConverter.toInsertRequest;
import static com.tigrisdata.db.client.TypeConverter.toReadRequest;
import static com.tigrisdata.db.client.TypeConverter.toReplaceRequest;
import static com.tigrisdata.db.client.TypeConverter.toUpdateRequest;
import com.tigrisdata.db.client.error.TigrisException;
import com.tigrisdata.db.type.TigrisCollectionType;
import io.grpc.ManagedChannel;
import io.grpc.stub.StreamObserver;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* An async implementation of TigrisDB Collection
*
* @param type of the collection
*/
public class StandardTigrisAsyncCollection
implements TigrisAsyncCollection {
private final String databaseName;
private final String collectionName;
private final Class collectionTypeClass;
private final Executor executor;
private final TigrisDBGrpc.TigrisDBStub stub;
private final TigrisDBGrpc.TigrisDBFutureStub futureStub;
private final ObjectMapper objectMapper;
StandardTigrisAsyncCollection(
String databaseName,
Class collectionTypeClass,
ManagedChannel channel,
Executor executor,
ObjectMapper objectMapper) {
this.databaseName = databaseName;
this.collectionName = Utilities.getCollectionName(collectionTypeClass);
this.collectionTypeClass = collectionTypeClass;
this.executor = executor;
this.stub = TigrisDBGrpc.newStub(channel);
this.futureStub = TigrisDBGrpc.newFutureStub(channel);
this.objectMapper = objectMapper;
}
@Override
public void read(
TigrisFilter filter,
ReadFields fields,
ReadRequestOptions readRequestOptions,
TigrisAsyncReader reader) {
Api.ReadRequest readRequest =
toReadRequest(
databaseName, collectionName, filter, fields, readRequestOptions, objectMapper);
stub.read(
readRequest,
new ReadManyResponseObserverAdapter<>(
reader, collectionTypeClass, objectMapper, READ_FAILED));
}
@Override
public void read(TigrisFilter filter, ReadFields fields, TigrisAsyncReader reader) {
this.read(filter, fields, new ReadRequestOptions(), reader);
}
@Override
public CompletableFuture> readOne(TigrisFilter filter) {
Api.ReadRequest readRequest =
toReadRequest(
databaseName,
collectionName,
filter,
ReadFields.empty(),
readOneDefaultReadRequestOptions(),
objectMapper);
CompletableFuture> completableFuture = new CompletableFuture<>();
stub.read(
readRequest,
new ReadSingleResponseObserverAdapter<>(
completableFuture, collectionTypeClass, objectMapper, READ_FAILED));
return completableFuture;
}
@Override
public CompletableFuture insert(
List documents, InsertRequestOptions insertRequestOptions) throws TigrisException {
try {
Api.InsertRequest insertRequest =
toInsertRequest(
databaseName, collectionName, documents, insertRequestOptions, objectMapper);
ListenableFuture insertResponseListenableFuture =
futureStub.insert(insertRequest);
return Utilities.transformFuture(
insertResponseListenableFuture,
input ->
new InsertResponse(
input.getStatus(),
input.getMetadata().getCreatedAt(),
input.getMetadata().getUpdatedAt()),
executor,
INSERT_FAILED);
} catch (JsonProcessingException jsonProcessingException) {
throw new TigrisException(
"Failed to serialize documents to JSON, This should never happen on generated Java "
+ "models, if the Java models are not locally modified please report a bug",
jsonProcessingException);
}
}
@Override
public CompletableFuture insert(List documents) throws TigrisException {
return this.insert(documents, new InsertRequestOptions());
}
@Override
public CompletableFuture insert(T document) throws TigrisException {
return this.insert(Collections.singletonList(document));
}
@Override
public CompletableFuture insertOrReplace(
List documents, InsertOrReplaceRequestOptions insertOrReplaceRequestOptions)
throws TigrisException {
try {
Api.ReplaceRequest replaceRequest =
toReplaceRequest(
databaseName, collectionName, documents, insertOrReplaceRequestOptions, objectMapper);
ListenableFuture replaceResponseListenableFuture =
futureStub.replace(replaceRequest);
return Utilities.transformFuture(
replaceResponseListenableFuture,
input ->
new InsertOrReplaceResponse(
input.getStatus(),
input.getMetadata().getCreatedAt(),
input.getMetadata().getUpdatedAt()),
executor,
INSERT_OR_REPLACE_FAILED);
} catch (JsonProcessingException jsonProcessingException) {
throw new TigrisException(
"Failed to serialize documents to JSON, This should never happen on generated Java "
+ "models, if the Java models are not locally modified please report a bug",
jsonProcessingException);
}
}
@Override
public CompletableFuture insertOrReplace(List documents)
throws TigrisException {
return this.insertOrReplace(documents, new InsertOrReplaceRequestOptions());
}
@Override
public CompletableFuture update(
TigrisFilter filter, UpdateFields fields, UpdateRequestOptions updateRequestOptions)
throws TigrisException {
Api.UpdateRequest updateRequest =
toUpdateRequest(
databaseName, collectionName, filter, fields, updateRequestOptions, objectMapper);
ListenableFuture updateResponseListenableFuture =
futureStub.update(updateRequest);
return Utilities.transformFuture(
updateResponseListenableFuture,
input ->
new UpdateResponse(
input.getStatus(),
input.getMetadata().getCreatedAt(),
input.getMetadata().getUpdatedAt(),
input.getModifiedCount()),
executor,
UPDATE_FAILED);
}
@Override
public CompletableFuture update(TigrisFilter filter, UpdateFields fields)
throws TigrisException {
return this.update(filter, fields, new UpdateRequestOptions());
}
@Override
public CompletableFuture delete(
TigrisFilter filter, DeleteRequestOptions deleteRequestOptions) {
Api.DeleteRequest deleteRequest =
toDeleteRequest(databaseName, collectionName, filter, deleteRequestOptions, objectMapper);
ListenableFuture deleteResponseListenableFuture =
futureStub.delete(deleteRequest);
return Utilities.transformFuture(
deleteResponseListenableFuture,
response ->
new DeleteResponse(
response.getStatus(),
response.getMetadata().getCreatedAt(),
response.getMetadata().getUpdatedAt()),
executor,
DELETE_FAILED);
}
@Override
public CompletableFuture delete(TigrisFilter filter) {
return this.delete(filter, new DeleteRequestOptions());
}
@Override
public CompletableFuture describe(CollectionOptions collectionOptions)
throws TigrisException {
ListenableFuture describeCollectionResponseListenableFuture =
futureStub.describeCollection(
Api.DescribeCollectionRequest.newBuilder()
.setCollection(collectionName)
.setOptions(toCollectionOptions(collectionOptions, Optional.empty()))
.build());
return Utilities.transformFuture(
describeCollectionResponseListenableFuture,
response -> {
try {
return toCollectionDescription(response);
} catch (TigrisException e) {
throw new IllegalArgumentException(e);
}
},
executor,
DESCRIBE_COLLECTION_FAILED);
}
@Override
public String name() {
return collectionName;
}
static class ReadManyResponseObserverAdapter
implements StreamObserver {
private final TigrisAsyncReader reader;
private final Class collectionTypeClass;
private final ObjectMapper objectMapper;
private final String errorMessage;
public ReadManyResponseObserverAdapter(
TigrisAsyncReader reader,
Class collectionTypeClass,
ObjectMapper objectMapper,
String errorMessage) {
this.reader = reader;
this.collectionTypeClass = collectionTypeClass;
this.objectMapper = objectMapper;
this.errorMessage = errorMessage;
}
@Override
public void onNext(Api.ReadResponse readResponse) {
try {
T doc = objectMapper.readValue(readResponse.getData().toStringUtf8(), collectionTypeClass);
reader.onNext(doc);
} catch (JsonProcessingException ex) {
reader.onError(
new TigrisException(
"Failed to deserialize the read document to your model of type "
+ collectionTypeClass.getName()
+ ", please make sure your local schema generated models are in sync with server schema, please file a bug if your schemas are already in sync",
ex));
}
}
@Override
public void onError(Throwable throwable) {
reader.onError(new TigrisException(errorMessage, throwable));
}
@Override
public void onCompleted() {
reader.onCompleted();
}
}
static class ReadSingleResponseObserverAdapter
implements StreamObserver {
private final CompletableFuture> completableFuture;
private final Class collectionTypeClass;
private final ObjectMapper objectMapper;
private final String errorMessage;
public ReadSingleResponseObserverAdapter(
CompletableFuture> completableFuture,
Class collectionTypeClass,
ObjectMapper objectMapper,
String errorMessage) {
this.completableFuture = completableFuture;
this.collectionTypeClass = collectionTypeClass;
this.objectMapper = objectMapper;
this.errorMessage = errorMessage;
}
@Override
public void onNext(Api.ReadResponse readResponse) {
try {
T doc = objectMapper.readValue(readResponse.getData().toStringUtf8(), collectionTypeClass);
completableFuture.complete(Optional.of(doc));
} catch (JsonProcessingException ex) {
completableFuture.completeExceptionally(
new TigrisException(
"Failed to deserialize the read document to your model of type "
+ collectionTypeClass.getName()
+ ", please make sure your local schema generated models are in sync with server schema, please file a bug if your schemas are already in sync",
ex));
}
}
@Override
public void onError(Throwable throwable) {
completableFuture.completeExceptionally(new TigrisException(errorMessage, throwable));
}
@Override
public void onCompleted() {
// no op
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy