Summary
Search::search() currently performs a GET /collections/{name} request whenever q is present and query_by is omitted, so a single logical search can become two HTTP calls. For a high-performance search client, that hidden schema lookup adds avoidable latency, doubles network overhead on a common path, and makes search behavior depend on collection-metadata availability even when the caller only wants results.
Context
The current implementation in src/search.cpp auto-detects searchable_fields by calling collections_->get(collection_name) before issuing /collections/{name}/documents/search. That is useful ergonomically, but it means every uncached search without query_by pays an extra request and response parse. In a low-latency search workload, this is expensive relative to the core operation and will inflate p95/p99 latency, especially over TLS where src/request.cpp opens a fresh connection per request.
This also creates a subtle reliability problem: if collection metadata is temporarily slow or unavailable, searches that could otherwise succeed with known schema semantics now fail earlier in the client. The issue is amplified because the library positions itself as a high-performance wrapper around hlquery/RocksDB rather than a convenience-only SDK.
Proposed Implementation
- Add a per-collection schema cache, or a narrower cache for
searchable_fields, in Search or Collections.
- Populate the cache lazily on the first search that omits
query_by, then reuse it for subsequent searches on the same collection.
- Invalidate or refresh the cache on collection lifecycle operations that can change schema, especially
create, update, and remove.
- Expose an explicit option to disable auto-discovery entirely for users who prefer strict, zero-hidden-I/O behavior.
- Add tests covering:
- first search without
query_by performs one metadata fetch;
- repeated searches on the same collection do not;
- cache invalidates after schema changes;
- behavior when metadata lookup fails.
Impact
This removes unnecessary work from the hottest user-facing path in the client. The result should be lower end-to-end search latency, fewer outbound requests, less load on hlquery nodes, and more predictable behavior under production traffic. It also preserves the convenience of implicit query_by discovery without letting that convenience become a default performance penalty.
Summary
Search::search()currently performs aGET /collections/{name}request wheneverqis present andquery_byis omitted, so a single logical search can become two HTTP calls. For a high-performance search client, that hidden schema lookup adds avoidable latency, doubles network overhead on a common path, and makes search behavior depend on collection-metadata availability even when the caller only wants results.Context
The current implementation in
src/search.cppauto-detectssearchable_fieldsby callingcollections_->get(collection_name)before issuing/collections/{name}/documents/search. That is useful ergonomically, but it means every uncached search withoutquery_bypays an extra request and response parse. In a low-latency search workload, this is expensive relative to the core operation and will inflate p95/p99 latency, especially over TLS wheresrc/request.cppopens a fresh connection per request.This also creates a subtle reliability problem: if collection metadata is temporarily slow or unavailable, searches that could otherwise succeed with known schema semantics now fail earlier in the client. The issue is amplified because the library positions itself as a high-performance wrapper around hlquery/RocksDB rather than a convenience-only SDK.
Proposed Implementation
searchable_fields, inSearchorCollections.query_by, then reuse it for subsequent searches on the same collection.create,update, andremove.query_byperforms one metadata fetch;Impact
This removes unnecessary work from the hottest user-facing path in the client. The result should be lower end-to-end search latency, fewer outbound requests, less load on hlquery nodes, and more predictable behavior under production traffic. It also preserves the convenience of implicit
query_bydiscovery without letting that convenience become a default performance penalty.