diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/CollectionSortClauseBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/CollectionSortClauseBuilder.java index f0577b5bf3..8286ec6b0f 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/CollectionSortClauseBuilder.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/CollectionSortClauseBuilder.java @@ -36,21 +36,28 @@ public SortClause buildClauseFromDefinition(ObjectNode sortNode) { JsonNode lexicalNode = sortNode.get(DocumentConstants.Fields.LEXICAL_CONTENT_FIELD); if (lexicalNode != null) { - // We can also check if lexical sort supported by the collection: - if (!schema.lexicalConfig().enabled()) { - throw SchemaException.Code.LEXICAL_NOT_ENABLED_FOR_COLLECTION.get(errVars(schema)); - } - if (sortNode.size() > 1) { - throw ErrorCodeV1.INVALID_SORT_CLAUSE.toApiException( - "if sorting by '%s' no other sort expressions allowed", - DocumentConstants.Fields.LEXICAL_CONTENT_FIELD); - } - if (!lexicalNode.isTextual()) { - throw ErrorCodeV1.INVALID_SORT_CLAUSE.toApiException( - "if sorting by '%s' value must be String, not %s", - DocumentConstants.Fields.LEXICAL_CONTENT_FIELD, JsonUtil.nodeTypeAsString(lexicalNode)); + // Typical we need a String, but "old" 1/-1 still allowed: so bail out + // if we got a Number + if (lexicalNode.isNumber()) { + ; // do nothing, yet, fall-through to next block + } else { + // We can also check if lexical sort supported by the collection: + if (!schema.lexicalConfig().enabled()) { + throw SchemaException.Code.LEXICAL_NOT_ENABLED_FOR_COLLECTION.get(errVars(schema)); + } + if (sortNode.size() > 1) { + throw ErrorCodeV1.INVALID_SORT_CLAUSE.toApiException( + "if sorting by '%s' no other sort expressions allowed", + DocumentConstants.Fields.LEXICAL_CONTENT_FIELD); + } + if (!lexicalNode.isTextual()) { + throw ErrorCodeV1.INVALID_SORT_CLAUSE.toApiException( + "if sorting by '%s' value must be String, not %s", + DocumentConstants.Fields.LEXICAL_CONTENT_FIELD, + JsonUtil.nodeTypeAsString(lexicalNode)); + } + return SortClause.immutable(SortExpression.collectionLexicalSort(lexicalNode.textValue())); } - return SortClause.immutable(SortExpression.collectionLexicalSort(lexicalNode.textValue())); } JsonNode vectorNode = sortNode.get(DocumentConstants.Fields.VECTOR_EMBEDDING_FIELD); if (vectorNode != null) { diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/operation/collections/FindCollectionOperation.java b/src/main/java/io/stargate/sgv2/jsonapi/service/operation/collections/FindCollectionOperation.java index ab07d2f993..8406da50b1 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/operation/collections/FindCollectionOperation.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/operation/collections/FindCollectionOperation.java @@ -402,7 +402,6 @@ public Uni getDocuments( QueryExecutor queryExecutor, String pageState, IDCollectionFilter additionalIdFilter) { - // ensure we pass failure down if read type is not DOCUMENT or KEY // COUNT is not supported switch (readType) { diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionWithLexicalIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionWithLexicalIntegrationTest.java index 730d015dff..623eeb56c3 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionWithLexicalIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindCollectionWithLexicalIntegrationTest.java @@ -158,11 +158,34 @@ void findManyWithLexicalAndOtherFilter() { .body("data.documents", hasSize(1)) .body("data.documents[0]._id", is("lexical-4")); } + + // [data-api#2109] Non-lexical sort on $lexical column should also work + @Test + void findManyWithNonLexicalSort() { + givenHeadersPostJsonThenOkNoErrors( + keyspaceName, + COLLECTION_WITH_LEXICAL, + """ + { + "find": { + "projection": {"$lexical": 1 }, + "sort" : {"$lexical": 1 } + } + } + """) + .body("$", responseIsFindSuccess()) + .body("data.documents", hasSize(5)) + .body("data.documents[0]._id", is("lexical-4")) + .body("data.documents[1]._id", is("lexical-3")) + .body("data.documents[2]._id", is("lexical-5")) + .body("data.documents[3]._id", is("lexical-2")) + .body("data.documents[4]._id", is("lexical-1")); + } } @DisabledIfSystemProperty(named = TEST_PROP_LEXICAL_DISABLED, matches = "true") @Nested - @Order(2) + @Order(3) class HappyCasesFindOne { @Test void findOneWithLexicalSortBiking() { @@ -221,7 +244,7 @@ void findOneWithOnlyLexicalFilter() { @DisabledIfSystemProperty(named = TEST_PROP_LEXICAL_DISABLED, matches = "true") @Nested - @Order(3) + @Order(4) class FailingCasesFindMany { @Test void failSortIfLexicalDisabledForCollection() { @@ -269,7 +292,7 @@ void failForBadLexicalSortValueType() { """ { "find": { - "sort" : {"$lexical": -1 } + "sort" : {"$lexical": false } } } """) @@ -277,7 +300,7 @@ void failForBadLexicalSortValueType() { .body("errors[0].errorCode", is("INVALID_SORT_CLAUSE")) .body( "errors[0].message", - containsString("if sorting by '$lexical' value must be String, not Number")); + containsString("if sorting by '$lexical' value must be String, not Boolean")); } @Test