refactor(param): introduce declarative schema and migrate BruteForceParameter#2376
refactor(param): introduce declarative schema and migrate BruteForceParameter#2376LHT129 wants to merge 1 commit into
Conversation
Merge Protections🟢 All 2 merge protections satisfied — ready to merge. Show 2 satisfied protections🟢 Require kind label
🟢 Require version label
|
There was a problem hiding this comment.
Code Review
This pull request introduces a declarative, macro-driven parameter schema framework under src/param to automate JSON parsing, serialization, and compatibility checks, migrating BruteForceParameter as the first user. While the design is a great improvement, there are two key issues: first, BruteForceParameter::FromJson fails to call schema().CheckUnknownKeys(json), meaning unknown keys are still silently accepted; second, the Range validator's use of static_cast<T> can lead to silent overflow, underflow, or truncation bugs when comparing different numeric types.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
c632815 to
e85f1d2
Compare
e85f1d2 to
b9f2105
Compare
b9f2105 to
357d56a
Compare
357d56a to
4b4b164
Compare
…ruteForceParameter
Add a declarative schema framework under src/param/ to automate JSON
parsing, serialization, and compatibility checks for parameter classes.
Migrate BruteForceParameter as the first user.
New files:
- src/param/validators.h: Range<T> validator with type-safe comparisons
- src/param/schema.h: Schema and VSAG_PARAM* macro definitions
- src/param/schema.cpp: Schema implementation
- src/param/schema_test.cpp: Schema framework unit tests
- src/param/CMakeLists.txt: Build configuration
Modified files:
- src/algorithm/bruteforce/bruteforce_parameter.{h,cpp}: Use schema
- src/inner_string_params.h: Add INDEX_BRUTE_FORCE to DEFAULT_MAP
- src/CMakeLists.txt: Add param subdirectory
- tests/CMakeLists.txt: Add param_test to unittests
Fixes CI failure: LazyHGraph test was failing because DEFAULT_MAP
was missing INDEX_BRUTE_FORCE entry, causing template expansion to
produce literal string "{INDEX_BRUTE_FORCE}" instead of "brute_force".
Signed-off-by: LHT129 <tianlan.lht@antgroup.com>
4b4b164 to
6f25428
Compare
What
Add a small declarative schema framework under
src/param/and use it forBruteForceParameteras the first migration. The framework lets a parameter class describe its own fields once and haveFromJson,ToJson,CheckCompatibility(and unknown-key detection) derived from that single source of truth.Why
BruteForceParameter(and every other*_parameter.{h,cpp}that will follow) hand-writes three near-identical blocks: aFromJsonwithCHECK_ARGUMENT(json.Contains(...)), aToJsonwith the matchingjson[KEY].SetJson(...), and aCheckCompatibilitythat chainsCHECK_FIELD_EQ/CHECK_SUB_PARAM. The boilerplate grows linearly with the field count and is silent on unknown keys, so typos like{"pq_dim": 8}on a BruteForce index parse cleanly today.A schema-driven approach lets each class declare the fields once and produce the three behaviours from that single declaration.
Range<T>covers numeric validation, and aConstantStringFieldhandles the common"type": "brute_force"tag without giving it special-case treatment.Schema framework (
src/param/)validators.hprovides a header-onlyRange<T>validator that callers can attach to a scalar field. It throwsVsagExceptionwithErrorType::INVALID_ARGUMENTif the value is outside the closed[min, max]range, mirroring the existingCHECK_ARGUMENTstyle.schema.hexposesSchema,SchemaBuilder<Owner>, and a small set ofVSAG_PARAM*macros. Two field kinds are supported in this PR:ScalarField<T>foruint64_t/int64_t/std::string/bool/float(the types BruteForce-class parameters use today).SubParamField<Sub>forParameter-derived sub-objects that are constructed via a factory (e.g.FlattenInterfaceParameterforBruteForceParameter::base_codes_param).ConstantStringFieldcovers the"type": "brute_force"tag pattern that several index parameters share.Schema::Parsecalls each field's parser, fills the C++ member, runs the per-field validator, and refuses unknown JSON keys viaCheckUnknownKeys.Schema::Serializewrites the fields back in the order they were declared so the JSON layout stays stable.Schema::Equalwalks the fields once and stops at the first mismatch, replacing manualCHECK_FIELD_EQ/CHECK_SUB_PARAMladders.schema.cppimplementsCheckUnknownKeysusingJsonWrapper::GetInnerJson()->items()so callers can register the set of keys the base class already consumes (extra_known_keys) without forcing every parameter to inherit a common JSON pre-filter.The framework is intentionally narrow: it does not touch any
InnerIndexParameterfield, does not add aValidatehook (left for PR #2), and does not migrate any of the other parameter classes. That restraint is what keeps the BruteForce-side diff small.BruteForce migration
BruteForceParameternow declares its schema in-class viaVSAG_PARAM_SCHEMA_DECL()and defines it out-of-line in the.cpp:Three overrides (
FromJson/ToJson/CheckCompatibility) delegate toschema().Parse/schema().Serialize/schema().Equalafter forwarding to theInnerIndexParameterbase, so inherited fields keep their existing behaviour. The hand-writtenCHECK_ARGUMENT(json.Contains(BASE_CODES_KEY)),CHECK_SUB_PARAM(*this, *p, base_codes_param), andjson[BASE_CODES_KEY].SetJson(base_codes_param->ToJson())blocks are gone.JSON compatibility
The schema keeps the field order stable and uses the same keys / types as the hand-written version, so
BruteForceParameter::FromJson(user_json)followed byToJson()produces byte-identical output before and after this PR. Unknown keys ({"pq_dim": 8}etc.) now throwINVALID_ARGUMENTinstead of being silently accepted.Tests
src/param/schema_test.cppexercises the framework end-to-end with a minimal in-processTestParam(one scalar with aRange, one string, one sub-parameter) and covers four behaviours: scalar + sub-parameter parse, JSON round-trip stability,Rangerejection, and unknown-key rejection. Both rejection paths assertVsagException.BruteForce Parameters CheckCompatibilitytest insrc/algorithm/bruteforce/bruteforce_parameter_test.cppis unchanged and continues to exercise the public surface ofBruteForceParameter(now backed by the schema).Build wiring
src/param/CMakeLists.txtfollows the project convention: anOBJECTlibraryparamfor production code and aSTATIClibraryparam_testfor the unit tests, gated onENABLE_TESTS.src/CMakeLists.txtregistersparamas a subdirectory and adds it toVSAG_DEP_LIBSsovsag/vsag_staticpick it up.tests/CMakeLists.txtlinksparam_testintounittestswith the same--whole-archive(Linux) /-Wl,-force_load(macOS) treatment used for the other*_teststatic libraries.Verification
cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_TESTS=ON -G 'Unix Makefiles' -S.succeeded.cmake --build build --target unittestsbuilt successfully (includingparam,param_test,bruteforce,algorithm_test).[ut][param][schema],[ut][BruteForceParameter],[brute_force_threshold]:make fmt(clang-format v15) clean.Signed-off-by: claude <claude@anthropic.com>
Signed-off-by: LHT129 <tianlan.lht@antgroup.com>