From bb1bd42d3d1511e3ec93e5a345b88459ee34e3aa Mon Sep 17 00:00:00 2001 From: Stefano Lottini <236640031+sl-at-ibm@users.noreply.github.com> Date: Mon, 7 Sep 2026 18:40:16 +0200 Subject: [PATCH 1/2] tentative fix to allow lexical-only FARR path --- .../jsonapi/exception/RequestException.java | 1 + .../FindAndRerankOperationBuilder.java | 27 +++++++--- src/main/resources/errors.yaml | 8 +++ .../FindAndRerankOperationBuilderTest.java | 53 +++++++++++++++++++ 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/stargate/sgv2/jsonapi/exception/RequestException.java b/src/main/java/io/stargate/sgv2/jsonapi/exception/RequestException.java index d5b8fc9266..bd3fa61728 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/exception/RequestException.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/exception/RequestException.java @@ -55,6 +55,7 @@ public enum Code implements ErrorCode { INVALID_CREATE_COLLECTION_FIELD, INVALID_RERANK_OVERRIDE, + MISSING_HYBRID_SORT, MISSING_RERANK_QUERY_TEXT, REQUEST_NOT_JSON, diff --git a/src/main/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilder.java index 1e40365891..c877c3eeff 100644 --- a/src/main/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilder.java +++ b/src/main/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilder.java @@ -170,6 +170,10 @@ private void checkLimitInBounds(String field, int value, IntConfigWithBounds bou */ private void checkSortSupported() { + if (!isVectorSort() && !isVectorizeSort() && !isLexicalSort()) { + throw RequestException.Code.MISSING_HYBRID_SORT.get(); + } + if (isVectorSort() || isVectorizeSort()) { if (!commandContext.schemaObject().vectorConfig().vectorEnabled()) { throw SortException.Code.UNSUPPORTED_VECTOR_SORT_FOR_COLLECTION.get( @@ -329,12 +333,19 @@ private TaskGroupAndDeferrablesof(); // No accumulator, this will be wrapped in an intermediate composite task - return new TaskGroupAndDeferrables<>(taskGroup, null, vectorReadAndDeferrables.deferrables()); + return new TaskGroupAndDeferrables<>(taskGroup, null, deferrables); } private IntermediateCollectionReadTask buildBm25Read(DeferredCommandResultAction deferredAction) { @@ -369,6 +380,12 @@ private IntermediateCollectionReadTask buildBm25Read(DeferredCommandResultAction private TaskAndDeferrables buildVectorRead(DeferredCommandResultAction deferredAction) { + if (!isVectorSort() && !isVectorizeSort()) { + // we can fake it now, the value will be waiting when the rerank command comes to get it + deferredAction.setEmptyMultiDocumentResponse(); + return null; + } + // we can sort with either vectorize OR a BYO vector var sortClause = new SortClause(new ArrayList<>()); DeferredVectorize deferredVectorize = null; @@ -389,12 +406,10 @@ private IntermediateCollectionReadTask buildBm25Read(DeferredCommandResultAction vectorDef.vectorSize(), vectorDef.vectorizeDefinition(), sortClause); - } else if (isVectorSort()) { + } else { sortClause .sortExpressions() .add(SortExpression.collectionVectorSort(command.sortClause().vectorSort())); - } else { - throw new IllegalArgumentException("buildVectorRead() - no vector or vectorize"); } // The intermediate task will set the sort when we give it the deferred vectorize diff --git a/src/main/resources/errors.yaml b/src/main/resources/errors.yaml index 38906b8fe8..cafcadfce4 100644 --- a/src/main/resources/errors.yaml +++ b/src/main/resources/errors.yaml @@ -194,6 +194,14 @@ request-errors: body: |- Unsupported JSON value type for '$hybrid' sub-field: ${errorMessage}. + - scope: + code: MISSING_HYBRID_SORT + title: Hybrid sort is missing + body: |- + The hybrid sort clause must specify at least one sort field: '$vector', '$vectorize', or '$lexical'. + + Resend the command with at least one sort field in '$hybrid'. + - scope: code: INVALID_CREATE_COLLECTION_FIELD title: Invalid field(s) for createCollection diff --git a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilderTest.java b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilderTest.java index 3ca81fc205..1c1fc75cee 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilderTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/service/resolver/FindAndRerankOperationBuilderTest.java @@ -242,6 +242,59 @@ void acceptsBoundaryValues() throws Exception { .build(); } + @Test + void bm25OnlyBuildSucceeds() throws Exception { + var commandContext = commandContext(); + var command = + command( + """ + { + "findAndRerank": { + "sort": { "$hybrid": { "$lexical": "text" } }, + "options": { + "rerankOn": "body", + "rerankQuery": "text" + } + } + } + """); + + var operation = + new FindAndRerankOperationBuilder(commandContext) + .withCommand(command) + .withFindCommandResolver(findCommandResolver) + .build(); + + assertThat(operation).isNotNull(); + } + + @Test + void failsWhenNoSortProvided() throws Exception { + var commandContext = commandContext(); + var command = + command( + """ + { + "findAndRerank": { + "sort": { "$hybrid": {} }, + "options": { + "rerankOn": "body", + "rerankQuery": "text" + } + } + } + """); + + assertThatThrownBy( + () -> + new FindAndRerankOperationBuilder(commandContext) + .withCommand(command) + .withFindCommandResolver(findCommandResolver) + .build()) + .isInstanceOf(RequestException.class) + .hasFieldOrPropertyWithValue("code", RequestException.Code.MISSING_HYBRID_SORT.name()); + } + private FindAndRerankCommand command(String json) throws Exception { return objectMapper.readValue(json, FindAndRerankCommand.class); } From 2b186146b84b331a4c5468a83e19fdcd62d1bb37 Mon Sep 17 00:00:00 2001 From: Stefano Lottini <236640031+sl-at-ibm@users.noreply.github.com> Date: Wed, 9 Sep 2026 17:07:15 +0200 Subject: [PATCH 2/2] update an HCD integration test error-message check --- src/main/resources/errors.yaml | 2 +- .../api/v1/FindAndRerankCollectionIntegrationTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/errors.yaml b/src/main/resources/errors.yaml index cafcadfce4..7515b77c22 100644 --- a/src/main/resources/errors.yaml +++ b/src/main/resources/errors.yaml @@ -198,7 +198,7 @@ request-errors: code: MISSING_HYBRID_SORT title: Hybrid sort is missing body: |- - The hybrid sort clause must specify at least one sort field: '$vector', '$vectorize', or '$lexical'. + The findAndRerank hybrid sort clause must specify at least one sort field: '$vector', '$vectorize', or '$lexical'. Resend the command with at least one sort field in '$hybrid'. diff --git a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindAndRerankCollectionIntegrationTest.java b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindAndRerankCollectionIntegrationTest.java index f9d216d9fc..fce6643023 100644 --- a/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindAndRerankCollectionIntegrationTest.java +++ b/src/test/java/io/stargate/sgv2/jsonapi/api/v1/FindAndRerankCollectionIntegrationTest.java @@ -185,11 +185,11 @@ void failOnEmptyRequest() { givenHeadersPostJsonThen(keyspaceName, collectionName, "{\"findAndRerank\": { } }") .body("$", responseIsError()) - .body("errors[0].errorCode", is(RequestException.Code.MISSING_RERANK_QUERY_TEXT.name())) + .body("errors[0].errorCode", is(RequestException.Code.MISSING_HYBRID_SORT.name())) .body( "errors[0].message", containsString( - "findAndRerank command is missing the text to use as the query with the reranking")); + "The findAndRerank hybrid sort clause must specify at least one sort field")); } private void errorOnNotEnabled(