English | 简体中文
zvec-java 是 Zvec 向量数据库 C API 的工业级 Java 语言绑定,底层基于 JavaCPP 生成 JNI 绑定。JavaCPP 会从 zvec/c_api.h 自动生成 JNI 胶水代码,并将各平台原生库打包进 JAR、运行时自动解压加载,无需任何手写 JNI 代码,也无需用户手动配置库路径。
- JavaCPP + JNI:由 JavaCPP 解析
c_api.h自动生成低层绑定类ZvecNative与 JNI 胶水,兼顾性能与可维护性 - 跨平台开箱即用:原生库(
libzvec_c_api+libjniZvecNative)按平台-架构目录打进 JAR,运行时零配置自动加载 - 高层包装类:在生成的
ZvecNative之上提供类型安全、资源安全的 Java 对象 - AutoCloseable 资源管理:所有持有原生资源的对象均实现
AutoCloseable,支持 try-with-resources - 丰富的索引支持:HNSW、IVF、Flat、Invert(倒排)、Vamana、DiskANN、IVF-RaBitQ(zvec ≥ v0.7.0)及量化变体(FP16/INT8/INT4/RaBitQ)。平台可用性与 zvec 本体一致:DiskANN 需要 Linux x86_64/ARM64 或 macOS ARM64,IVF-RaBitQ 需要 Linux x86_64,其他平台原生层返回
NotSupported - 文档迭代器:支持对集合做快照遍历,可选择输出字段(
Collection.createIterator,zvec ≥ v0.7.0) - Jieba 全文索引开箱即用:JAR 内置 cppjieba 词表(
jieba.dict.utf8+hmm_model.utf8,位于zvec/jieba_dict/),Zvec.initialize()时自动注册,jieba分词器无需任何额外配置 - 多种数据类型:支持 30 余种字段类型,包括各维度稀疏/稠密向量
- Java 8+:最低兼容 Java 8
发布产物已上传 Maven Central,坐标为 org.zvec:zvec-java。加上依赖就是全部准备工作:JAR 内已经带了各平台原生库和 cppjieba 词表,不需要单独安装原生库,也不需要配置任何库路径。
Maven
<dependency>
<groupId>org.zvec</groupId>
<artifactId>zvec-java</artifactId>
<version>0.7.0</version>
</dependency>Gradle
implementation 'org.zvec:zvec-java:0.7.0'org.bytedeco:javacpp 会作为传递依赖自动引入,无需自己声明。运行环境要求 Java 8 及以上。
对外 API 都在 org.zvec.binding 包下:Zvec、Collection、Doc、Schema、IndexParams、VectorQuery 等都在这里,而不是 org.zvec。
版本号跟随内置的 zvec 原生库版本:0.7.0 对应 zvec v0.7.0。
| 产物 | 内容 | 适用场景 |
|---|---|---|
| (不带 classifier) | classes + jieba 词表 + 全部受支持平台的原生库 | 想用一个依赖跑遍所有平台。最省事,体积最大。 |
macosx-arm64 |
classes + jieba 词表 + macOS ARM64 原生库 | 部署平台确定,想要更小的体积。 |
linux-x86_64 |
classes + jieba 词表 + Linux x86_64 原生库 | 同上,Linux x86_64。 |
linux-arm64 |
classes + jieba 词表 + Linux ARM64 原生库 | 同上,Linux ARM64。 |
windows-x86_64 |
classes + jieba 词表 + Windows x86_64 原生库 | 同上,Windows x86_64。 |
nolib |
classes + jieba 词表,不含原生库 | 自己编译或分发 zvec_c_api,再让加载器指向它(见原生库如何加载?)。 |
指定单平台 classifier:
<dependency>
<groupId>org.zvec</groupId>
<artifactId>zvec-java</artifactId>
<version>0.7.0</version>
<classifier>linux-x86_64</classifier>
</dependency>implementation 'org.zvec:zvec-java:0.7.0:linux-x86_64'受支持平台:macOS ARM64、Linux x86_64、Linux ARM64、Windows x86_64。Linux 原生库在 manylinux_2_28 镜像中构建,要求 glibc 2.27 及以上,因此 Ubuntu 18.04+、Debian 10+、RHEL/CentOS 8+、Fedora 28+ 均可使用。各类索引的平台可用性仍与 zvec 本身一致(见特性)。
接下来直接看代码示例即可,唯一需要的初始化调用是 Zvec.initialize(null)。
以下内容是从源码构建绑定的流程,适用于开发调试,或者需要为发布产物未覆盖的平台/架构自行编译原生库的场景。如果只是想使用 zvec-java,按安装加上 Maven Central 依赖就够了,可以直接跳到代码示例。
| 工具 | 最低版本 | 用途 |
|---|---|---|
| JDK | 8 | 编译与运行 |
| Maven | 3.6 | 构建 |
| C++ 编译器 (clang / gcc / MSVC) | 支持 C++17 | JavaCPP 编译 JNI 胶水 |
| CMake + Ninja | 3.30 / 1.11 | 编译 Zvec C 库 |
Zvec 核心以 git submodule 形式引入到 ./zvec:
git clone https://github.com/zvec-ai/zvec-java.git
cd zvec-java
git submodule update --init --recursivecd zvec
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_C_BINDINGS=ON -G Ninja
cmake --build . --target zvec_c_api -j
# 产物位于 zvec/build/lib/
cd ../..# 默认从子模块 ./zvec 读取头文件与库(zvec.home=${project.basedir}/zvec)
mvn package
# 若复用一个已存在的 Zvec 检出(例如同级目录),用 -Dzvec.home 覆盖:
mvn package -Dzvec.home=/path/to/zvec构建期涉及的关键属性:
| 属性 | 默认值 | 说明 |
|---|---|---|
zvec.home |
${project.basedir}/zvec |
Zvec 核心根目录(子模块) |
zvec.include.path |
${zvec.home}/src/include |
头文件目录(JavaCPP parse) |
zvec.lib.path |
${zvec.home}/build/lib |
链接库目录(JavaCPP link) |
mvn test
# 或指定 Zvec 位置
mvn test -Dzvec.home=/path/to/zvec打包生成的 fat JAR 已内置当前平台的原生库,运行时无需任何库路径配置:
mvn package -DskipTests
java -jar target/zvec-java-0.7.0-with-dependencies.jarscripts/smoke-test.sh 会按消费者的方式加载一个已构建好的 JAR 并完整跑一遍:
JavaCPP 从 JAR 中解压出当前平台的原生库,内置的 jieba 词表被释放并注册,然后真实
地创建一个 collection、写入数据,并分别用向量检索和 jieba 全文检索查询。它只依赖
JDK —— 不需要 Docker、不需要 Maven、也不需要 zvec 源码。
scripts/smoke-test.sh --jar target/zvec-java-0.7.0.jar
# 也可以从 Maven 目录布局中解析 JAR,例如发布前从 Central Portal 下载的
# deployment bundle
scripts/smoke-test.sh --repo /tmp/central-staging --version 0.7.0
scripts/smoke-test.sh --repo /tmp/central-staging --version 0.7.0 --classifier linux-arm64想证明产出的 .so 真的能在老发行版上加载,就要在 glibc 不高于文档所述下限的机器上
跑 —— 在更新的系统上通过说明不了什么。CI 的 Publish JAR 工作流正是这么做的:它的
smoke-test job 会在 bundle 已上传到 Central Portal、但仍停在 VALIDATED 状态、还没
人点 Publish 的窗口里,在两种 linux 架构的 manylinux_2_28 容器内各跑一次。
zvec-java/
├── pom.xml # Maven 构建(JavaCPP 插件两段式:parse + build)
├── zvec/ # git submodule:Zvec 核心
└── src/
├── main/java/org/zvec/binding/
│ ├── presets/ZvecConfig.java # JavaCPP InfoMapper:指导解析 c_api.h
│ ├── ZvecNative.java # 【自动生成】低层 JNI 绑定(勿手改,已在 .gitignore)
│ ├── NativeSupport.java # String <-> const char* 等桥接工具
│ ├── NativeLoader.java # 三级原生库加载器
│ ├── Zvec.java # 顶层入口:初始化、版本、Collection 工厂
│ ├── Collection.java # Collection 操作(增删改查、搜索)
│ ├── CollectionOptions.java / CollectionSchema.java / CollectionStats.java
│ ├── FieldSchema.java # 字段 Schema 定义
│ ├── Doc.java # 文档 CRUD(读写各类型字段)
│ ├── IndexParams.java # 索引参数(HNSW/IVF/Flat/Invert/Vamana/DiskANN/IVF-RaBitQ)
│ ├── VectorQuery.java / GroupByVectorQuery.java / MultiQuery.java / SubQuery.java
│ ├── FlatQueryParams.java / HnswQueryParams.java / IvfQueryParams.java /
│ │ IvfRabitqQueryParams.java / DiskAnnQueryParams.java /
│ │ VamanaQueryParams.java / FtsQueryParams.java # 类型化查询参数,每种索引族一个
│ ├── FtsPayload.java # jieba 全文检索载荷
│ ├── DocIterator.java / IteratorOptions.java # v0.7.0 集合迭代器
│ ├── IoBackendType.java # v0.7.0 I/O 后端枚举
│ ├── JiebaDictSupport.java # 解压内置的 jieba 词表
│ ├── ConfigData.java / LogConfig.java
│ ├── ZvecException.java
│ └── DataType / IndexType / MetricType / QuantizeType / LogLevel / DocOperator / ErrorCode (枚举)
└── test/java/org/zvec/binding/
├── ZvecTest.java # 基础 API 测试
├── TestSupport.java # 测试基类(守护式 init + 索引集合/向量助手)
├── DocCoverageTest.java # Doc 元数据/UTF-8/异常 强断言
├── SchemaIndexConfigCoverageTest.java # Schema/IndexParams(out 参数)/Config/异常
├── CollectionQueryCoverageTest.java # DML/DQL 强断言(query/update/delete/filter)
├── ApiCoverageTest.java # 更广的 API 面:枚举码、DiskANN/IVF-RaBitQ/FTS 参数、multi-query、迭代器、I/O 后端、jieba 词表
└── SearchIntegrationTest.java # 端到端检索:纯 FTS、向量 + FTS 混合、multi-query 多路子查询
// 使用默认配置初始化
Zvec.initialize(null);
// 自定义配置
try (ConfigData config = new ConfigData()) {
config.setQueryThreadCount(4);
config.setMemoryLimit(512 * 1024 * 1024L); // 512MB
config.setConsoleLog(LogLevel.INFO);
Zvec.initialize(config);
}
// 关闭(进程退出前调用)
Zvec.shutdown();CollectionSchema schema = new CollectionSchema("my_collection");
// FP32 向量字段
try (FieldSchema vecField = new FieldSchema("embedding", DataType.VECTOR_FP32, false, 128)) {
try (IndexParams hnsw = IndexParams.createHNSW(MetricType.L2, 32, 200)) {
vecField.setIndexParams(hnsw);
}
schema.addField(vecField);
}
// 元数据字段
try (FieldSchema titleField = new FieldSchema("title", DataType.STRING, true, 0)) {
schema.addField(titleField);
}
// 创建并打开 Collection
Collection coll = Zvec.createAndOpen("/tmp/my_db", schema, null);
schema.close();// 插入文档
List<Doc> docs = new ArrayList<>();
Doc doc = new Doc();
doc.setPK("doc_001");
doc.addStringField("title", "Hello Zvec");
doc.addVectorFP32Field("embedding", new float[128]); // 示意:全零向量
docs.add(doc);
coll.insert(docs);
Doc.freeDocs(docs);
coll.flush();
// 向量查询
try (VectorQuery query = new VectorQuery()) {
query.setTopK(10);
query.setFieldName("embedding");
query.setQueryVector(new float[128]); // 查询向量
List<Doc> results = coll.query(query);
for (Doc d : results) {
System.out.printf("id=%s, score=%.4f%n", d.getPK(), d.getScore());
}
Doc.freeDocs(results); // 结果由原生内存支持,用后必须释放
}
coll.close();IndexParams hnsw = IndexParams.createHNSW(MetricType.L2, 32, 200);
IndexParams hnswQ = IndexParams.createHNSWQuantized(MetricType.IP, 32, 200, QuantizeType.FP16);
IndexParams ivf = IndexParams.createIVF(MetricType.COSINE, 256, 100, false);
IndexParams flat = IndexParams.createFlat(MetricType.L2);
IndexParams invert = IndexParams.createInvert(true, false); // 倒排,用于文本/标签| 依赖 | 版本 | 用途 | 许可证 |
|---|---|---|---|
org.bytedeco:javacpp |
1.5.11 | JNI 代码生成 + 跨平台原生库加载 | Apache-2.0 或 GPL-2.0-or-later 或 GPL-2.0-with-classpath-exception;本项目按 Apache-2.0 使用 |
org.junit.jupiter:junit-jupiter |
5.10.2 | 单元测试(仅 test scope) | EPL-2.0 |
Java 应用代码
│
▼
高层 API(Zvec / Collection / Doc / VectorQuery …) 类型安全 + AutoCloseable
│
▼
ZvecNative(JavaCPP 自动生成的 JNI 绑定)
│ JavaCPP 生成的 JNI 胶水(libjniZvecNative)
▼
libzvec_c_api.(so|dylib|dll)
│
▼
Zvec C++ 核心引擎
构建流程:JavaCPP Parser 解析 c_api.h(由 presets/ZvecConfig 指导)生成 ZvecNative.java → 编译 → JavaCPP Generator/Compiler 生成并编译 JNI 胶水为 libjniZvecNative,链接 zvec_c_api → 两者一并打进 JAR 的 平台-架构 目录。
原生库(zvec_c_api + JavaCPP JNI 胶水 jnizvec)由 NativeLoader 按三级优先级(从高到低)解析:
- 第一级 · 显式路径:设置
-Dzvec.native.path=/dir或环境变量ZVEC_NATIVE_PATH,即优先从该目录解析zvec_c_api(内部通过 JavaCPP 的pathsFirst+platform.preloadpath实现)。适用于本地开发或使用自建/自定义原生库。 - 第二级 · classpath / fat JAR:默认行为——从 JAR 内
平台-架构资源目录解压到临时目录并加载,无需任何库路径配置。 - 第三级 · 系统库路径:兜底回退到
java.library.path/LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH。
加载时按第一级 → 第二级 → 第三级依次尝试,三级均失败时会抛出列明上述来源的可读 UnsatisfiedLinkError,便于定位。
# 第一级:指向本地已构建的原生库目录
java -Dzvec.native.path=/path/to/zvec/build/lib -jar app.jar
# 或用环境变量
ZVEC_NATIVE_PATH=/path/to/zvec/build/lib java -jar app.jar本地 mvn package 产出的 fat JAR 只含构建时所在平台的原生库;而 CI 的 Publish JAR 工作流会在各平台分别构建、聚合出含全部平台原生库的多平台 fat JAR(见 .github/workflows/publish-jar.yml)。发布到 Maven Central 的 org.zvec:zvec-java 就是这个多平台 JAR,同时还会发布安装一节中列出的单平台 classifier JAR 与 nolib JAR。
JAR 内置了 cppjieba 词表文件(jieba.dict.utf8、hmm_model.utf8),位于 zvec/jieba_dict/。Zvec.initialize() 期间,它们会被解压到一个按版本区分的缓存目录(默认 ~/.zvec/jieba_dict/<zvec-version>,home 目录不可写时回退到 <tmpdir>/zvec-java/jieba_dict/<version>),并通过 zvec_set_default_jieba_dict_dir() 注册,因此创建 jieba 全文索引不需要任何额外配置。
分词时的解析优先级(从高到低):
- 字段级
extra_params.jieba_dict_dir ZVEC_JIEBA_DICT_DIR环境变量- 进程级默认值(
ConfigData.setJiebaDictDir()/Zvec.setDefaultJiebaDictDir()/ 自动解压的内置词表)
用 -Dzvec.jieba.cache.dir=/dir(或 ZVEC_JIEBA_CACHE_DIR)可以改变解压位置。
fetch() 需要目标字段建立了 forward index(正排索引)。确保 schema 中为需要 fetch 的字段配置了正排索引,或改用 query()。
- 所有实现了
AutoCloseable的对象(Collection、Doc、IndexParams、VectorQuery等)都应在 try-with-resources 块中使用,或在 finally 中显式close()。 Collection.query()/Collection.fetch()返回的List<Doc>由原生内存支持,使用完毕后必须调用Doc.freeDocs(list)释放。
Zvec C API 未提供文档级校验函数(zvec_doc_validate 不存在),该方法会抛出 UnsupportedOperationException;请改用 CollectionSchema.validate() / FieldSchema.validate()。
欢迎提 issue 和 pull request。构建环境、绑定遵循的约定、NOTICE 如何与 zvec 子模块保持同步,以及发布流程,见 CONTRIBUTING.md。安全问题请按 SECURITY.md 私下报告。本项目遵循行为准则,变更记录见 CHANGELOG.md。
本项目采用 Apache License 2.0,与 Zvec 主项目保持一致,详见 zvec/LICENSE。
- JavaCPP (
org.bytedeco:javacpp:1.5.11) 采用三重许可证:Apache-2.0 或 GPL-2.0-or-later 或 GPL-2.0-with-classpath-exception。 本项目按 Apache-2.0 条款使用 JavaCPP。 - JUnit 5 仅在
testscope 中使用,许可证为 EPL-2.0,不会被打入发布的 JAR。 - 原生库
zvec_c_api(由zvec子模块构建)静态链接了 RocksDB;RocksDB 采用 Apache License 2.0 与 GPLv2 双重许可,本项目按 Apache-2.0 条款使用。RocksDB 中唯一 仅以 GPL 授权的部分是源自 PerconaFT 的range_tree锁管理器 (utilities/transactions/lock/range/),而 zvec 从不使用悲观事务,因此这些目标文件不会 被链接进发布的二进制。两条发布流水线都会强制校验这一点:每个待发布的原生库都会被扫描range_tree/locktree/PessimisticTransaction符号,一旦出现即构建失败。 - 打包在
zvec/jieba_dict/下的 cppjieba 词表(jieba.dict.utf8、hmm_model.utf8) 来自 cppjieba,采用 MIT 许可证。