Repository navigation
plan findAndRerank before building the operation - #2650
Hazel-Datastax wants to merge 17 commits into
Conversation
The sort parser copied the $hybrid text into vectorizeSort and lexicalSort,
so {"$hybrid": "text"} parsed to the same values as a $hybrid object with
both keys. The builder then used the LEXICAL metrics flag to tell an
explicit $lexical from the $hybrid text (#2598).
FindAndRerankSort now records rerankLegMode, MULTI_LEG when the sort uses
$hybrid, and the $hybrid text in hybridSort. The parser builds it with the
constructor, and the constructor only checks for null. vectorizeText() and
lexicalText() give the text each read uses. The builder decides an explicit
$lexical from lexicalSort, not from the metrics flag, so
CommandFeatures.contains() is no longer used and is removed.
normalizedText() already trims, so the parser no longer calls trim() first.
Should not change behaviour. The MCP tool schema for the sort lists the
record fields, so it also shows rerankLegMode and hybridSort.
Three findAndRerank sorts fail with UNEXPECTED_SERVER_ERROR on main, from the IllegalArgumentException in the builder when there is no vector or vectorize read (#2577). These SORT errors replace it: - MISSING_VECTOR_OR_VECTORIZE_IN_HYBRID_SORT: a $hybrid sort with a $lexical value but no $vectorize or $vector value. - MISSING_HYBRID_SORT_VALUE: a $hybrid sort with no value. - UNSUPPORTED_FIND_AND_RERANK_SORT: no sort, or an empty sort. Nothing throws them yet.
FindAndRerankPlan holds the steps a findAndRerank command runs: vectorize the sort text when there is a VectorizeStep, read with each RerankLeg, then rerank what the legs read with the RerankStep. It is only data, and checks its invariants when it is built. A RerankLeg keeps the vector it is given, as FindAndRerankSort does, and compares it by value. RerankLegType has VECTOR_LEG, LEXICAL_LEG and FILTER_LEG. FILTER_LEG is for the read with only the filter in #2603, the planner does not make one yet. FindAndRerankPlanner makes the plan from the command and its context. Its checks are the builder's checks, copied in the same order and changed only to take the sort, the command or the context as arguments. checkSortSupported() no longer also tests isLexicalSort(), an explicit $lexical always has lexical text. A $hybrid sort always gets a VECTOR_LEG, and a LEXICAL_LEG when it has lexical text and the collection has a lexical index. A sort without a vector or vectorize value, or no sort, fails with the errors from the previous commit. HybridLimitsDeserializer exposes the field names it uses in its errors, so the planner does not repeat them. TestConstants gets the schema object and the reranking providers config that the new tests use. Nothing calls the planner yet, the next commit does.
FindAndRerankCommandResolver makes the plan with FindAndRerankPlanner and gives it to the builder with withPlan(). The builder no longer checks the command, the planner does. It takes from the plan what it used to work out from the command: the legs and their read limits, the vectorize step, the rerank service, query, passage, projection and limit, and includeScores and includeSortVector. How the builder puts the tasks together does not change: the same read tasks and deferred results, the embedding task when there is text to vectorize, and the reranking task. The three sorts in #2577 now fail with the SORT errors instead of UNEXPECTED_SERVER_ERROR, after the same checks as before. The reranking provider is now created after the rerank query, rerankOn and projection checks, not before them. This only changes which error comes first when the provider cannot be created. FindAndRerankOperationBuilderTest makes the plan before it builds, as the resolver does, and FindAndRerankPlannerTest also builds the operation from each plan it makes.
📈 Unit Test Coverage Delta vs Main Branch
|
Unit Test Coverage Report
|
📉 Integration Test Coverage Delta vs Main Branch (dse69-it)
|
Integration Test Coverage Report (dse69-it)
|
📉 Integration Test Coverage Delta vs Main Branch (hcd-it)
|
Integration Test Coverage Report (hcd-it)
|
# Conflicts: # src/main/java/io/stargate/sgv2/jsonapi/api/model/command/clause/sort/FindAndRerankSort.java
| checkSortSupported(sort, schemaObject); | ||
| validateHybridLimits(command, operationsConfig); | ||
| var rerankServiceDef = resolveRerankServiceDef(command, commandContext); | ||
| var rerankingQuery = RerankingQuery.create(command); | ||
| var passageLocator = passageLocator(command); | ||
| var projector = command.buildProjector(); |
There was a problem hiding this comment.
These checks moved here from FindAndRerankOperationBuilder.build() and run in the same order.
| switch (sort.rerankLegMode()) { | ||
| // TODO: GH #2602, GH #2603 - plan single leg sorts, until then only a command with no | ||
| // sort is SINGLE_LEG | ||
| case SINGLE_LEG -> throw SortException.Code.UNSUPPORTED_FIND_AND_RERANK_SORT.get(); | ||
| case MULTI_LEG -> multiLegs(command, schemaObject); |
There was a problem hiding this comment.
Before this PR, a command with no sort (or {}, null) and a $hybrid sort with no vector or vectorize failed later in buildVectorRead() with UNEXPECTED_SERVER_ERROR (#2577).
Now SINGLE_LEG fails with UNSUPPORTED_FIND_AND_RERANK_SORT, and multiLegs() fails with MISSING_HYBRID_SORT_VALUE or MISSING_VECTOR_OR_VECTORIZE_IN_HYBRID_SORT.
| private static boolean isExplicitLexicalSort(FindAndRerankSort sort) { | ||
| return sort.lexicalSort() != null; | ||
| } |
There was a problem hiding this comment.
This used to read the LEXICAL metrics flag with CommandFeatures.contains() (#2598).
Now the $hybrid text stays in hybridSort, so lexicalSort() is only set by an explicit $lexical. The result is the same as before.
| }) | ||
| public record FindAndRerankSort( | ||
| @JsonIgnore RerankLegMode rerankLegMode, | ||
| @JsonIgnore String hybridSort, |
There was a problem hiding this comment.
The $hybrid text used to be copied into vectorizeSort and lexicalSort. It now stays here, so the planner can tell $hybrid text from an explicit $lexical.
vectorizeText() and lexicalText() return the text to use for both forms.
| * <p>The operation does not expose its tasks, so the tests check the providers the builder creates | ||
| * and the hybridLimits it sets on the command context. | ||
| */ | ||
| class FindAndRerankOperationBuilderTest { |
There was a problem hiding this comment.
The checks that moved to the planner are now tested in FindAndRerankPlannerTest.
This test builds from plans made in the test, so it no longer needs Quarkus. The operation does not expose its read tasks, so the test checks the providers the builder creates and the hybridLimits it sets on the command context.
| public FindAndRerankOperationBuilder withPlan(FindAndRerankPlan plan) { | ||
| this.plan = plan; | ||
| return this; | ||
| } |
There was a problem hiding this comment.
The builder now gets the plan here. It still takes the command for the filter and for the #1961 page size hack in readTasks().
| switch (sort.rerankLegMode()) { | ||
| // TODO: GH #2602, GH #2603 - plan single leg sorts, until then only a command with no | ||
| // sort is SINGLE_LEG | ||
| case SINGLE_LEG -> throw SortException.Code.UNSUPPORTED_FIND_AND_RERANK_SORT.get(); |
There was a problem hiding this comment.
Could we add API regression tests for #2577, plus the empty hybrid and missing sort cases? The planner tests cover the exceptions, but these new error codes are part of the client response.
What this PR does:
First of the two PRs split out of #2637. The resolver now gets a
FindAndRerankPlanfrom the newFindAndRerankPlannerand passes it toFindAndRerankOperationBuilder.The plan is only data: an optional vectorize step, the legs to read, and the rerank step.
The checks moved from the builder to the planner and run in the same order, and the builder builds the same tasks from the plan.
The planner no longer uses the metrics flags to decide what to run (#2598), and sorts that failed with UNEXPECTED_SERVER_ERROR now fail with three new SORT errors (#2577).
Which issue(s) this PR fixes:
Fixes #2598
Fixes #2577
Checklist