Skip to content

feat(query): fromId() works in every query entry point (#219) - #242

Merged
s2x merged 1 commit into
mainfrom
feat/219-fromid-in-query
Sep 29, 2026
Merged

s2x merged 1 commit into
mainfrom
feat/219-fromid-in-query

Conversation

@s2x

@s2x s2x commented Sep 29, 2026

Copy link
Copy Markdown
Member

Closes #219.

Summary

ZVecVectorQuery::fromId() existed and was documented, but no entry point could run the query it built:

call before
query(), queryWithReranker(), queryMulti(), groupByQuery() ZVecException: query() with docId not yet implemented
queryVector() upstream: Invalid query: missing query clause for field[embedding]

Only queryById() worked. All of them now work:

$collection->query(ZVecVectorQuery::fromId('embedding', 'doc_1')->setTopk(5));

Why client-side, not in C++

Upstream has no native query-by-document-id: the C++ SearchQuery has no id field, QueryTarget holds only a vector or FTS clause, and the C API has no such function. The Python SDK resolves it client-side too — QueryExecutor.set_query_vector() fetches the document and puts its vector into the native query. Doing it in C++ would be the same two steps behind more plumbing, plus new FFI functions on every platform.

The query object stays reusable

The fetched vector is not written back into the query object. If the document changes, the next call must see the new vector. The test runs one object twice and asserts $q->vector is still [] — a write-back would have passed the first two calls and been wrong afterwards.

Precision, honestly bounded

  • queryVector() writes the vector onto the native handle, so it needs an fp32/fp64 choice. INT8 travels the fp32 path, as it already does in queryById().
  • groupByQuery() only builds a float[] buffer, so it accepts FP32 and INT8 and names the actual type for anything else instead of failing obscurely.
  • FP16 has no setter on the native query handle at all, so query() / queryVector() / groupByQuery() point at queryById(), which does have a half-precision path. A missing document is not removed from the results, matching the Python SDK and queryById().

queryById() unchanged

The fetch-and-resolve logic moved into a private fetchQueryVector() shared by both, so the four error messages live in one place. Both existing queryById tests pass untouched — that is the refactor guarantee.

Two behaviour changes

  • fromId() now rejects an empty $docId at construction, rather than failing later with a "document not found" naming an empty string.
  • Setting both docId and a vector throws Cannot provide both docId and vector. The two test_query_decomposition tests asserted the old "not yet implemented" message and were updated — they are the only existing tests this changes.

Verification

1. query fromId: doc1,doc2,doc3
2. filter: doc3,doc4
3. queryVector fromId: doc1,doc2,doc3
4. reuse: doc1,doc2,doc3
5. query object unchanged: yes
6. reranker: doc1,doc2,doc3
7. group fin: doc3,doc4
7. group tech: doc1,doc2
8. missing document: Document not found: missing
9. empty docId: Document ID must not be empty
10. docId and vector: Cannot provide both docId and vector
11. non-vector field: Vector field 'category' not found in document: doc1

Full suite:

Number of tests : 212               212
Tests skipped   :   0 (  0.0%)
Tests failed    :   0 (  0.0%)
Tests passed    : 210 ( 99.1%)

No C++ change and therefore no rebuild needed. test_dbs/ is left with only .gitignore.

Note

One bug I introduced and caught before committing: both resolveQueryParams() and groupByQuery() overwrite $fieldName from the query object to a plain string early on, so an instanceof ZVecVectorQuery check placed after that never fires. The docId is now captured before the reassignment. Worth knowing if more code is added to those two methods.

Out of scope per the issue: removing the source document from results, FP16 in query() / queryVector() / groupByQuery(), sparse fields as the source, fromId() on ZVecGroupByVectorQuery, and an end-to-end FP64 test — the v0.7.0 schema rejects dense VECTOR_FP64 outright, which is why the FP64 test scripts are marked XFAIL.

🤖 Generated with Claude Code

ZVecVectorQuery::fromId() existed and was documented, but no entry point
could run the query it built:

- query(), queryWithReranker(), queryMulti() and groupByQuery() all threw
  "query() with docId not yet implemented"
- queryVector() passed the handle straight through and upstream rejected it
  with "Invalid query: missing query clause", because the native handle
  carried no vector

Only queryById() worked. All of them now fetch the document, read its
vector and run an ordinary query -- the same client-side approach the
Python SDK takes, since upstream has no native query-by-document-id and no
id-based C API either. Doing it in C++ would be the same two steps with
more plumbing, and it would need new FFI functions on every platform.

The fetched vector is deliberately not written back into the query object,
so it stays reusable: if the document changes, the next call must see the
new vector. A test runs one object twice to pin that down, and asserts
$q->vector is still [].

queryVector() needed the vector written onto the native handle, which
means an fp32/fp64 choice; int8 travels the fp32 path as it already does
in queryById(). groupByQuery() only builds a float[] buffer, so it accepts
fp32 and int8 and names the actual type for anything else rather than
failing obscurely. fp16 has no setter on the native query handle at all,
so query()/queryVector()/groupByQuery() point at queryById(), which does
have a half-precision path.

queryById() is unchanged. The fetch-and-resolve logic moved into a private
fetchQueryVector() shared by both, so the four error messages stay in one
place and both existing queryById tests pass untouched.

Two behaviour changes worth calling out:
- fromId() now rejects an empty $docId at construction instead of failing
  later with a "document not found" that named an empty string.
- Setting both docId and a vector throws "Cannot provide both docId and
  vector" instead of "not yet implemented". The two existing
  test_query_decomposition tests asserted the old message and were updated.

Tests: 212/212, 0 skipped, 0 failed, 2 expected fail.
@s2x
s2x merged commit 9171a6b into main Sep 29, 2026
6 checks passed
@s2x
s2x deleted the feat/219-fromid-in-query branch September 29, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(query): support ZVecVectorQuery::fromId() in query(), queryVector(), queryWithReranker() and groupByQuery()

1 participant