Skip to content

Commit

Permalink
Count command implementation (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
maheshrajamani authored Feb 15, 2023
1 parent 94b544d commit 294eaa0
Show file tree
Hide file tree
Showing 31 changed files with 1,196 additions and 122 deletions.
22 changes: 22 additions & 0 deletions src/main/java/io/stargate/sgv2/jsonapi/StargateJsonApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@
}
}
"""),
@ExampleObject(
name = "countDocuments",
summary = "`countDocuments` command",
value =
"""
{
"countDocuments": {
"filter": {"location": "London", "race.competitors" : {"$eq" : 100}}
}
}
"""),
@ExampleObject(
name = "find",
summary = "`find` command",
Expand Down Expand Up @@ -209,6 +220,17 @@
}
}
"""),
@ExampleObject(
name = "resultCount",
summary = "countDocuments command result",
value =
"""
{
"status": {
"counted_documents": 2
}
}
"""),
@ExampleObject(
name = "resultRead",
summary = "Read command result",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands;
import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateCollectionCommand;
import io.stargate.sgv2.jsonapi.api.model.command.impl.CreateNamespaceCommand;
import io.stargate.sgv2.jsonapi.api.model.command.impl.DeleteOneCommand;
Expand Down Expand Up @@ -34,6 +35,7 @@
include = JsonTypeInfo.As.WRAPPER_OBJECT,
property = "commandName")
@JsonSubTypes({
@JsonSubTypes.Type(value = CountDocumentsCommands.class),
@JsonSubTypes.Type(value = CreateNamespaceCommand.class),
@JsonSubTypes.Type(value = CreateCollectionCommand.class),
@JsonSubTypes.Type(value = DeleteOneCommand.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@

import com.fasterxml.jackson.annotation.JsonProperty;

/** Enum with it's json property name which is returned in api response inside status */
public enum CommandStatus {
/** The element has the count of document */
@JsonProperty("counted_documents")
COUNTED_DOCUMENT,
/** The element has the list of deleted ids */
@JsonProperty("deletedIds")
DELETED_IDS,
/** The element has the list of inserted ids */
@JsonProperty("insertedIds")
INSERTED_IDS,
/** The element has value 1 if collection is created */
@JsonProperty("ok")
OK,
/** The element has the list of updated ids */
@JsonProperty("updatedIds")
UPDATED_IDS;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.stargate.sgv2.jsonapi.api.model.command.impl;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.stargate.sgv2.jsonapi.api.model.command.Filterable;
import io.stargate.sgv2.jsonapi.api.model.command.ReadCommand;
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.FilterClause;
import javax.annotation.Nullable;
import javax.validation.Valid;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

@Schema(
description =
"Command that returns count of documents in a collection based on the collection.")
@JsonTypeName("countDocuments")
public record CountDocumentsCommands(
@Valid @JsonProperty("filter") FilterClause filterClause, @Valid @Nullable Options options)
implements ReadCommand, Filterable {

public record Options() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.stargate.sgv2.jsonapi.api.model.command.CollectionCommand;
import io.stargate.sgv2.jsonapi.api.model.command.CommandContext;
import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands;
import io.stargate.sgv2.jsonapi.api.model.command.impl.DeleteOneCommand;
import io.stargate.sgv2.jsonapi.api.model.command.impl.FindCommand;
import io.stargate.sgv2.jsonapi.api.model.command.impl.FindOneAndUpdateCommand;
Expand Down Expand Up @@ -66,6 +67,7 @@ public CollectionResource(CommandProcessor commandProcessor) {
schema =
@Schema(
anyOf = {
CountDocumentsCommands.class,
DeleteOneCommand.class,
FindOneCommand.class,
FindCommand.class,
Expand All @@ -75,6 +77,7 @@ public CollectionResource(CommandProcessor commandProcessor) {
UpdateOneCommand.class
}),
examples = {
@ExampleObject(ref = "count"),
@ExampleObject(ref = "deleteOne"),
@ExampleObject(ref = "findOne"),
@ExampleObject(ref = "find"),
Expand All @@ -93,6 +96,7 @@ public CollectionResource(CommandProcessor commandProcessor) {
mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = CommandResult.class),
examples = {
@ExampleObject(ref = "resultCount"),
@ExampleObject(ref = "resultRead"),
@ExampleObject(ref = "resultFindOneAndUpdate"),
@ExampleObject(ref = "resultInsert"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public enum ErrorCode {

UNSUPPORTED_FILTER_OPERATION("Unsupported filter operator"),

UNSUPPORTED_OPERATION("Unsupported operation class"),

UNSUPPORTED_UPDATE_DATA_TYPE("Unsupported update data type"),

UNSUPPORTED_UPDATE_OPERATION("Unsupported update operation"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.stargate.sgv2.jsonapi.service.operation.model;

import io.smallrye.mutiny.Uni;
import io.stargate.bridge.proto.QueryOuterClass;
import io.stargate.sgv2.api.common.cql.builder.BuiltCondition;
import io.stargate.sgv2.api.common.cql.builder.QueryBuilder;
import io.stargate.sgv2.jsonapi.api.model.command.CommandContext;
import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import io.stargate.sgv2.jsonapi.exception.ErrorCode;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.bridge.executor.QueryExecutor;
import io.stargate.sgv2.jsonapi.service.operation.model.impl.CountOperationPage;
import io.stargate.sgv2.jsonapi.service.operation.model.impl.DBFilterBase;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
* Operation that returns count of documents based on the filter condition. Written with the
* assumption that all variables to be indexed.
*/
public record CountOperation(CommandContext commandContext, List<DBFilterBase> filters)
implements ReadOperation {

@Override
public Uni<Supplier<CommandResult>> execute(QueryExecutor queryExecutor) {
QueryOuterClass.Query query = buildSelectQuery();
return countDocuments(queryExecutor, query)
.onItem()
.transform(docs -> new CountOperationPage(docs.count()));
}

private QueryOuterClass.Query buildSelectQuery() {
List<BuiltCondition> conditions = new ArrayList<>(filters.size());
for (DBFilterBase filter : filters) {
conditions.add(filter.get());
}
return new QueryBuilder()
.select()
.count("key")
.as("count")
.from(commandContext.namespace(), commandContext.collection())
.where(conditions)
.build();
}

@Override
public Uni<FindResponse> getDocuments(QueryExecutor queryExecutor) {
return Uni.createFrom().failure(new JsonApiException(ErrorCode.UNSUPPORTED_OPERATION));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
* implementation to excute and query and parse the result set as {@link FindResponse}
*/
public interface ReadOperation extends Operation {
static String[] documentColumns = {"key", "tx_id", "doc_json"};
static String[] documentKeyColumns = {"key", "tx_id"};
String[] documentColumns = {"key", "tx_id", "doc_json"};
String[] documentKeyColumns = {"key", "tx_id"};

/**
* Default implementation to query and parse the result set
Expand Down Expand Up @@ -90,6 +90,26 @@ private String extractPagingStateFromResultSet(QueryOuterClass.ResultSet rSet) {
}
return null;
}
/**
* Default implementation to run count query and parse the result set
*
* @param queryExecutor
* @param query
* @return
*/
default Uni<CountResponse> countDocuments(
QueryExecutor queryExecutor, QueryOuterClass.Query query) {
return queryExecutor
.executeRead(query, Optional.empty(), 1)
.onItem()
.transform(
rSet -> {
QueryOuterClass.Row row = rSet.getRows(0); // For count there will be only one row
int count =
Values.int_(row.getValues(0)); // Count value will be the first column value
return new CountResponse(count);
});
}

/**
* A operation method which can return FindResponse instead of CommandResult. This method will be
Expand All @@ -100,5 +120,7 @@ private String extractPagingStateFromResultSet(QueryOuterClass.ResultSet rSet) {
*/
Uni<FindResponse> getDocuments(QueryExecutor queryExecutor);

public static record FindResponse(List<ReadDocument> docs, String pagingState) {}
record FindResponse(List<ReadDocument> docs, String pagingState) {}

record CountResponse(int count) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.stargate.sgv2.jsonapi.service.operation.model;

/**
* Read type specifies what data needs to be read and returned as part of the response for
* operations
*/
public enum ReadType {
/**
* Return documents and transaction id which satisfies the filter conditions as part of response
*/
DOCUMENT,
/**
* Return only document id and transaction id of documents which satisfies the filter conditions
* as part of response
*/
KEY,
/** Return only count of documents which satisfies the filter condition */
COUNT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.stargate.sgv2.jsonapi.service.operation.model.impl;

import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import io.stargate.sgv2.jsonapi.api.model.command.CommandStatus;
import java.util.Map;
import java.util.function.Supplier;

public record CountOperationPage(int count) implements Supplier<CommandResult> {
@Override
public CommandResult get() {
return new CommandResult(Map.of(CommandStatus.COUNTED_DOCUMENT, count()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,23 @@
import io.stargate.sgv2.api.common.cql.builder.QueryBuilder;
import io.stargate.sgv2.jsonapi.api.model.command.CommandContext;
import io.stargate.sgv2.jsonapi.api.model.command.CommandResult;
import io.stargate.sgv2.jsonapi.exception.ErrorCode;
import io.stargate.sgv2.jsonapi.exception.JsonApiException;
import io.stargate.sgv2.jsonapi.service.bridge.executor.QueryExecutor;
import io.stargate.sgv2.jsonapi.service.operation.model.ReadOperation;
import io.stargate.sgv2.jsonapi.service.operation.model.ReadType;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
* Full dynamic query generation for any of the types of filtering we can do against the the db
* table.
*
* <p>Create with a series of filters that are implicitly AND'd together.
*/
/** Operation that returns the documents or its key based on the filter condition. */
public record FindOperation(
CommandContext commandContext,
List<DBFilterBase> filters,
String pagingState,
int limit,
int pageSize,
boolean readDocument,
ReadType readType,
ObjectMapper objectMapper)
implements ReadOperation {

Expand All @@ -38,8 +36,23 @@ public Uni<Supplier<CommandResult>> execute(QueryExecutor queryExecutor) {

@Override
public Uni<FindResponse> getDocuments(QueryExecutor queryExecutor) {
QueryOuterClass.Query query = buildSelectQuery();
return findDocument(queryExecutor, query, pagingState, pageSize, readDocument, objectMapper);
switch (readType) {
case DOCUMENT:
case KEY:
{
QueryOuterClass.Query query = buildSelectQuery();
return findDocument(
queryExecutor,
query,
pagingState,
pageSize,
ReadType.DOCUMENT == readType,
objectMapper);
}
default:
throw new JsonApiException(
ErrorCode.UNSUPPORTED_OPERATION, "Unsupported find operation read type " + readType);
}
}

private QueryOuterClass.Query buildSelectQuery() {
Expand All @@ -49,7 +62,7 @@ private QueryOuterClass.Query buildSelectQuery() {
}
return new QueryBuilder()
.select()
.column(readDocument ? documentColumns : documentKeyColumns)
.column(ReadType.DOCUMENT == readType ? documentColumns : documentKeyColumns)
.from(commandContext.namespace(), commandContext.collection())
.where(conditions)
.limit(limit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
/** FindOperation response implementing the {@link CommandResult} */
public record ReadOperationPage(List<ReadDocument> docs, String pagingState)
implements Supplier<CommandResult> {

@Override
public CommandResult get() {
final List<JsonNode> jsonNodes = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.stargate.sgv2.jsonapi.service.resolver.model.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.stargate.sgv2.jsonapi.api.model.command.CommandContext;
import io.stargate.sgv2.jsonapi.api.model.command.impl.CountDocumentsCommands;
import io.stargate.sgv2.jsonapi.service.operation.model.Operation;
import io.stargate.sgv2.jsonapi.service.operation.model.ReadType;
import io.stargate.sgv2.jsonapi.service.resolver.model.CommandResolver;
import io.stargate.sgv2.jsonapi.service.resolver.model.impl.matcher.FilterableResolver;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

/** Resolves the {@link CountDocumentsCommands } */
@ApplicationScoped
public class CountDocumentsCommandResolver extends FilterableResolver<CountDocumentsCommands>
implements CommandResolver<CountDocumentsCommands> {
@Inject
public CountDocumentsCommandResolver(ObjectMapper objectMapper) {
super(objectMapper);
}

@Override
public Class<CountDocumentsCommands> getCommandClass() {
return CountDocumentsCommands.class;
}

@Override
public Operation resolveCommand(CommandContext ctx, CountDocumentsCommands command) {
return resolve(ctx, command);
}

@Override
protected FilteringOptions getFilteringOption(CountDocumentsCommands command) {
return new FilteringOptions(Integer.MAX_VALUE, null, 1, ReadType.COUNT);
}
}
Loading

0 comments on commit 294eaa0

Please sign in to comment.