Open-source project
datastax/jvector avatar
datastax/jvector

JVector: a graph index that merges HNSW and DiskANN, with a two-pass search path

JVector: the most advanced embedded vector search engine

1,749 stars157 forksJavaApache-2.0

At a glance

What is it?
JVector is a Java embedded vector search library that borrows the hierarchical layer structure of HNSW and uses Vamana inside each layer. Its distinguishing feature is a two-pass search design that keeps compressed vectors in memory and reads fuller representations from disk, plus the ability to build an index that does not fit in memory.
Who is it for?
Adopt JVector if you are working in Java 11 or later, you control the JVM and the machines, and you want a graph index you can build incrementally while keeping the bottom layer on disk. Do not adopt it if you need a managed service, a non-JVM client, or a static dataset where a partition-based index already meets your recall target.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem JVector addresses: exact KNN collapses in high dimensions

The README opens with the constraint that shapes everything else. Exact k-nearest-neighbor search is prohibitively expensive at higher dimensions, because the space-partitioning structures that work in two or three dimensions, such as a quadtree or a k-d tree, devolve into linear scans as dimensionality grows. The README names this as one aspect of the curse of dimensionality. The practical consequence is a trade: with larger datasets it is almost always more useful to get an approximate answer in logarithmic time than the exact answer in linear time.

That trade is not new. What is worth noting is who JVector is built for. This is a library, not a service. It is embedded, it is written in Java, and the README describes it as suitable for use as a dependency from any Java 11 code. The audience is therefore teams that already run a JVM and want vector search inside their own process, rather than teams looking for a hosted endpoint. The README's framing also makes an argument about index families: partition-based indexes such as LSH, IVF or SCANN work on static datasets that are completely specified up front, while graph indexes can be constructed and updated incrementally, which the README says is why all the major commercial vector indexes use graph approaches. That claim about commercial products is the README's assertion, not something this review can verify.

How the graph is layered, and where the two passes happen

JVector sits in the DiskANN and HNSW family trees. It takes the hierarchical structure from HNSW and uses Vamana, the algorithm behind DiskANN, within each layer. The README describes the result as a multi-layer graph with nonblocking concurrency control, and states that construction scales linearly with the number of cores. That linear-scaling claim is illustrated by a chart in the repository; it is not a measurement performed here.

The memory layout is where the design gets specific. Upper layers are held as an in-memory adjacency list per node, so navigation through them requires no IOs. The bottom layer is an on-disk adjacency list per node. To make that bottom layer usable without reading everything, JVector stores additional data inline and runs a two-pass search. The first pass uses lossily compressed representations of the vectors kept in memory. The second pass uses a more accurate representation read from disk.

The first pass can be performed with product quantization, optionally with anisotropic weighting, or with binary quantization, or with fused PQ, where the PQ codebooks are written inline with the graph adjacency list. The second pass can use full-resolution float32 vectors, or NVQ, which the README describes as a non-uniform technique for quantizing vectors with high accuracy. The README links a paper for each of these: anisotropic PQ, Quicker ADC, and NVQ.

The second thing the two-pass machinery enables is index construction itself. The README states that JVector is unique in offering the ability to construct the index using two-pass searches, which allows indexes larger than memory to be built. The stated benefit is that you get logarithmic search within a single index instead of spilling over to linear-time merging of results from multiple indexes. That last point is the real argument for the design: sharding a vector index and merging results across shards degrades the search from logarithmic back toward linear, which is the thing ANN was supposed to avoid.

Building JVector: Maven modules, a git submodule, and a native SIMD library

The repository is a multimodule Maven build. The stated intent is to produce a multirelease jar suitable as a dependency from any Java 11 code, and when it runs on a Java 20 or later JVM with the Vector module enabled, optimized vector providers are used. The project is structured to be built with JDK 20 or later, but if JAVA_HOME points at Java 11 through 19, the README says certain build features remain available. Module responsibilities are explicit: jvector-base is compiled for Java 11 and restricts language features and APIs accordingly, jvector-twenty is compiled for Java 20 language features and APIs, and jvector-multirelease packages the two into the final multirelease jar. jvector-examples uses the reactor representation of those modules to run example code, and jvector-tests holds tests.

Before any of that, there is a submodule. Google Highway lives at jvector-native/src/main/native/third_party/highway, and the README gives two ways to fetch it:

git submodule update --init

git clone --recurse-submodules <repo-url>

The native SIMD library, libjvector.so, is built with Meson and Ninja and requires g++ 11 or later. The entry-point script is jvector-native/src/main/native/build_native_lib.sh, run from that directory:

cd jvector-native/src/main/native bash build_native_lib.sh

On a fresh Ubuntu machine the README offers a one-step path that installs g++, Meson and Ninja and then builds:

cd jvector-native/src/main/native/src bash build_native_lib.sh --auto-install-deps

On other distributions the script prints the install commands it needs rather than running them. The native README is cited for full build options, Maven integration, ISA dispatch details, and instructions for adding new kernels. Note the directory change between the two invocations: the plain build runs from jvector-native/src/main/native, while the auto-install variant runs from the src subdirectory. Getting that wrong is the kind of thing that costs twenty minutes.

For a first look at the API, the README points to docs/tutorials, starting with 1-intro-tutorial.md, and to VectorIntro.java under jvector-examples for a simple example. An older step-by-step guide at docs/legacy/jvector-step-by-step.md is described as useful commentary for advanced users, with new users directed to the tutorials instead.

The two-pass design is a recall trade, and the README does not quantify it

