Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public enum Code implements ErrorCode<RequestException> {

INVALID_CREATE_COLLECTION_FIELD,
INVALID_RERANK_OVERRIDE,
MISSING_HYBRID_SORT,
MISSING_RERANK_QUERY_TEXT,

REQUEST_NOT_JSON,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -329,12 +333,19 @@ private TaskGroupAndDeferrables<IntermediateCollectionReadTask, CollectionSchema
taskGroup.add(bm25Read);
}

// always a vector or vectorize read
// vector or vectorize read (if present)
var vectorReadAndDeferrables = buildVectorRead(deferredVectorReadAction);
taskGroup.add(vectorReadAndDeferrables.task());
if (vectorReadAndDeferrables != null) {
taskGroup.add(vectorReadAndDeferrables.task());
}

var deferrables =
vectorReadAndDeferrables != null
? vectorReadAndDeferrables.deferrables()
: List.<Deferrable>of();

// 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) {
Expand Down Expand Up @@ -369,6 +380,12 @@ private IntermediateCollectionReadTask buildBm25Read(DeferredCommandResultAction
private TaskAndDeferrables<IntermediateCollectionReadTask, CollectionSchemaObject>
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;
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/errors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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 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'.

- scope:
code: INVALID_CREATE_COLLECTION_FIELD
title: Invalid field(s) for createCollection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down