From 2ca81302c746ca961a87acbc40b50d2180f56f93 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:36:06 -0500 Subject: [PATCH 01/11] Changes for _id to support all data types --- .../clause/filter/ComparisonExpression.java | 4 + .../model/command/clause/filter/JsonType.java | 3 +- .../FilterClauseDeserializer.java | 12 +- .../serializer/CustomValueSerializers.java | 6 +- .../operation/model/ReadOperation.java | 16 ++- .../model/impl/CreateCollectionOperation.java | 2 +- .../operation/model/impl/DeleteOperation.java | 9 +- .../model/impl/DeleteOperationPage.java | 2 +- .../operation/model/impl/FindOperation.java | 13 +- .../operation/model/impl/InsertOperation.java | 10 +- ...tionPage.java => InsertOperationPage.java} | 4 +- .../model/impl/ReadAndUpdateOperation.java | 9 +- .../operation/model/impl/ReadDocument.java | 3 +- .../model/impl/UpdateOperationPage.java | 4 +- .../impl/matcher/FilterableResolver.java | 11 +- .../service/shredding/model/DocumentId.java | 76 ++++++++++-- .../docsapi/api/v3/InsertIntegrationTest.java | 111 ++++++++++++++++++ .../impl/CreateCollectionOperationTest.java | 2 +- .../model/impl/DeleteOperationTest.java | 50 ++++++-- .../model/impl/FindOperationTest.java | 67 ++++++++--- .../impl/DeleteOneCommandResolverTest.java | 5 +- .../model/impl/FindCommandResolverTest.java | 5 +- .../impl/FindOneAndUpdateResolverTest.java | 5 +- .../impl/FindOneCommandResolverTest.java | 5 +- .../model/impl/UpdateOneResolverTest.java | 5 +- 25 files changed, 364 insertions(+), 75 deletions(-) rename src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/{ModifyOperationPage.java => InsertOperationPage.java} (86%) diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java index 43ffe676e2..b635afef2c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.EnumSet; import java.util.List; @@ -58,6 +59,9 @@ private static JsonLiteral getLiteral(Object value) { if (value == null) { return new JsonLiteral<>(null, JsonType.NULL); } + if (value instanceof DocumentId) { + return new JsonLiteral<>((DocumentId) value, JsonType.DOCUMENT_ID); + } if (value instanceof BigDecimal) { return new JsonLiteral<>((BigDecimal) value, JsonType.NUMBER); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java index 095da4c44e..f1b7089236 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java @@ -7,5 +7,6 @@ public enum JsonType { STRING, NULL, SUB_DOC, - ARRAY + ARRAY, + DOCUMENT_ID } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java index 8904fd0509..7703757817 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java @@ -8,8 +8,10 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ComparisonExpression; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.FilterClause; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; +import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -44,7 +46,8 @@ public FilterClause deserialize( } else { // @TODO: Need to add array value type to this condition expressionList.add( - ComparisonExpression.eq(entry.getKey(), jsonNodeValue(entry.getValue()))); + ComparisonExpression.eq( + entry.getKey(), jsonNodeValue(entry.getKey(), entry.getValue()))); } } return new FilterClause(expressionList); @@ -70,12 +73,15 @@ private ComparisonExpression createComparisonExpression(Map.Entry getDoubleMapValu return to; } - public static QueryOuterClass.Value getDocumentIdValue(DocumentId documentId) { + public static List getDocumentIdValue(DocumentId documentId) { // Temporary implementation until we convert it to Tuple in DB - return Values.of(documentId.toString()); + List tupleValues = + List.of(Values.of(documentId.typeId()), Values.of(documentId.toString())); + return tupleValues; } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java index ec9af7a6e0..58dc4b8d6e 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadDocument; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -56,7 +57,7 @@ default Uni findDocument( try { document = new ReadDocument( - Values.string(row.getValues(0)), // key + getDocumentId(row.getValues(0)), // key Values.uuid(row.getValues(1)), // tx_id readDocument ? objectMapper.readTree(Values.string(row.getValues(2))) @@ -70,6 +71,19 @@ default Uni findDocument( }); } + /** + * Database key type is tuple, first field is json value type and second field is text + * + * @param value + * @return + */ + default DocumentId getDocumentId(QueryOuterClass.Value value) { + QueryOuterClass.Collection coll = value.getCollection(); + int typeId = Values.tinyint(coll.getElements(0)); + String documentIdAsText = Values.string(coll.getElements(1)); + return DocumentId.fromDatabase(typeId, documentIdAsText); + } + private String extractPagingStateFromResultSet(QueryOuterClass.ResultSet rSet) { if (rSet.hasPagingState()) { return BytesValues.toBase64(rSet.getPagingState()); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java index 412bbf3ca6..d5a860c1bf 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java @@ -35,7 +35,7 @@ public Uni> execute(QueryExecutor queryExecutor) { protected QueryOuterClass.Query getCreateTable(String keyspace, String table) { String createTable = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java index 030b881942..008e18df4a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java @@ -7,6 +7,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandContext; import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation.FindResponse; @@ -23,7 +24,7 @@ public record DeleteOperation(CommandContext commandContext, ReadOperation readO public Uni> execute(QueryExecutor queryExecutor) { Uni docsToDelete = readOperation().getDocuments(queryExecutor); final QueryOuterClass.Query delete = buildDeleteQuery(); - final Uni> ids = + final Uni> ids = docsToDelete .onItem() .transformToMulti( @@ -62,7 +63,7 @@ private QueryOuterClass.Query buildDeleteQuery() { * @param doc * @return */ - private static Uni deleteDocument( + private static Uni deleteDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, ReadDocument doc) { query = bindDeleteQuery(query, doc); return queryExecutor @@ -71,7 +72,7 @@ private static Uni deleteDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(doc.id()); + return Uni.createFrom().item(doc.id().value()); } else { return Uni.createFrom().nothing(); } @@ -82,7 +83,7 @@ private static QueryOuterClass.Query bindDeleteQuery( QueryOuterClass.Query builtQuery, ReadDocument doc) { QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(Values.of(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txnId())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java index 37070666c3..6c4f122e0c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java @@ -11,7 +11,7 @@ * * @param deletedIds - document ids deleted */ -public record DeleteOperationPage(List deletedIds) implements Supplier { +public record DeleteOperationPage(List deletedIds) implements Supplier { @Override public CommandResult get() { return new CommandResult(Map.of(CommandStatus.DELETED_IDS, deletedIds)); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java index 734e2297c7..58ed03358f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java @@ -12,7 +12,9 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -145,9 +147,9 @@ public enum Operator { } protected final Operator operator; - protected final String value; + protected final DocumentId value; - public IDFilter(Operator operator, String value) { + public IDFilter(Operator operator, DocumentId value) { this.operator = operator; this.value = value; } @@ -169,7 +171,8 @@ public int hashCode() { public BuiltCondition get() { switch (operator) { case EQ: - return BuiltCondition.of(BuiltCondition.LHS.column("key"), Predicate.EQ, getValue(value)); + return BuiltCondition.of( + BuiltCondition.LHS.column("key"), Predicate.EQ, getDocumentIdValue(value)); default: throw new DocsException( ErrorCode.UNSUPPORTED_FILTER_OPERATION, @@ -245,4 +248,8 @@ private static QueryOuterClass.Value getValue(Object value) { } return Values.of((String) null); } + + private static QueryOuterClass.Value getDocumentIdValue(DocumentId value) { + return Values.of(CustomValueSerializers.getDocumentIdValue(value)); + } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java index b4e46e9390..838d4539cb 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java @@ -26,20 +26,20 @@ public InsertOperation(CommandContext commandContext, WritableShreddedDocument d @Override public Uni> execute(QueryExecutor queryExecutor) { QueryOuterClass.Query query = buildInsertQuery(); - final Uni> ids = + final Uni> ids = Multi.createFrom() .items(documents.stream()) .onItem() .transformToUniAndConcatenate(doc -> insertDocument(queryExecutor, query, doc)) .collect() .asList(); - return ids.onItem().transform(insertedIds -> new ModifyOperationPage(insertedIds, documents)); + return ids.onItem().transform(insertedIds -> new InsertOperationPage(insertedIds, documents)); } - private static Uni insertDocument( + private static Uni insertDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, WritableShreddedDocument doc) { query = bindInsertValues(query, doc); - return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().toString()); + return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().value()); } private QueryOuterClass.Query buildInsertQuery() { @@ -58,7 +58,7 @@ private static QueryOuterClass.Query bindInsertValues( // respect the order in the DocsApiConstants.ALL_COLUMNS_NAMES QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.docJson())) .addValues(Values.of(CustomValueSerializers.getIntegerMapValues(doc.docProperties()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.existKeys()))) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java similarity index 86% rename from src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java rename to src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java index 5eced11fe7..474b6cb558 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java @@ -11,8 +11,8 @@ * The internal to modification operation results, what were the ID's of the docs we changed and * what change. */ -public record ModifyOperationPage( - List insertedIds, List insertedDocs) +public record InsertOperationPage( + List insertedIds, List insertedDocs) implements Supplier { @Override public CommandResult get() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java index b092bdc1de..343e34698a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java @@ -12,6 +12,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -52,7 +53,7 @@ public Uni> execute(QueryExecutor queryExecutor) { .transform(updates -> new UpdateOperationPage(updates, returnDoc())); } - private Uni updatedDocument( + private Uni updatedDocument( QueryExecutor queryExecutor, WritableShreddedDocument writableShreddedDocument) { final QueryOuterClass.Query updateQuery = bindUpdateValues(buildUpdateQuery(), writableShreddedDocument); @@ -62,7 +63,7 @@ private Uni updatedDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(writableShreddedDocument.id().toString()); + return Uni.createFrom().item(writableShreddedDocument.id()); } else { return Uni.createFrom().nothing(); } @@ -111,10 +112,10 @@ protected static QueryOuterClass.Query bindUpdateValues( .addValues(Values.of(CustomValueSerializers.getStringMapValues(doc.queryTextValues()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.queryNullValues()))) .addValues(Values.of(doc.docJson())) - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txID())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } - record UpdatedDocument(String id, JsonNode document) {} + record UpdatedDocument(DocumentId id, JsonNode document) {} } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java index 1ffd0fbc9f..fc6aae1719 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java @@ -1,6 +1,7 @@ package io.stargate.sgv3.docsapi.service.operation.model.impl; import com.fasterxml.jackson.databind.JsonNode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.UUID; /** @@ -10,4 +11,4 @@ * @param txnId Unique UUID resenting point in time of a document, used for LWT transactions * @param document JsonNode representation of the document */ -public record ReadDocument(String id, UUID txnId, JsonNode document) {} +public record ReadDocument(DocumentId id, UUID txnId, JsonNode document) {} diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java index ff943e05dc..3d46e2db65 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java @@ -13,11 +13,11 @@ public record UpdateOperationPage( implements Supplier { @Override public CommandResult get() { - List updatedIds = new ArrayList<>(updatedDocuments().size()); + List updatedIds = new ArrayList<>(updatedDocuments().size()); List updatedDocs = new ArrayList<>(updatedDocuments().size()); updatedDocuments.forEach( update -> { - updatedIds.add(update.id()); + updatedIds.add(update.id().value()); updatedDocs.add(update.document()); }); if (returnDocs) { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java index 7967749830..d71f48e695 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java @@ -8,6 +8,7 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -54,7 +55,7 @@ public FilterableResolver(ObjectMapper objectMapper, boolean findOne, boolean re .addMatchRule(this::findById, FilterMatcher.MatchStrategy.STRICT) .matcher() .capture(ID_GROUP) - .compareValues("_id", ValueComparisonOperator.EQ, JsonType.STRING); + .compareValues("_id", ValueComparisonOperator.EQ, JsonType.DOCUMENT_ID); // NOTE - can only do eq ops on fields until SAI changes matchRules @@ -83,8 +84,8 @@ public record FilteringOptions(int limit, String pagingState, int pageSize) {} private ReadOperation findById(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> @@ -118,8 +119,8 @@ private ReadOperation findNoFilter(CommandContext commandContext, CaptureGroups< private ReadOperation findDynamic(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index da3fffa03c..5d054cc334 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; @@ -21,7 +23,9 @@ * */ public interface DocumentId { - JsonType type(); + int typeId(); + + Object value(); default JsonNode asJson(ObjectMapper mapper) { return asJson(mapper.getNodeFactory()); @@ -29,6 +33,16 @@ default JsonNode asJson(ObjectMapper mapper) { JsonNode asJson(JsonNodeFactory nodeFactory); + // This mapped integers are used in keys in the storage layer. Don't change the values in this + // map. + BiMap dataTypeMapper = + new ImmutableBiMap.Builder() + .put(JsonType.STRING, 1) + .put(JsonType.NUMBER, 2) + .put(JsonType.BOOLEAN, 3) + .put(JsonType.NULL, 4) + .build(); + static DocumentId fromJson(JsonNode node) { switch (node.getNodeType()) { case BOOLEAN -> { @@ -51,6 +65,28 @@ static DocumentId fromJson(JsonNode node) { ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), node.getNodeType())); } + static DocumentId fromDatabase(int typeId, String documentIdAsText) { + switch (dataTypeMapper.inverse().get(typeId)) { + case BOOLEAN -> { + return fromBoolean(Boolean.valueOf(documentIdAsText)); + } + case NULL -> { + return fromNull(); + } + case NUMBER -> { + return fromNumber(new BigDecimal(documentIdAsText)); + } + case STRING -> { + return fromString(documentIdAsText); + } + } + throw new DocsException( + ErrorCode.SHRED_BAD_DOCID_TYPE, + String.format( + "%s: Document Id must be a JSON String(1), Number(2), Boolean(3) or NULL(4) instead got %s", + ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), typeId)); + } + static DocumentId fromBoolean(boolean key) { return BooleanId.valueOf(key); } @@ -79,8 +115,13 @@ static DocumentId fromUUID(UUID uuid) { record StringId(String key) implements DocumentId { @Override - public JsonType type() { - return JsonType.STRING; + public int typeId() { + return dataTypeMapper.get(JsonType.STRING); + } + + @Override + public Object value() { + return key(); } @Override @@ -96,8 +137,13 @@ public String toString() { record NumberId(BigDecimal key) implements DocumentId { @Override - public JsonType type() { - return JsonType.NUMBER; + public int typeId() { + return dataTypeMapper.get(JsonType.NUMBER); + } + + @Override + public Object value() { + return key(); } @Override @@ -120,8 +166,13 @@ public static BooleanId valueOf(boolean b) { } @Override - public JsonType type() { - return JsonType.BOOLEAN; + public int typeId() { + return dataTypeMapper.get(JsonType.BOOLEAN); + } + + @Override + public Object value() { + return key(); } @Override @@ -139,8 +190,13 @@ record NullId() implements DocumentId { public static final NullId NULL = new NullId(); @Override - public JsonType type() { - return JsonType.NULL; + public Object value() { + return null; + } + + @Override + public int typeId() { + return dataTypeMapper.get(JsonType.NULL); } @Override @@ -150,7 +206,7 @@ public JsonNode asJson(JsonNodeFactory nodeFactory) { @Override public String toString() { - return "null"; + return null; } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java index f3a556421a..82b910b49a 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java @@ -68,6 +68,50 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithNumberId() { + String json = + """ + { + "insertOne": { + "document": { + "_id": 4, + "username": "user4" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds[0]", is(4)); + + json = + """ + { + "find": { + "filter" : {"_id" : 4} + } + } + """; + String expected = "{\"_id\": 4, \"username\":\"user4\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = @@ -163,6 +207,73 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithDifferentTypes() { + String json = + """ + { + "insertMany": { + "documents": [{ + "_id": "5", + "username": "user_id_5" + }, + { + "_id": 5, + "username": "user_id_5_number" + }] + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds", contains("5", 5)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + String expected = "{\"_id\":\"5\", \"username\":\"user_id_5\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + expected = "{\"_id\":5, \"username\":\"user_id_5_number\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java index d035199667..a401139025 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java @@ -53,7 +53,7 @@ private List getAllQueryString(String database, String collection) { List queries = new ArrayList<>(); String create = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java index 6df477f037..82e31a703d 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java @@ -15,6 +15,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -43,30 +45,45 @@ public void deleteWithId() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -95,7 +112,10 @@ public void deleteWithIdNoData() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( @@ -112,7 +132,9 @@ public void deleteWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -151,12 +173,22 @@ public void deleteWithDynamic() throws Exception { .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java index 2a9c4847e5..43809a324b 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java @@ -14,6 +14,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -62,7 +64,7 @@ public void findAll() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -74,8 +76,18 @@ public void findAll() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)), - List.of(Values.of("doc2"), Values.of(UUID.randomUUID()), Values.of(doc2)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc2"))), + Values.of(UUID.randomUUID()), + Values.of(doc2)))); FindOperation findOperation = new FindOperation(commandContext, List.of(), null, 2, 2, true, objectMapper); final Supplier execute = @@ -102,13 +114,16 @@ public void findWithId() throws Exception { } """; ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -120,11 +135,18 @@ public void findWithId() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -147,13 +169,16 @@ public void findWithIdNoData() throws Exception { "SELECT key, tx_id, doc_json FROM \"%s\".\"%s\" WHERE key = ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -167,7 +192,9 @@ public void findWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -203,7 +230,7 @@ public void findWithDynamic() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -215,7 +242,12 @@ public void findWithDynamic() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -258,7 +290,7 @@ public void findWithBooleanFilter() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -270,7 +302,12 @@ public void findWithBooleanFilter() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -312,7 +349,7 @@ public void findWithNoResult() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java index 6de743ba9e..a84c1c7ff4 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.DeleteOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java index c72b02fcd0..14c65d6560 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.bridge.config.DocumentConfig; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, documentConfig.maxLimit(), documentConfig.defaultPageSize(), diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java index 124e6467ec..0367acc6c3 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -51,7 +52,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java index b1453c826e..0d8e168eeb 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.api.model.command.impl.FindOneCommand; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -46,7 +47,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java index b40c521c01..d7457810ba 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -50,7 +51,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, From f7d9242f4babab76f14ce0bf4b7b40af9f933a2e Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:36:06 -0500 Subject: [PATCH 02/11] Changes for _id to support all data types --- .../clause/filter/ComparisonExpression.java | 4 + .../model/command/clause/filter/JsonType.java | 3 +- .../FilterClauseDeserializer.java | 12 +- .../serializer/CustomValueSerializers.java | 6 +- .../operation/model/ReadOperation.java | 16 ++- .../model/impl/CreateCollectionOperation.java | 2 +- .../operation/model/impl/DeleteOperation.java | 9 +- .../model/impl/DeleteOperationPage.java | 2 +- .../operation/model/impl/FindOperation.java | 13 +- .../operation/model/impl/InsertOperation.java | 10 +- ...tionPage.java => InsertOperationPage.java} | 4 +- .../model/impl/ReadAndUpdateOperation.java | 9 +- .../operation/model/impl/ReadDocument.java | 3 +- .../model/impl/UpdateOperationPage.java | 4 +- .../impl/matcher/FilterableResolver.java | 11 +- .../service/shredding/model/DocumentId.java | 76 ++++++++++-- .../docsapi/api/v3/InsertIntegrationTest.java | 111 ++++++++++++++++++ .../impl/CreateCollectionOperationTest.java | 2 +- .../model/impl/DeleteOperationTest.java | 50 ++++++-- .../model/impl/FindOperationTest.java | 67 ++++++++--- .../impl/DeleteOneCommandResolverTest.java | 5 +- .../model/impl/FindCommandResolverTest.java | 5 +- .../impl/FindOneAndUpdateResolverTest.java | 5 +- .../impl/FindOneCommandResolverTest.java | 5 +- .../model/impl/UpdateOneResolverTest.java | 5 +- 25 files changed, 364 insertions(+), 75 deletions(-) rename src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/{ModifyOperationPage.java => InsertOperationPage.java} (86%) diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java index 43ffe676e2..b635afef2c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.EnumSet; import java.util.List; @@ -58,6 +59,9 @@ private static JsonLiteral getLiteral(Object value) { if (value == null) { return new JsonLiteral<>(null, JsonType.NULL); } + if (value instanceof DocumentId) { + return new JsonLiteral<>((DocumentId) value, JsonType.DOCUMENT_ID); + } if (value instanceof BigDecimal) { return new JsonLiteral<>((BigDecimal) value, JsonType.NUMBER); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java index 095da4c44e..f1b7089236 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java @@ -7,5 +7,6 @@ public enum JsonType { STRING, NULL, SUB_DOC, - ARRAY + ARRAY, + DOCUMENT_ID } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java index 8904fd0509..7703757817 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java @@ -8,8 +8,10 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ComparisonExpression; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.FilterClause; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; +import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -44,7 +46,8 @@ public FilterClause deserialize( } else { // @TODO: Need to add array value type to this condition expressionList.add( - ComparisonExpression.eq(entry.getKey(), jsonNodeValue(entry.getValue()))); + ComparisonExpression.eq( + entry.getKey(), jsonNodeValue(entry.getKey(), entry.getValue()))); } } return new FilterClause(expressionList); @@ -70,12 +73,15 @@ private ComparisonExpression createComparisonExpression(Map.Entry getDoubleMapValu return to; } - public static QueryOuterClass.Value getDocumentIdValue(DocumentId documentId) { + public static List getDocumentIdValue(DocumentId documentId) { // Temporary implementation until we convert it to Tuple in DB - return Values.of(documentId.toString()); + List tupleValues = + List.of(Values.of(documentId.typeId()), Values.of(documentId.toString())); + return tupleValues; } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java index ec9af7a6e0..58dc4b8d6e 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadDocument; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -56,7 +57,7 @@ default Uni findDocument( try { document = new ReadDocument( - Values.string(row.getValues(0)), // key + getDocumentId(row.getValues(0)), // key Values.uuid(row.getValues(1)), // tx_id readDocument ? objectMapper.readTree(Values.string(row.getValues(2))) @@ -70,6 +71,19 @@ default Uni findDocument( }); } + /** + * Database key type is tuple, first field is json value type and second field is text + * + * @param value + * @return + */ + default DocumentId getDocumentId(QueryOuterClass.Value value) { + QueryOuterClass.Collection coll = value.getCollection(); + int typeId = Values.tinyint(coll.getElements(0)); + String documentIdAsText = Values.string(coll.getElements(1)); + return DocumentId.fromDatabase(typeId, documentIdAsText); + } + private String extractPagingStateFromResultSet(QueryOuterClass.ResultSet rSet) { if (rSet.hasPagingState()) { return BytesValues.toBase64(rSet.getPagingState()); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java index 412bbf3ca6..d5a860c1bf 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java @@ -35,7 +35,7 @@ public Uni> execute(QueryExecutor queryExecutor) { protected QueryOuterClass.Query getCreateTable(String keyspace, String table) { String createTable = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java index 030b881942..008e18df4a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java @@ -7,6 +7,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandContext; import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation.FindResponse; @@ -23,7 +24,7 @@ public record DeleteOperation(CommandContext commandContext, ReadOperation readO public Uni> execute(QueryExecutor queryExecutor) { Uni docsToDelete = readOperation().getDocuments(queryExecutor); final QueryOuterClass.Query delete = buildDeleteQuery(); - final Uni> ids = + final Uni> ids = docsToDelete .onItem() .transformToMulti( @@ -62,7 +63,7 @@ private QueryOuterClass.Query buildDeleteQuery() { * @param doc * @return */ - private static Uni deleteDocument( + private static Uni deleteDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, ReadDocument doc) { query = bindDeleteQuery(query, doc); return queryExecutor @@ -71,7 +72,7 @@ private static Uni deleteDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(doc.id()); + return Uni.createFrom().item(doc.id().value()); } else { return Uni.createFrom().nothing(); } @@ -82,7 +83,7 @@ private static QueryOuterClass.Query bindDeleteQuery( QueryOuterClass.Query builtQuery, ReadDocument doc) { QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(Values.of(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txnId())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java index 37070666c3..6c4f122e0c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java @@ -11,7 +11,7 @@ * * @param deletedIds - document ids deleted */ -public record DeleteOperationPage(List deletedIds) implements Supplier { +public record DeleteOperationPage(List deletedIds) implements Supplier { @Override public CommandResult get() { return new CommandResult(Map.of(CommandStatus.DELETED_IDS, deletedIds)); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java index 734e2297c7..58ed03358f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java @@ -12,7 +12,9 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -145,9 +147,9 @@ public enum Operator { } protected final Operator operator; - protected final String value; + protected final DocumentId value; - public IDFilter(Operator operator, String value) { + public IDFilter(Operator operator, DocumentId value) { this.operator = operator; this.value = value; } @@ -169,7 +171,8 @@ public int hashCode() { public BuiltCondition get() { switch (operator) { case EQ: - return BuiltCondition.of(BuiltCondition.LHS.column("key"), Predicate.EQ, getValue(value)); + return BuiltCondition.of( + BuiltCondition.LHS.column("key"), Predicate.EQ, getDocumentIdValue(value)); default: throw new DocsException( ErrorCode.UNSUPPORTED_FILTER_OPERATION, @@ -245,4 +248,8 @@ private static QueryOuterClass.Value getValue(Object value) { } return Values.of((String) null); } + + private static QueryOuterClass.Value getDocumentIdValue(DocumentId value) { + return Values.of(CustomValueSerializers.getDocumentIdValue(value)); + } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java index b4e46e9390..838d4539cb 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java @@ -26,20 +26,20 @@ public InsertOperation(CommandContext commandContext, WritableShreddedDocument d @Override public Uni> execute(QueryExecutor queryExecutor) { QueryOuterClass.Query query = buildInsertQuery(); - final Uni> ids = + final Uni> ids = Multi.createFrom() .items(documents.stream()) .onItem() .transformToUniAndConcatenate(doc -> insertDocument(queryExecutor, query, doc)) .collect() .asList(); - return ids.onItem().transform(insertedIds -> new ModifyOperationPage(insertedIds, documents)); + return ids.onItem().transform(insertedIds -> new InsertOperationPage(insertedIds, documents)); } - private static Uni insertDocument( + private static Uni insertDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, WritableShreddedDocument doc) { query = bindInsertValues(query, doc); - return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().toString()); + return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().value()); } private QueryOuterClass.Query buildInsertQuery() { @@ -58,7 +58,7 @@ private static QueryOuterClass.Query bindInsertValues( // respect the order in the DocsApiConstants.ALL_COLUMNS_NAMES QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.docJson())) .addValues(Values.of(CustomValueSerializers.getIntegerMapValues(doc.docProperties()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.existKeys()))) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java similarity index 86% rename from src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java rename to src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java index 5eced11fe7..474b6cb558 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java @@ -11,8 +11,8 @@ * The internal to modification operation results, what were the ID's of the docs we changed and * what change. */ -public record ModifyOperationPage( - List insertedIds, List insertedDocs) +public record InsertOperationPage( + List insertedIds, List insertedDocs) implements Supplier { @Override public CommandResult get() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java index b092bdc1de..343e34698a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java @@ -12,6 +12,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -52,7 +53,7 @@ public Uni> execute(QueryExecutor queryExecutor) { .transform(updates -> new UpdateOperationPage(updates, returnDoc())); } - private Uni updatedDocument( + private Uni updatedDocument( QueryExecutor queryExecutor, WritableShreddedDocument writableShreddedDocument) { final QueryOuterClass.Query updateQuery = bindUpdateValues(buildUpdateQuery(), writableShreddedDocument); @@ -62,7 +63,7 @@ private Uni updatedDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(writableShreddedDocument.id().toString()); + return Uni.createFrom().item(writableShreddedDocument.id()); } else { return Uni.createFrom().nothing(); } @@ -111,10 +112,10 @@ protected static QueryOuterClass.Query bindUpdateValues( .addValues(Values.of(CustomValueSerializers.getStringMapValues(doc.queryTextValues()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.queryNullValues()))) .addValues(Values.of(doc.docJson())) - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txID())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } - record UpdatedDocument(String id, JsonNode document) {} + record UpdatedDocument(DocumentId id, JsonNode document) {} } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java index 1ffd0fbc9f..fc6aae1719 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java @@ -1,6 +1,7 @@ package io.stargate.sgv3.docsapi.service.operation.model.impl; import com.fasterxml.jackson.databind.JsonNode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.UUID; /** @@ -10,4 +11,4 @@ * @param txnId Unique UUID resenting point in time of a document, used for LWT transactions * @param document JsonNode representation of the document */ -public record ReadDocument(String id, UUID txnId, JsonNode document) {} +public record ReadDocument(DocumentId id, UUID txnId, JsonNode document) {} diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java index ff943e05dc..3d46e2db65 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java @@ -13,11 +13,11 @@ public record UpdateOperationPage( implements Supplier { @Override public CommandResult get() { - List updatedIds = new ArrayList<>(updatedDocuments().size()); + List updatedIds = new ArrayList<>(updatedDocuments().size()); List updatedDocs = new ArrayList<>(updatedDocuments().size()); updatedDocuments.forEach( update -> { - updatedIds.add(update.id()); + updatedIds.add(update.id().value()); updatedDocs.add(update.document()); }); if (returnDocs) { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java index 7967749830..d71f48e695 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java @@ -8,6 +8,7 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -54,7 +55,7 @@ public FilterableResolver(ObjectMapper objectMapper, boolean findOne, boolean re .addMatchRule(this::findById, FilterMatcher.MatchStrategy.STRICT) .matcher() .capture(ID_GROUP) - .compareValues("_id", ValueComparisonOperator.EQ, JsonType.STRING); + .compareValues("_id", ValueComparisonOperator.EQ, JsonType.DOCUMENT_ID); // NOTE - can only do eq ops on fields until SAI changes matchRules @@ -83,8 +84,8 @@ public record FilteringOptions(int limit, String pagingState, int pageSize) {} private ReadOperation findById(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> @@ -118,8 +119,8 @@ private ReadOperation findNoFilter(CommandContext commandContext, CaptureGroups< private ReadOperation findDynamic(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index da3fffa03c..5d054cc334 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; @@ -21,7 +23,9 @@ * */ public interface DocumentId { - JsonType type(); + int typeId(); + + Object value(); default JsonNode asJson(ObjectMapper mapper) { return asJson(mapper.getNodeFactory()); @@ -29,6 +33,16 @@ default JsonNode asJson(ObjectMapper mapper) { JsonNode asJson(JsonNodeFactory nodeFactory); + // This mapped integers are used in keys in the storage layer. Don't change the values in this + // map. + BiMap dataTypeMapper = + new ImmutableBiMap.Builder() + .put(JsonType.STRING, 1) + .put(JsonType.NUMBER, 2) + .put(JsonType.BOOLEAN, 3) + .put(JsonType.NULL, 4) + .build(); + static DocumentId fromJson(JsonNode node) { switch (node.getNodeType()) { case BOOLEAN -> { @@ -51,6 +65,28 @@ static DocumentId fromJson(JsonNode node) { ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), node.getNodeType())); } + static DocumentId fromDatabase(int typeId, String documentIdAsText) { + switch (dataTypeMapper.inverse().get(typeId)) { + case BOOLEAN -> { + return fromBoolean(Boolean.valueOf(documentIdAsText)); + } + case NULL -> { + return fromNull(); + } + case NUMBER -> { + return fromNumber(new BigDecimal(documentIdAsText)); + } + case STRING -> { + return fromString(documentIdAsText); + } + } + throw new DocsException( + ErrorCode.SHRED_BAD_DOCID_TYPE, + String.format( + "%s: Document Id must be a JSON String(1), Number(2), Boolean(3) or NULL(4) instead got %s", + ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), typeId)); + } + static DocumentId fromBoolean(boolean key) { return BooleanId.valueOf(key); } @@ -79,8 +115,13 @@ static DocumentId fromUUID(UUID uuid) { record StringId(String key) implements DocumentId { @Override - public JsonType type() { - return JsonType.STRING; + public int typeId() { + return dataTypeMapper.get(JsonType.STRING); + } + + @Override + public Object value() { + return key(); } @Override @@ -96,8 +137,13 @@ public String toString() { record NumberId(BigDecimal key) implements DocumentId { @Override - public JsonType type() { - return JsonType.NUMBER; + public int typeId() { + return dataTypeMapper.get(JsonType.NUMBER); + } + + @Override + public Object value() { + return key(); } @Override @@ -120,8 +166,13 @@ public static BooleanId valueOf(boolean b) { } @Override - public JsonType type() { - return JsonType.BOOLEAN; + public int typeId() { + return dataTypeMapper.get(JsonType.BOOLEAN); + } + + @Override + public Object value() { + return key(); } @Override @@ -139,8 +190,13 @@ record NullId() implements DocumentId { public static final NullId NULL = new NullId(); @Override - public JsonType type() { - return JsonType.NULL; + public Object value() { + return null; + } + + @Override + public int typeId() { + return dataTypeMapper.get(JsonType.NULL); } @Override @@ -150,7 +206,7 @@ public JsonNode asJson(JsonNodeFactory nodeFactory) { @Override public String toString() { - return "null"; + return null; } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java index f3a556421a..82b910b49a 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java @@ -68,6 +68,50 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithNumberId() { + String json = + """ + { + "insertOne": { + "document": { + "_id": 4, + "username": "user4" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds[0]", is(4)); + + json = + """ + { + "find": { + "filter" : {"_id" : 4} + } + } + """; + String expected = "{\"_id\": 4, \"username\":\"user4\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = @@ -163,6 +207,73 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithDifferentTypes() { + String json = + """ + { + "insertMany": { + "documents": [{ + "_id": "5", + "username": "user_id_5" + }, + { + "_id": 5, + "username": "user_id_5_number" + }] + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds", contains("5", 5)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + String expected = "{\"_id\":\"5\", \"username\":\"user_id_5\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + expected = "{\"_id\":5, \"username\":\"user_id_5_number\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java index d035199667..a401139025 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java @@ -53,7 +53,7 @@ private List getAllQueryString(String database, String collection) { List queries = new ArrayList<>(); String create = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java index 6df477f037..82e31a703d 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java @@ -15,6 +15,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -43,30 +45,45 @@ public void deleteWithId() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -95,7 +112,10 @@ public void deleteWithIdNoData() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( @@ -112,7 +132,9 @@ public void deleteWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -151,12 +173,22 @@ public void deleteWithDynamic() throws Exception { .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java index 2a9c4847e5..43809a324b 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java @@ -14,6 +14,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -62,7 +64,7 @@ public void findAll() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -74,8 +76,18 @@ public void findAll() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)), - List.of(Values.of("doc2"), Values.of(UUID.randomUUID()), Values.of(doc2)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc2"))), + Values.of(UUID.randomUUID()), + Values.of(doc2)))); FindOperation findOperation = new FindOperation(commandContext, List.of(), null, 2, 2, true, objectMapper); final Supplier execute = @@ -102,13 +114,16 @@ public void findWithId() throws Exception { } """; ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -120,11 +135,18 @@ public void findWithId() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -147,13 +169,16 @@ public void findWithIdNoData() throws Exception { "SELECT key, tx_id, doc_json FROM \"%s\".\"%s\" WHERE key = ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -167,7 +192,9 @@ public void findWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -203,7 +230,7 @@ public void findWithDynamic() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -215,7 +242,12 @@ public void findWithDynamic() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -258,7 +290,7 @@ public void findWithBooleanFilter() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -270,7 +302,12 @@ public void findWithBooleanFilter() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -312,7 +349,7 @@ public void findWithNoResult() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java index 6de743ba9e..a84c1c7ff4 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.DeleteOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java index c72b02fcd0..14c65d6560 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.bridge.config.DocumentConfig; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, documentConfig.maxLimit(), documentConfig.defaultPageSize(), diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java index 124e6467ec..0367acc6c3 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -51,7 +52,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java index b1453c826e..0d8e168eeb 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.api.model.command.impl.FindOneCommand; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -46,7 +47,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java index b40c521c01..d7457810ba 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -50,7 +51,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, From 27e1abdc53d0bbd89eb733aced5ac650989b6405 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:33:50 -0500 Subject: [PATCH 03/11] IT fix for number _id field --- .../io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java index 82b910b49a..0a6c1a01c9 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java @@ -258,7 +258,7 @@ public void insertDocumentWithDifferentTypes() { """ { "find": { - "filter" : {"_id" : "5"} + "filter" : {"_id" : 5} } } """; From 2538ee95e856eec5f2649834e5b182518008dea9 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta <87213665+tatu-at-datastax@users.noreply.github.com> Date: Wed, 1 Feb 2023 09:02:38 -0800 Subject: [PATCH 04/11] Fix #61: Add validation that '_id' can't be empty String, test to verify (#63) --- .../sgv3/docsapi/exception/DocsException.java | 2 +- .../stargate/sgv3/docsapi/exception/ErrorCode.java | 2 ++ .../sgv3/docsapi/service/shredding/Shredder.java | 2 +- .../docsapi/service/shredding/model/DocumentId.java | 6 ++++++ .../docsapi/service/shredding/ShredderTest.java | 13 ++++++++++++- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/stargate/sgv3/docsapi/exception/DocsException.java b/src/main/java/io/stargate/sgv3/docsapi/exception/DocsException.java index 40157ff37d..27bb6bcc1a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/exception/DocsException.java +++ b/src/main/java/io/stargate/sgv3/docsapi/exception/DocsException.java @@ -18,7 +18,7 @@ public class DocsException extends RuntimeException implements Supplier shredder.shred(objectMapper.readTree("{ \"_id\" : [ ] }"))); @@ -197,5 +197,16 @@ public void docBadDocIdType() { "Bad type for '_id' property: Document Id must be a JSON String, Number, Boolean or NULL instead got ARRAY") .hasFieldOrPropertyWithValue("errorCode", ErrorCode.SHRED_BAD_DOCID_TYPE); } + + @Test + public void docBadDocIdEmptyString() { + Throwable t = + catchThrowable(() -> shredder.shred(objectMapper.readTree("{ \"_id\" : \"\" }"))); + + assertThat(t) + .isNotNull() + .hasMessage("Bad value for '_id' property: empty String not allowed") + .hasFieldOrPropertyWithValue("errorCode", ErrorCode.SHRED_BAD_DOCID_EMPTY_STRING); + } } } From c252535a906b38069c2deee61dc7d7f663754871 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 21:21:35 -0500 Subject: [PATCH 05/11] Changes as per review commants. PR #66 --- .../model/command/clause/filter/JsonType.java | 1 + .../config/constants/DocumentConstants.java | 30 +++++++++++++++++++ .../operation/model/impl/DeleteOperation.java | 7 +++-- .../model/impl/DeleteOperationPage.java | 3 +- .../operation/model/impl/InsertOperation.java | 7 +++-- .../model/impl/InsertOperationPage.java | 3 +- .../model/impl/UpdateOperationPage.java | 5 ++-- .../service/shredding/model/DocumentId.java | 28 ++++++----------- .../model/impl/DeleteOperationTest.java | 12 ++++---- 9 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java index f1b7089236..b4860e249c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java @@ -8,5 +8,6 @@ public enum JsonType { NULL, SUB_DOC, ARRAY, + // DOCUMENT_ID represent the _id field type which is union of String, Number, Boolean and Null DOCUMENT_ID } diff --git a/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java b/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java index 8b6fdbf5bc..d0855c5966 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java +++ b/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java @@ -1,9 +1,39 @@ package io.stargate.sgv3.docsapi.config.constants; +import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; +import java.util.Map; + public interface DocumentConstants { /** Names of "special" fields in Documents */ interface Fields { /** Primary key for Documents stored; has special handling for many operations. */ String DOC_ID = "_id"; } + + interface KeyTypeId { + /** + * Type id are used in key stored in database representing the datatype of the id field. These + * values should not be changed once data is stored in the DB. + */ + int TYPE_ID_STRING = 1; + + int TYPE_ID_NUMBER = 2; + int TYPE_ID_BOOLEAN = 3; + int TYPE_ID_NULL = 4; + + Map keyIDMap = + Map.of( + TYPE_ID_STRING, + JsonType.STRING, + TYPE_ID_NUMBER, + JsonType.NUMBER, + TYPE_ID_BOOLEAN, + JsonType.BOOLEAN, + TYPE_ID_NULL, + JsonType.NULL); + + static JsonType getJsonType(int typeId) { + return keyIDMap.get(typeId); + } + } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java index 008e18df4a..f4a1bfe977 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation.FindResponse; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.function.Supplier; @@ -24,7 +25,7 @@ public record DeleteOperation(CommandContext commandContext, ReadOperation readO public Uni> execute(QueryExecutor queryExecutor) { Uni docsToDelete = readOperation().getDocuments(queryExecutor); final QueryOuterClass.Query delete = buildDeleteQuery(); - final Uni> ids = + final Uni> ids = docsToDelete .onItem() .transformToMulti( @@ -63,7 +64,7 @@ private QueryOuterClass.Query buildDeleteQuery() { * @param doc * @return */ - private static Uni deleteDocument( + private static Uni deleteDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, ReadDocument doc) { query = bindDeleteQuery(query, doc); return queryExecutor @@ -72,7 +73,7 @@ private static Uni deleteDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(doc.id().value()); + return Uni.createFrom().item(doc.id()); } else { return Uni.createFrom().nothing(); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java index 6c4f122e0c..155bed5ac5 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -11,7 +12,7 @@ * * @param deletedIds - document ids deleted */ -public record DeleteOperationPage(List deletedIds) implements Supplier { +public record DeleteOperationPage(List deletedIds) implements Supplier { @Override public CommandResult get() { return new CommandResult(Map.of(CommandStatus.DELETED_IDS, deletedIds)); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java index 838d4539cb..1ca7b7fd36 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java @@ -9,6 +9,7 @@ import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import java.util.List; import java.util.function.Supplier; @@ -26,7 +27,7 @@ public InsertOperation(CommandContext commandContext, WritableShreddedDocument d @Override public Uni> execute(QueryExecutor queryExecutor) { QueryOuterClass.Query query = buildInsertQuery(); - final Uni> ids = + final Uni> ids = Multi.createFrom() .items(documents.stream()) .onItem() @@ -36,10 +37,10 @@ public Uni> execute(QueryExecutor queryExecutor) { return ids.onItem().transform(insertedIds -> new InsertOperationPage(insertedIds, documents)); } - private static Uni insertDocument( + private static Uni insertDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, WritableShreddedDocument doc) { query = bindInsertValues(query, doc); - return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().value()); + return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id()); } private QueryOuterClass.Query buildInsertQuery() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java index 474b6cb558..c4a84fa75f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import java.util.List; import java.util.Map; @@ -12,7 +13,7 @@ * what change. */ public record InsertOperationPage( - List insertedIds, List insertedDocs) + List insertedIds, List insertedDocs) implements Supplier { @Override public CommandResult get() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java index 3d46e2db65..5f33a6c6a9 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,11 +14,11 @@ public record UpdateOperationPage( implements Supplier { @Override public CommandResult get() { - List updatedIds = new ArrayList<>(updatedDocuments().size()); + List updatedIds = new ArrayList<>(updatedDocuments().size()); List updatedDocs = new ArrayList<>(updatedDocuments().size()); updatedDocuments.forEach( update -> { - updatedIds.add(update.id().value()); + updatedIds.add(update.id()); updatedDocs.add(update.document()); }); if (returnDocs) { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index 5d054cc334..44abada898 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -1,11 +1,10 @@ package io.stargate.sgv3.docsapi.service.shredding.model; +import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; -import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; +import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; import java.math.BigDecimal; @@ -25,6 +24,7 @@ public interface DocumentId { int typeId(); + @JsonValue Object value(); default JsonNode asJson(ObjectMapper mapper) { @@ -33,16 +33,6 @@ default JsonNode asJson(ObjectMapper mapper) { JsonNode asJson(JsonNodeFactory nodeFactory); - // This mapped integers are used in keys in the storage layer. Don't change the values in this - // map. - BiMap dataTypeMapper = - new ImmutableBiMap.Builder() - .put(JsonType.STRING, 1) - .put(JsonType.NUMBER, 2) - .put(JsonType.BOOLEAN, 3) - .put(JsonType.NULL, 4) - .build(); - static DocumentId fromJson(JsonNode node) { switch (node.getNodeType()) { case BOOLEAN -> { @@ -66,7 +56,7 @@ static DocumentId fromJson(JsonNode node) { } static DocumentId fromDatabase(int typeId, String documentIdAsText) { - switch (dataTypeMapper.inverse().get(typeId)) { + switch (DocumentConstants.KeyTypeId.getJsonType(typeId)) { case BOOLEAN -> { return fromBoolean(Boolean.valueOf(documentIdAsText)); } @@ -116,7 +106,7 @@ static DocumentId fromUUID(UUID uuid) { record StringId(String key) implements DocumentId { @Override public int typeId() { - return dataTypeMapper.get(JsonType.STRING); + return DocumentConstants.KeyTypeId.TYPE_ID_STRING; } @Override @@ -138,7 +128,7 @@ public String toString() { record NumberId(BigDecimal key) implements DocumentId { @Override public int typeId() { - return dataTypeMapper.get(JsonType.NUMBER); + return DocumentConstants.KeyTypeId.TYPE_ID_NUMBER; } @Override @@ -167,7 +157,7 @@ public static BooleanId valueOf(boolean b) { @Override public int typeId() { - return dataTypeMapper.get(JsonType.BOOLEAN); + return DocumentConstants.KeyTypeId.TYPE_ID_BOOLEAN; } @Override @@ -196,7 +186,7 @@ public Object value() { @Override public int typeId() { - return dataTypeMapper.get(JsonType.NULL); + return DocumentConstants.KeyTypeId.TYPE_ID_NULL; } @Override @@ -206,7 +196,7 @@ public JsonNode asJson(JsonNodeFactory nodeFactory) { @Override public String toString() { - return null; + return "null"; } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java index 82e31a703d..0e3ed9d6bb 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java @@ -98,10 +98,10 @@ public void deleteWithId() throws Exception { .satisfies( commandResult -> { assertThat(result.status().get(CommandStatus.DELETED_IDS)).isNotNull(); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) .hasSize(1); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) - .contains("doc1"); + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + .contains(DocumentId.fromString("doc1")); }); } @@ -211,10 +211,10 @@ public void deleteWithDynamic() throws Exception { .satisfies( commandResult -> { assertThat(result.status().get(CommandStatus.DELETED_IDS)).isNotNull(); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) .hasSize(1); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) - .contains("doc1"); + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + .contains(DocumentId.fromString("doc1")); }); } From fb9307295bfe90b87a9414b9b563276f1eabbfb3 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:36:06 -0500 Subject: [PATCH 06/11] Changes for _id to support all data types --- .../clause/filter/ComparisonExpression.java | 4 + .../model/command/clause/filter/JsonType.java | 3 +- .../FilterClauseDeserializer.java | 12 +- .../serializer/CustomValueSerializers.java | 6 +- .../operation/model/ReadOperation.java | 16 ++- .../model/impl/CreateCollectionOperation.java | 2 +- .../operation/model/impl/DeleteOperation.java | 9 +- .../model/impl/DeleteOperationPage.java | 2 +- .../operation/model/impl/FindOperation.java | 13 +- .../operation/model/impl/InsertOperation.java | 10 +- ...tionPage.java => InsertOperationPage.java} | 4 +- .../model/impl/ReadAndUpdateOperation.java | 9 +- .../operation/model/impl/ReadDocument.java | 3 +- .../model/impl/UpdateOperationPage.java | 4 +- .../impl/matcher/FilterableResolver.java | 11 +- .../service/shredding/model/DocumentId.java | 76 ++++++++++-- .../docsapi/api/v3/InsertIntegrationTest.java | 111 ++++++++++++++++++ .../impl/CreateCollectionOperationTest.java | 2 +- .../model/impl/DeleteOperationTest.java | 50 ++++++-- .../model/impl/FindOperationTest.java | 67 ++++++++--- .../impl/DeleteOneCommandResolverTest.java | 5 +- .../model/impl/FindCommandResolverTest.java | 5 +- .../impl/FindOneAndUpdateResolverTest.java | 5 +- .../impl/FindOneCommandResolverTest.java | 5 +- .../model/impl/UpdateOneResolverTest.java | 5 +- 25 files changed, 364 insertions(+), 75 deletions(-) rename src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/{ModifyOperationPage.java => InsertOperationPage.java} (86%) diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java index 43ffe676e2..b635afef2c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/ComparisonExpression.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.EnumSet; import java.util.List; @@ -58,6 +59,9 @@ private static JsonLiteral getLiteral(Object value) { if (value == null) { return new JsonLiteral<>(null, JsonType.NULL); } + if (value instanceof DocumentId) { + return new JsonLiteral<>((DocumentId) value, JsonType.DOCUMENT_ID); + } if (value instanceof BigDecimal) { return new JsonLiteral<>((BigDecimal) value, JsonType.NUMBER); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java index 095da4c44e..f1b7089236 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java @@ -7,5 +7,6 @@ public enum JsonType { STRING, NULL, SUB_DOC, - ARRAY + ARRAY, + DOCUMENT_ID } diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java index 8904fd0509..7703757817 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/FilterClauseDeserializer.java @@ -8,8 +8,10 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ComparisonExpression; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.FilterClause; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; +import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -44,7 +46,8 @@ public FilterClause deserialize( } else { // @TODO: Need to add array value type to this condition expressionList.add( - ComparisonExpression.eq(entry.getKey(), jsonNodeValue(entry.getValue()))); + ComparisonExpression.eq( + entry.getKey(), jsonNodeValue(entry.getKey(), entry.getValue()))); } } return new FilterClause(expressionList); @@ -70,12 +73,15 @@ private ComparisonExpression createComparisonExpression(Map.Entry getDoubleMapValu return to; } - public static QueryOuterClass.Value getDocumentIdValue(DocumentId documentId) { + public static List getDocumentIdValue(DocumentId documentId) { // Temporary implementation until we convert it to Tuple in DB - return Values.of(documentId.toString()); + List tupleValues = + List.of(Values.of(documentId.typeId()), Values.of(documentId.toString())); + return tupleValues; } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java index ec9af7a6e0..58dc4b8d6e 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/ReadOperation.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadDocument; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -56,7 +57,7 @@ default Uni findDocument( try { document = new ReadDocument( - Values.string(row.getValues(0)), // key + getDocumentId(row.getValues(0)), // key Values.uuid(row.getValues(1)), // tx_id readDocument ? objectMapper.readTree(Values.string(row.getValues(2))) @@ -70,6 +71,19 @@ default Uni findDocument( }); } + /** + * Database key type is tuple, first field is json value type and second field is text + * + * @param value + * @return + */ + default DocumentId getDocumentId(QueryOuterClass.Value value) { + QueryOuterClass.Collection coll = value.getCollection(); + int typeId = Values.tinyint(coll.getElements(0)); + String documentIdAsText = Values.string(coll.getElements(1)); + return DocumentId.fromDatabase(typeId, documentIdAsText); + } + private String extractPagingStateFromResultSet(QueryOuterClass.ResultSet rSet) { if (rSet.hasPagingState()) { return BytesValues.toBase64(rSet.getPagingState()); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java index 412bbf3ca6..d5a860c1bf 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperation.java @@ -35,7 +35,7 @@ public Uni> execute(QueryExecutor queryExecutor) { protected QueryOuterClass.Query getCreateTable(String keyspace, String table) { String createTable = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java index 030b881942..008e18df4a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java @@ -7,6 +7,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandContext; import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation.FindResponse; @@ -23,7 +24,7 @@ public record DeleteOperation(CommandContext commandContext, ReadOperation readO public Uni> execute(QueryExecutor queryExecutor) { Uni docsToDelete = readOperation().getDocuments(queryExecutor); final QueryOuterClass.Query delete = buildDeleteQuery(); - final Uni> ids = + final Uni> ids = docsToDelete .onItem() .transformToMulti( @@ -62,7 +63,7 @@ private QueryOuterClass.Query buildDeleteQuery() { * @param doc * @return */ - private static Uni deleteDocument( + private static Uni deleteDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, ReadDocument doc) { query = bindDeleteQuery(query, doc); return queryExecutor @@ -71,7 +72,7 @@ private static Uni deleteDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(doc.id()); + return Uni.createFrom().item(doc.id().value()); } else { return Uni.createFrom().nothing(); } @@ -82,7 +83,7 @@ private static QueryOuterClass.Query bindDeleteQuery( QueryOuterClass.Query builtQuery, ReadDocument doc) { QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(Values.of(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txnId())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java index 37070666c3..6c4f122e0c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java @@ -11,7 +11,7 @@ * * @param deletedIds - document ids deleted */ -public record DeleteOperationPage(List deletedIds) implements Supplier { +public record DeleteOperationPage(List deletedIds) implements Supplier { @Override public CommandResult get() { return new CommandResult(Map.of(CommandStatus.DELETED_IDS, deletedIds)); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java index 734e2297c7..58ed03358f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperation.java @@ -12,7 +12,9 @@ import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -145,9 +147,9 @@ public enum Operator { } protected final Operator operator; - protected final String value; + protected final DocumentId value; - public IDFilter(Operator operator, String value) { + public IDFilter(Operator operator, DocumentId value) { this.operator = operator; this.value = value; } @@ -169,7 +171,8 @@ public int hashCode() { public BuiltCondition get() { switch (operator) { case EQ: - return BuiltCondition.of(BuiltCondition.LHS.column("key"), Predicate.EQ, getValue(value)); + return BuiltCondition.of( + BuiltCondition.LHS.column("key"), Predicate.EQ, getDocumentIdValue(value)); default: throw new DocsException( ErrorCode.UNSUPPORTED_FILTER_OPERATION, @@ -245,4 +248,8 @@ private static QueryOuterClass.Value getValue(Object value) { } return Values.of((String) null); } + + private static QueryOuterClass.Value getDocumentIdValue(DocumentId value) { + return Values.of(CustomValueSerializers.getDocumentIdValue(value)); + } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java index b4e46e9390..838d4539cb 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java @@ -26,20 +26,20 @@ public InsertOperation(CommandContext commandContext, WritableShreddedDocument d @Override public Uni> execute(QueryExecutor queryExecutor) { QueryOuterClass.Query query = buildInsertQuery(); - final Uni> ids = + final Uni> ids = Multi.createFrom() .items(documents.stream()) .onItem() .transformToUniAndConcatenate(doc -> insertDocument(queryExecutor, query, doc)) .collect() .asList(); - return ids.onItem().transform(insertedIds -> new ModifyOperationPage(insertedIds, documents)); + return ids.onItem().transform(insertedIds -> new InsertOperationPage(insertedIds, documents)); } - private static Uni insertDocument( + private static Uni insertDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, WritableShreddedDocument doc) { query = bindInsertValues(query, doc); - return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().toString()); + return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().value()); } private QueryOuterClass.Query buildInsertQuery() { @@ -58,7 +58,7 @@ private static QueryOuterClass.Query bindInsertValues( // respect the order in the DocsApiConstants.ALL_COLUMNS_NAMES QueryOuterClass.Values.Builder values = QueryOuterClass.Values.newBuilder() - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.docJson())) .addValues(Values.of(CustomValueSerializers.getIntegerMapValues(doc.docProperties()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.existKeys()))) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java similarity index 86% rename from src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java rename to src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java index 5eced11fe7..474b6cb558 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ModifyOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java @@ -11,8 +11,8 @@ * The internal to modification operation results, what were the ID's of the docs we changed and * what change. */ -public record ModifyOperationPage( - List insertedIds, List insertedDocs) +public record InsertOperationPage( + List insertedIds, List insertedDocs) implements Supplier { @Override public CommandResult get() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java index b092bdc1de..343e34698a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadAndUpdateOperation.java @@ -12,6 +12,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -52,7 +53,7 @@ public Uni> execute(QueryExecutor queryExecutor) { .transform(updates -> new UpdateOperationPage(updates, returnDoc())); } - private Uni updatedDocument( + private Uni updatedDocument( QueryExecutor queryExecutor, WritableShreddedDocument writableShreddedDocument) { final QueryOuterClass.Query updateQuery = bindUpdateValues(buildUpdateQuery(), writableShreddedDocument); @@ -62,7 +63,7 @@ private Uni updatedDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(writableShreddedDocument.id().toString()); + return Uni.createFrom().item(writableShreddedDocument.id()); } else { return Uni.createFrom().nothing(); } @@ -111,10 +112,10 @@ protected static QueryOuterClass.Query bindUpdateValues( .addValues(Values.of(CustomValueSerializers.getStringMapValues(doc.queryTextValues()))) .addValues(Values.of(CustomValueSerializers.getSetValue(doc.queryNullValues()))) .addValues(Values.of(doc.docJson())) - .addValues(CustomValueSerializers.getDocumentIdValue(doc.id())) + .addValues(Values.of(CustomValueSerializers.getDocumentIdValue(doc.id()))) .addValues(Values.of(doc.txID())); return QueryOuterClass.Query.newBuilder(builtQuery).setValues(values).build(); } - record UpdatedDocument(String id, JsonNode document) {} + record UpdatedDocument(DocumentId id, JsonNode document) {} } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java index 1ffd0fbc9f..fc6aae1719 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/ReadDocument.java @@ -1,6 +1,7 @@ package io.stargate.sgv3.docsapi.service.operation.model.impl; import com.fasterxml.jackson.databind.JsonNode; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.UUID; /** @@ -10,4 +11,4 @@ * @param txnId Unique UUID resenting point in time of a document, used for LWT transactions * @param document JsonNode representation of the document */ -public record ReadDocument(String id, UUID txnId, JsonNode document) {} +public record ReadDocument(DocumentId id, UUID txnId, JsonNode document) {} diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java index ff943e05dc..3d46e2db65 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java @@ -13,11 +13,11 @@ public record UpdateOperationPage( implements Supplier { @Override public CommandResult get() { - List updatedIds = new ArrayList<>(updatedDocuments().size()); + List updatedIds = new ArrayList<>(updatedDocuments().size()); List updatedDocs = new ArrayList<>(updatedDocuments().size()); updatedDocuments.forEach( update -> { - updatedIds.add(update.id()); + updatedIds.add(update.id().value()); updatedDocs.add(update.document()); }); if (returnDocs) { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java index 7967749830..d71f48e695 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/matcher/FilterableResolver.java @@ -8,6 +8,7 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.filter.ValueComparisonOperator; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -54,7 +55,7 @@ public FilterableResolver(ObjectMapper objectMapper, boolean findOne, boolean re .addMatchRule(this::findById, FilterMatcher.MatchStrategy.STRICT) .matcher() .capture(ID_GROUP) - .compareValues("_id", ValueComparisonOperator.EQ, JsonType.STRING); + .compareValues("_id", ValueComparisonOperator.EQ, JsonType.DOCUMENT_ID); // NOTE - can only do eq ops on fields until SAI changes matchRules @@ -83,8 +84,8 @@ public record FilteringOptions(int limit, String pagingState, int pageSize) {} private ReadOperation findById(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> @@ -118,8 +119,8 @@ private ReadOperation findNoFilter(CommandContext commandContext, CaptureGroups< private ReadOperation findDynamic(CommandContext commandContext, CaptureGroups captures) { List filters = new ArrayList<>(); - final CaptureGroup idGroup = - (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); + final CaptureGroup idGroup = + (CaptureGroup) captures.getGroupIfPresent(ID_GROUP); if (idGroup != null) { idGroup.consumeAllCaptures( expression -> diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index 77fd4f5217..da86c5a76d 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -3,6 +3,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; @@ -22,7 +24,9 @@ * */ public interface DocumentId { - JsonType type(); + int typeId(); + + Object value(); default JsonNode asJson(ObjectMapper mapper) { return asJson(mapper.getNodeFactory()); @@ -30,6 +34,16 @@ default JsonNode asJson(ObjectMapper mapper) { JsonNode asJson(JsonNodeFactory nodeFactory); + // This mapped integers are used in keys in the storage layer. Don't change the values in this + // map. + BiMap dataTypeMapper = + new ImmutableBiMap.Builder() + .put(JsonType.STRING, 1) + .put(JsonType.NUMBER, 2) + .put(JsonType.BOOLEAN, 3) + .put(JsonType.NULL, 4) + .build(); + static DocumentId fromJson(JsonNode node) { switch (node.getNodeType()) { case BOOLEAN -> { @@ -52,6 +66,28 @@ static DocumentId fromJson(JsonNode node) { ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), node.getNodeType())); } + static DocumentId fromDatabase(int typeId, String documentIdAsText) { + switch (dataTypeMapper.inverse().get(typeId)) { + case BOOLEAN -> { + return fromBoolean(Boolean.valueOf(documentIdAsText)); + } + case NULL -> { + return fromNull(); + } + case NUMBER -> { + return fromNumber(new BigDecimal(documentIdAsText)); + } + case STRING -> { + return fromString(documentIdAsText); + } + } + throw new DocsException( + ErrorCode.SHRED_BAD_DOCID_TYPE, + String.format( + "%s: Document Id must be a JSON String(1), Number(2), Boolean(3) or NULL(4) instead got %s", + ErrorCode.SHRED_BAD_DOCID_TYPE.getMessage(), typeId)); + } + static DocumentId fromBoolean(boolean key) { return BooleanId.valueOf(key); } @@ -85,8 +121,13 @@ static DocumentId fromUUID(UUID uuid) { record StringId(String key) implements DocumentId { @Override - public JsonType type() { - return JsonType.STRING; + public int typeId() { + return dataTypeMapper.get(JsonType.STRING); + } + + @Override + public Object value() { + return key(); } @Override @@ -102,8 +143,13 @@ public String toString() { record NumberId(BigDecimal key) implements DocumentId { @Override - public JsonType type() { - return JsonType.NUMBER; + public int typeId() { + return dataTypeMapper.get(JsonType.NUMBER); + } + + @Override + public Object value() { + return key(); } @Override @@ -126,8 +172,13 @@ public static BooleanId valueOf(boolean b) { } @Override - public JsonType type() { - return JsonType.BOOLEAN; + public int typeId() { + return dataTypeMapper.get(JsonType.BOOLEAN); + } + + @Override + public Object value() { + return key(); } @Override @@ -145,8 +196,13 @@ record NullId() implements DocumentId { public static final NullId NULL = new NullId(); @Override - public JsonType type() { - return JsonType.NULL; + public Object value() { + return null; + } + + @Override + public int typeId() { + return dataTypeMapper.get(JsonType.NULL); } @Override @@ -156,7 +212,7 @@ public JsonNode asJson(JsonNodeFactory nodeFactory) { @Override public String toString() { - return "null"; + return null; } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java index f3a556421a..82b910b49a 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java @@ -68,6 +68,50 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithNumberId() { + String json = + """ + { + "insertOne": { + "document": { + "_id": 4, + "username": "user4" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds[0]", is(4)); + + json = + """ + { + "find": { + "filter" : {"_id" : 4} + } + } + """; + String expected = "{\"_id\": 4, \"username\":\"user4\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = @@ -163,6 +207,73 @@ public void insertDocument() { .body("data.docs[0]", jsonEquals(expected)); } + @Test + public void insertDocumentWithDifferentTypes() { + String json = + """ + { + "insertMany": { + "documents": [{ + "_id": "5", + "username": "user_id_5" + }, + { + "_id": 5, + "username": "user_id_5_number" + }] + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.insertedIds", contains("5", 5)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + String expected = "{\"_id\":\"5\", \"username\":\"user_id_5\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + + json = + """ + { + "find": { + "filter" : {"_id" : "5"} + } + } + """; + expected = "{\"_id\":5, \"username\":\"user_id_5_number\"}"; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + @Test public void emptyDocument() { String json = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java index d035199667..a401139025 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/CreateCollectionOperationTest.java @@ -53,7 +53,7 @@ private List getAllQueryString(String database, String collection) { List queries = new ArrayList<>(); String create = "CREATE TABLE IF NOT EXISTS %s.%s (" - + " key text," + + " key tuple," + " tx_id timeuuid, " + " doc_json text," + " doc_properties map," diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java index 6df477f037..82e31a703d 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java @@ -15,6 +15,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -43,30 +45,45 @@ public void deleteWithId() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -95,7 +112,10 @@ public void deleteWithIdNoData() throws Exception { .formatted(KEYSPACE_NAME, COLLECTION_NAME); UUID tx_id = UUID.randomUUID(); ValidatingStargateBridge.QueryAssert readAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( @@ -112,7 +132,9 @@ public void deleteWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -151,12 +173,22 @@ public void deleteWithDynamic() throws Exception { .setName("tx_id") .setType(TypeSpecs.UUID) .build())) - .returning(List.of(List.of(Values.of("doc1"), Values.of(tx_id)))); + .returning( + List.of( + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(tx_id)))); String collectionDeleteCql = "DELETE FROM \"%s\".\"%s\" WHERE key = ? IF tx_id = ?" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert deleteAssert = - withQuery(collectionDeleteCql, Values.of("doc1"), Values.of(tx_id)) + withQuery( + collectionDeleteCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1"))), + Values.of(tx_id)) .returning(List.of(List.of(Values.of(true)))); FindOperation findOperation = diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java index 2a9c4847e5..43809a324b 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/FindOperationTest.java @@ -14,6 +14,8 @@ import io.stargate.sgv3.docsapi.service.bridge.AbstractValidatingStargateBridgeTest; import io.stargate.sgv3.docsapi.service.bridge.ValidatingStargateBridge; import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; +import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.UUID; import java.util.function.Supplier; @@ -62,7 +64,7 @@ public void findAll() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -74,8 +76,18 @@ public void findAll() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)), - List.of(Values.of("doc2"), Values.of(UUID.randomUUID()), Values.of(doc2)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)), + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc2"))), + Values.of(UUID.randomUUID()), + Values.of(doc2)))); FindOperation findOperation = new FindOperation(commandContext, List.of(), null, 2, 2, true, objectMapper); final Supplier execute = @@ -102,13 +114,16 @@ public void findWithId() throws Exception { } """; ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -120,11 +135,18 @@ public void findWithId() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -147,13 +169,16 @@ public void findWithIdNoData() throws Exception { "SELECT key, tx_id, doc_json FROM \"%s\".\"%s\" WHERE key = ? LIMIT 1" .formatted(KEYSPACE_NAME, COLLECTION_NAME); ValidatingStargateBridge.QueryAssert candidatesAssert = - withQuery(collectionReadCql, Values.of("doc1")) + withQuery( + collectionReadCql, + Values.of( + CustomValueSerializers.getDocumentIdValue(DocumentId.fromString("doc1")))) .withPageSize(1) .withColumnSpec( List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -167,7 +192,9 @@ public void findWithIdNoData() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "doc1")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("doc1"))), null, 1, 1, @@ -203,7 +230,7 @@ public void findWithDynamic() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -215,7 +242,12 @@ public void findWithDynamic() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -258,7 +290,7 @@ public void findWithBooleanFilter() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") @@ -270,7 +302,12 @@ public void findWithBooleanFilter() throws Exception { .build())) .returning( List.of( - List.of(Values.of("doc1"), Values.of(UUID.randomUUID()), Values.of(doc1)))); + List.of( + Values.of( + CustomValueSerializers.getDocumentIdValue( + DocumentId.fromString("doc1"))), + Values.of(UUID.randomUUID()), + Values.of(doc1)))); FindOperation findOperation = new FindOperation( commandContext, @@ -312,7 +349,7 @@ public void findWithNoResult() throws Exception { List.of( QueryOuterClass.ColumnSpec.newBuilder() .setName("key") - .setType(TypeSpecs.VARCHAR) + .setType(TypeSpecs.tuple(TypeSpecs.TINYINT, TypeSpecs.VARCHAR)) .build(), QueryOuterClass.ColumnSpec.newBuilder() .setName("tx_id") diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java index 6de743ba9e..a84c1c7ff4 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/DeleteOneCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.DeleteOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation findOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java index c72b02fcd0..14c65d6560 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindCommandResolverTest.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.bridge.config.DocumentConfig; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -43,7 +44,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, documentConfig.maxLimit(), documentConfig.defaultPageSize(), diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java index 124e6467ec..0367acc6c3 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneAndUpdateResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -51,7 +52,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java index b1453c826e..0d8e168eeb 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/FindOneCommandResolverTest.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.api.model.command.impl.FindOneCommand; import io.stargate.sgv3.docsapi.service.operation.model.Operation; import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import javax.inject.Inject; import org.junit.jupiter.api.Nested; @@ -46,7 +47,9 @@ public void idFilterCondition() throws Exception { FindOperation expected = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java index b40c521c01..d7457810ba 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java @@ -14,6 +14,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.testutil.DocumentUpdaterUtils; import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; import java.util.List; @@ -50,7 +51,9 @@ public void idFilterConditionBsonType() throws Exception { ReadOperation readOperation = new FindOperation( commandContext, - List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + List.of( + new FindOperation.IDFilter( + FindOperation.IDFilter.Operator.EQ, DocumentId.fromString("id"))), null, 1, 1, From a2a6ccad192ba75908b178f57addd993a3201966 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 11:33:50 -0500 Subject: [PATCH 07/11] IT fix for number _id field --- .../io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java index 82b910b49a..0a6c1a01c9 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/InsertIntegrationTest.java @@ -258,7 +258,7 @@ public void insertDocumentWithDifferentTypes() { """ { "find": { - "filter" : {"_id" : "5"} + "filter" : {"_id" : 5} } } """; From 0fe9e159f1bcf578d5e4d9e21a450d1072948d3f Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Wed, 1 Feb 2023 21:21:35 -0500 Subject: [PATCH 08/11] Changes as per review commants. PR #66 --- .../model/command/clause/filter/JsonType.java | 1 + .../config/constants/DocumentConstants.java | 30 +++++++++++++++++++ .../operation/model/impl/DeleteOperation.java | 7 +++-- .../model/impl/DeleteOperationPage.java | 3 +- .../operation/model/impl/InsertOperation.java | 7 +++-- .../model/impl/InsertOperationPage.java | 3 +- .../model/impl/UpdateOperationPage.java | 5 ++-- .../service/shredding/model/DocumentId.java | 28 ++++++----------- .../model/impl/DeleteOperationTest.java | 12 ++++---- 9 files changed, 61 insertions(+), 35 deletions(-) diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java index f1b7089236..b4860e249c 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/clause/filter/JsonType.java @@ -8,5 +8,6 @@ public enum JsonType { NULL, SUB_DOC, ARRAY, + // DOCUMENT_ID represent the _id field type which is union of String, Number, Boolean and Null DOCUMENT_ID } diff --git a/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java b/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java index 8b6fdbf5bc..d0855c5966 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java +++ b/src/main/java/io/stargate/sgv3/docsapi/config/constants/DocumentConstants.java @@ -1,9 +1,39 @@ package io.stargate.sgv3.docsapi.config.constants; +import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; +import java.util.Map; + public interface DocumentConstants { /** Names of "special" fields in Documents */ interface Fields { /** Primary key for Documents stored; has special handling for many operations. */ String DOC_ID = "_id"; } + + interface KeyTypeId { + /** + * Type id are used in key stored in database representing the datatype of the id field. These + * values should not be changed once data is stored in the DB. + */ + int TYPE_ID_STRING = 1; + + int TYPE_ID_NUMBER = 2; + int TYPE_ID_BOOLEAN = 3; + int TYPE_ID_NULL = 4; + + Map keyIDMap = + Map.of( + TYPE_ID_STRING, + JsonType.STRING, + TYPE_ID_NUMBER, + JsonType.NUMBER, + TYPE_ID_BOOLEAN, + JsonType.BOOLEAN, + TYPE_ID_NULL, + JsonType.NULL); + + static JsonType getJsonType(int typeId) { + return keyIDMap.get(typeId); + } + } } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java index 008e18df4a..f4a1bfe977 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperation.java @@ -11,6 +11,7 @@ import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation.FindResponse; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.function.Supplier; @@ -24,7 +25,7 @@ public record DeleteOperation(CommandContext commandContext, ReadOperation readO public Uni> execute(QueryExecutor queryExecutor) { Uni docsToDelete = readOperation().getDocuments(queryExecutor); final QueryOuterClass.Query delete = buildDeleteQuery(); - final Uni> ids = + final Uni> ids = docsToDelete .onItem() .transformToMulti( @@ -63,7 +64,7 @@ private QueryOuterClass.Query buildDeleteQuery() { * @param doc * @return */ - private static Uni deleteDocument( + private static Uni deleteDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, ReadDocument doc) { query = bindDeleteQuery(query, doc); return queryExecutor @@ -72,7 +73,7 @@ private static Uni deleteDocument( .transformToUni( result -> { if (result.getRows(0).getValues(0).getBoolean()) { - return Uni.createFrom().item(doc.id().value()); + return Uni.createFrom().item(doc.id()); } else { return Uni.createFrom().nothing(); } diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java index 6c4f122e0c..155bed5ac5 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationPage.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -11,7 +12,7 @@ * * @param deletedIds - document ids deleted */ -public record DeleteOperationPage(List deletedIds) implements Supplier { +public record DeleteOperationPage(List deletedIds) implements Supplier { @Override public CommandResult get() { return new CommandResult(Map.of(CommandStatus.DELETED_IDS, deletedIds)); diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java index 838d4539cb..1ca7b7fd36 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperation.java @@ -9,6 +9,7 @@ import io.stargate.sgv3.docsapi.service.bridge.executor.QueryExecutor; import io.stargate.sgv3.docsapi.service.bridge.serializer.CustomValueSerializers; import io.stargate.sgv3.docsapi.service.operation.model.ModifyOperation; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import java.util.List; import java.util.function.Supplier; @@ -26,7 +27,7 @@ public InsertOperation(CommandContext commandContext, WritableShreddedDocument d @Override public Uni> execute(QueryExecutor queryExecutor) { QueryOuterClass.Query query = buildInsertQuery(); - final Uni> ids = + final Uni> ids = Multi.createFrom() .items(documents.stream()) .onItem() @@ -36,10 +37,10 @@ public Uni> execute(QueryExecutor queryExecutor) { return ids.onItem().transform(insertedIds -> new InsertOperationPage(insertedIds, documents)); } - private static Uni insertDocument( + private static Uni insertDocument( QueryExecutor queryExecutor, QueryOuterClass.Query query, WritableShreddedDocument doc) { query = bindInsertValues(query, doc); - return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id().value()); + return queryExecutor.executeWrite(query).onItem().transform(result -> doc.id()); } private QueryOuterClass.Query buildInsertQuery() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java index 474b6cb558..c4a84fa75f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/InsertOperationPage.java @@ -2,6 +2,7 @@ import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import io.stargate.sgv3.docsapi.service.shredding.model.WritableShreddedDocument; import java.util.List; import java.util.Map; @@ -12,7 +13,7 @@ * what change. */ public record InsertOperationPage( - List insertedIds, List insertedDocs) + List insertedIds, List insertedDocs) implements Supplier { @Override public CommandResult get() { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java index 3d46e2db65..5f33a6c6a9 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/operation/model/impl/UpdateOperationPage.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.JsonNode; import io.stargate.sgv3.docsapi.api.model.command.CommandResult; import io.stargate.sgv3.docsapi.api.model.command.CommandStatus; +import io.stargate.sgv3.docsapi.service.shredding.model.DocumentId; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,11 +14,11 @@ public record UpdateOperationPage( implements Supplier { @Override public CommandResult get() { - List updatedIds = new ArrayList<>(updatedDocuments().size()); + List updatedIds = new ArrayList<>(updatedDocuments().size()); List updatedDocs = new ArrayList<>(updatedDocuments().size()); updatedDocuments.forEach( update -> { - updatedIds.add(update.id().value()); + updatedIds.add(update.id()); updatedDocs.add(update.document()); }); if (returnDocs) { diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index da86c5a76d..42b883a6b1 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -1,11 +1,10 @@ package io.stargate.sgv3.docsapi.service.shredding.model; +import com.fasterxml.jackson.annotation.JsonValue; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; -import io.stargate.sgv3.docsapi.api.model.command.clause.filter.JsonType; +import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; import java.math.BigDecimal; @@ -26,6 +25,7 @@ public interface DocumentId { int typeId(); + @JsonValue Object value(); default JsonNode asJson(ObjectMapper mapper) { @@ -34,16 +34,6 @@ default JsonNode asJson(ObjectMapper mapper) { JsonNode asJson(JsonNodeFactory nodeFactory); - // This mapped integers are used in keys in the storage layer. Don't change the values in this - // map. - BiMap dataTypeMapper = - new ImmutableBiMap.Builder() - .put(JsonType.STRING, 1) - .put(JsonType.NUMBER, 2) - .put(JsonType.BOOLEAN, 3) - .put(JsonType.NULL, 4) - .build(); - static DocumentId fromJson(JsonNode node) { switch (node.getNodeType()) { case BOOLEAN -> { @@ -67,7 +57,7 @@ static DocumentId fromJson(JsonNode node) { } static DocumentId fromDatabase(int typeId, String documentIdAsText) { - switch (dataTypeMapper.inverse().get(typeId)) { + switch (DocumentConstants.KeyTypeId.getJsonType(typeId)) { case BOOLEAN -> { return fromBoolean(Boolean.valueOf(documentIdAsText)); } @@ -122,7 +112,7 @@ static DocumentId fromUUID(UUID uuid) { record StringId(String key) implements DocumentId { @Override public int typeId() { - return dataTypeMapper.get(JsonType.STRING); + return DocumentConstants.KeyTypeId.TYPE_ID_STRING; } @Override @@ -144,7 +134,7 @@ public String toString() { record NumberId(BigDecimal key) implements DocumentId { @Override public int typeId() { - return dataTypeMapper.get(JsonType.NUMBER); + return DocumentConstants.KeyTypeId.TYPE_ID_NUMBER; } @Override @@ -173,7 +163,7 @@ public static BooleanId valueOf(boolean b) { @Override public int typeId() { - return dataTypeMapper.get(JsonType.BOOLEAN); + return DocumentConstants.KeyTypeId.TYPE_ID_BOOLEAN; } @Override @@ -202,7 +192,7 @@ public Object value() { @Override public int typeId() { - return dataTypeMapper.get(JsonType.NULL); + return DocumentConstants.KeyTypeId.TYPE_ID_NULL; } @Override @@ -212,7 +202,7 @@ public JsonNode asJson(JsonNodeFactory nodeFactory) { @Override public String toString() { - return null; + return "null"; } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java index 82e31a703d..0e3ed9d6bb 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/service/operation/model/impl/DeleteOperationTest.java @@ -98,10 +98,10 @@ public void deleteWithId() throws Exception { .satisfies( commandResult -> { assertThat(result.status().get(CommandStatus.DELETED_IDS)).isNotNull(); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) .hasSize(1); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) - .contains("doc1"); + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + .contains(DocumentId.fromString("doc1")); }); } @@ -211,10 +211,10 @@ public void deleteWithDynamic() throws Exception { .satisfies( commandResult -> { assertThat(result.status().get(CommandStatus.DELETED_IDS)).isNotNull(); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) .hasSize(1); - assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) - .contains("doc1"); + assertThat((List) result.status().get(CommandStatus.DELETED_IDS)) + .contains(DocumentId.fromString("doc1")); }); } From 1df0fb9766dceb3369c4d3aa8f99e0eeb600d2ed Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:21:00 -0500 Subject: [PATCH 09/11] Document id records annotate @RegisterForReflection --- .../sgv3/docsapi/service/shredding/model/DocumentId.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index 42b883a6b1..2af77f2f0a 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import io.quarkus.runtime.annotations.RegisterForReflection; import io.stargate.sgv3.docsapi.config.constants.DocumentConstants; import io.stargate.sgv3.docsapi.exception.DocsException; import io.stargate.sgv3.docsapi.exception.ErrorCode; @@ -109,6 +110,7 @@ static DocumentId fromUUID(UUID uuid) { /********************************************************************** */ + @RegisterForReflection record StringId(String key) implements DocumentId { @Override public int typeId() { @@ -131,6 +133,7 @@ public String toString() { } } + @RegisterForReflection record NumberId(BigDecimal key) implements DocumentId { @Override public int typeId() { @@ -153,6 +156,7 @@ public String toString() { } } + @RegisterForReflection record BooleanId(boolean key) implements DocumentId { private static final BooleanId FALSE = new BooleanId(false); private static final BooleanId TRUE = new BooleanId(true); @@ -182,6 +186,7 @@ public String toString() { } } + @RegisterForReflection record NullId() implements DocumentId { public static final NullId NULL = new NullId(); From 7dd3e8fe942baffe9799e0b8c42645e010369301 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:43:37 -0500 Subject: [PATCH 10/11] Document id records annotate @RegisterForReflection --- .../sgv3/docsapi/service/shredding/model/DocumentId.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index 2af77f2f0a..69b55242cc 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -23,6 +23,7 @@ *
  • null * */ +@RegisterForReflection public interface DocumentId { int typeId(); From c412424a891282f81eaa6813399327ac251bc07d Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 2 Feb 2023 11:51:02 -0500 Subject: [PATCH 11/11] Document id annotate @RegisterForReflection at top level --- .../sgv3/docsapi/service/shredding/model/DocumentId.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java index 69b55242cc..b0b6c083d6 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java +++ b/src/main/java/io/stargate/sgv3/docsapi/service/shredding/model/DocumentId.java @@ -111,7 +111,6 @@ static DocumentId fromUUID(UUID uuid) { /********************************************************************** */ - @RegisterForReflection record StringId(String key) implements DocumentId { @Override public int typeId() { @@ -134,7 +133,6 @@ public String toString() { } } - @RegisterForReflection record NumberId(BigDecimal key) implements DocumentId { @Override public int typeId() { @@ -157,7 +155,6 @@ public String toString() { } } - @RegisterForReflection record BooleanId(boolean key) implements DocumentId { private static final BooleanId FALSE = new BooleanId(false); private static final BooleanId TRUE = new BooleanId(true); @@ -187,7 +184,6 @@ public String toString() { } } - @RegisterForReflection record NullId() implements DocumentId { public static final NullId NULL = new NullId();