The first pass is lossy by construction. Product quantization, binary quantization and fused PQ all compress the vector, and the README is upfront that these are lossily compressed representations. The second pass exists to correct for that, reading a more accurate representation from disk. The design goal stated in the README is to reduce memory usage and reduce latency while preserving accuracy.

Preserving accuracy is a claim, not a number. The README does not state recall figures for any of the first-pass options, does not give a memory-per-vector figure, and does not give latency numbers. If recall is the thing you care about, you will have to measure it against your own data rather than read it off the project page. That is a real gap in the documentation, not a criticism of the algorithm.

The second-pass choice matters too. Full-resolution float32 vectors are the accurate option and cost disk reads. NVQ is described as a non-uniform quantization technique, which is a middle ground, but again without published recall or size numbers in the README. The fused PQ variant is interesting because it writes codebooks inline with the adjacency list, which the README presents as a first-pass option; the implication is fewer separate structures to read, though the README does not spell out the tradeoff against non-fused PQ.

There is a second limitation worth naming. The upper layers are in memory. That is what makes navigation fast, and it is also a floor on memory use that does not shrink just because the bottom layer lives on disk. An index whose bottom layer exceeds memory still needs its hierarchy resident. The README's larger-than-memory claim is about construction, and about the bottom layer, not about the whole structure being free of memory pressure.

Where JVector is the wrong choice

If your vectors fit comfortably in memory and your dataset is static, the two-pass machinery is overhead you are paying for nothing. You would build the index, keep everything resident, and never exercise the disk path. A simpler in-memory graph index, or an in-process library without a native build step, would get you the same recall with less to install.

The native build is the second friction point. libjvector.so requires g++ 11 or later plus Meson and Ninja, and the auto-install path is documented specifically for fresh Ubuntu. On other distributions the script prints install commands rather than executing them, which means an operator has to run them. A pure-Java deployment with no native toolchain is not the path this project is on, at least not for the optimized providers. The README does not describe a supported fallback that avoids the native library entirely, so if you cannot compile C++ in your build environment, treat that as an open question to resolve before adopting.

Third, this is Java. The multirelease jar targets Java 11 and up. If your services are not on the JVM, nothing here helps you, and there is no indication in the supplied material of bindings for other languages.

Finally, the release line is not finished. The most recent release listed is 4.0.0-rc.9, published in July 2026, following 4.0.0-rc.8 and a hotfix release for rc.8. A hotfix on a release candidate is a normal part of stabilising, but it does mean the 4.0 series is still in candidate status as of the latest release in the material. Teams that require a final, non-candidate version should check whether a 4.0.0 final exists before planning around it.

How JVector differs from HNSW implementations and from partition-based indexes

Against a plain HNSW implementation, the difference is the bottom layer. HNSW keeps the graph in memory. JVector keeps the upper layers in memory and puts the bottom layer's adjacency list on disk, then compensates with a compressed in-memory representation for the first pass. That is the DiskANN lineage showing. The cost is a native library and a disk read on the second pass; the benefit is an index whose bottom layer can be much larger than what you would keep resident otherwise.

Against partition-based indexes such as IVF or SCANN, the difference is not memory but mutability. The README's argument is that partitioning approaches only work on static datasets that are completely specified up front, while graph indexes can be constructed and updated incrementally. If your corpus is appended to continuously, that argument is the one that matters, and it is the reason the README gives for commercial vector indexes choosing graph approaches. If your corpus is a fixed snapshot rebuilt nightly, the argument does not apply to you, and the simpler partitioning approach may be easier to reason about.

Within the graph family, the layered structure is the other differentiator. Taking HNSW's hierarchy and using Vamana inside each layer is a specific combination rather than a generic graph index. The README presents it as the merge point of the two family trees, and the research section cites the HNSW and DiskANN papers plus a higher-level explainer for readers who want the underlying work.

Licence, maintenance and upgrade cost

JVector is Apache-2.0. That is a permissive licence, and the supplied material does not include a NOTICE file or any additional terms, so nothing here suggests a copyleft obligation. This is not legal advice; if you are redistributing the library or the native library it builds, read the licence text and any third-party notices yourself, particularly for the bundled Google Highway submodule, which carries its own licence.

Upgrade cost has two components. The Java side is a Maven dependency, and the multirelease jar means a single artifact serves Java 11 through the Java 20+ optimized path, so the Java upgrade surface is the library's own API. The native side is different: libjvector.so is built locally with Meson and Ninja against g++ 11 or later, and the README mentions ISA dispatch details in the native README. That means the build is sensitive to the target machine's instruction set support, and a rebuild is part of any deployment that changes the build environment. Budget for that rather than treating it as a one-time setup step.

The release cadence in the material is candidate-heavy: rc.8 in April 2026, a hotfix for rc.8 in June, rc.9 in July. The hotfix naming convention, 4.0.0-rc.8-hf1, indicates the project does ship corrective releases between candidates. For a team tracking the 4.0 line, pinning to a specific candidate tag and reading the release notes for each one is more useful than tracking the branch.

Editorial conclusion

Adopt JVector if you are working in Java 11 or later, you control the JVM and the machines, and you want a graph index you can build incrementally while keeping the bottom layer on disk. Do not adopt it if you need a managed service, a non-JVM client, or a static dataset where a partition-based index already meets your recall target. Before committing, verify three things in your own environment: that libjvector.so builds with your g++ and Meson versions, that your recall target survives the compressed first pass, and that the 4.0.0 release candidate line has stabilised, since the newest published release is 4.0.0-rc.9 rather than a final 4.0.0.

Official sources

  1. datastax/jvector on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Community notes

Community notes