Redis Vector Set Commands in Java: VADD, VSIM, and the Full RVectorSet API

Last updated
August 28, 2026

Vector sets are a native Redis 8 data type: elements with high-dimensional vectors attached, searchable by similarity with no secondary index to declare. This page is the command reference — all seventeen V* commands mapped to their Redisson RVectorSet equivalents in Java, with the argument shapes that are easy to get wrong.

If you are still deciding whether to use vector sets at all — as opposed to the Query Engine or the Spring AI store — start with Redis vector search in Java, which compares the three approaches and covers hybrid ranking, range queries, and the memory arithmetic. This page assumes that decision is made.

One prerequisite up front: vector sets require Redis 8.0 or higher. Valkey forked from Redis at 7.4 and does not have them — on Valkey you want valkey-search and the RSearch API instead. See what differs between the two engines.

Setting Up RVectorSet in Java

The vector API lives in org.redisson.api.vector. This is worth stating explicitly because org.redisson.api.search also exists, holds the Query Engine types, and is the package people reach for first — producing an unresolved import rather than a useful error.

import org.redisson.api.RVectorSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.vector.VectorAddArgs;
import org.redisson.api.vector.VectorSimilarArgs;
import org.redisson.api.vector.QuantizationType;
import org.redisson.api.vector.VectorInfo;
import org.redisson.client.protocol.ScoredEntry;
import org.redisson.client.protocol.ScoreAttributesEntry;
import org.redisson.codec.JacksonCodec;

RVectorSet vectorSet = redisson.getVectorSet("document-embeddings");

Add the Redisson dependency to your build:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>4.7.0</version>
</dependency>

Vector Set Command Reference: Redis to Redisson

Every vector set command and the Redisson method that issues it. Each method also has an Async variant returning RFuture, omitted here for brevity.

Redis commandRedisson methodReturnsWhat it does
VADDadd(VectorAddArgs)booleanInsert an element with its vector
VSIMgetSimilar(VectorSimilarArgs)List<String>Nearest neighbours, names only
VSIM WITHSCORESgetSimilarEntries(...)List<ScoredEntry<String>>Nearest neighbours with distances
VSIM WITHSCORES WITHATTRIBSgetSimilarEntriesWithAttributes(...)List<ScoreAttributesEntry<String>>Neighbours, distances and attributes in one round trip
VCARDsize()intNumber of elements in the set
VDIMdimensions()intDimensionality of the stored vectors
VEMBgetVector(String)List<Double>Read an element's approximate vector back
VEMB … RAWgetRawVector(String)List<Object>Raw internal representation, pre-dequantization
VGETATTRgetAttributes(String, Class<T>)TDeserialize an element's JSON attributes
VSETATTRsetAttributes(String, Object, JsonCodec)booleanReplace an element's attributes
VREMremove(String)booleanDelete an element
VISMEMBERcontains(String)booleanMembership test without fetching the vector
VINFOgetInfo()VectorInfoSet metadata — size, dimensions, quantization
VLINKSgetNeighbors(String)List<String>An element's HNSW graph neighbours
VLINKS WITHSCORESgetNeighborEntries(String)List<ScoredEntry<String>>Graph neighbours with distances
VRANDMEMBERrandom() / random(int)String / List<String>Random element name, or several
VRANGErange(String, String[, int])List<String>Element names in lexicographical range
VRANGE (batched)iterator() / stream()Iterator / StreamLazy traversal of every element name

Two entries in that table deserve attention because they have no obvious analogue in other Redis data types.

VLINKS exposes the HNSW graph itself — the neighbours a given element is directly connected to at the index level, which is not the same thing as its nearest neighbours by distance. It is a debugging and introspection tool: if similarity results look wrong, comparing getNeighbors() against getSimilar() tells you whether the problem is the graph or the query.

VEMB returns an approximate vector, not the one you inserted. Under the default Q8 quantization the stored values are 8-bit, so getVector() gives you the dequantized reconstruction. If you need the exact original embedding, keep it somewhere else — a vector set is an index, not a system of record.

Adding Elements: VADD

The builder starts with the element name and requires a vector, supplied either as Double... or as a byte[] of 32-bit little-endian floats. Production embeddings arrive as the latter.

vectorSet.add(VectorAddArgs.element("doc-123")
    .vector(0.12, -0.34, 0.56, 0.78)
    .quantization(QuantizationType.Q8));

Attaching attributes is where the API most often surprises people. attributes() takes two arguments — the object and a JsonCodec to serialize it — and there is no single-String overload, so passing a hand-built JSON literal on its own will not compile:

vectorSet.add(VectorAddArgs.element("doc-124")
    .vector(0.11, -0.31, 0.58, 0.75)
    .attributes(Map.of("category", "finance", "year", 2026),
                new JacksonCodec<>(Map.class)));

The remaining builder options on VectorAddArgs:

  • quantization(QuantizationType)Q8 (default), BIN, or NOQUANT. Set at insert time and consistent for the whole set.

  • reduce(int) — applies random projection to store vectors at a lower dimensionality than supplied.

  • explorationFactor(int) — how widely the graph is explored while inserting. Higher builds a better index more slowly.

  • maxConnections(int) — HNSW connections per node. Raises recall and memory together.

  • useCheckAndSet() — performs the insert as a check-and-set operation.

reduce() and maxConnections() are structural: they cannot be changed later without rebuilding the set. Decide them before you load a large catalogue, not after.

Querying: VSIM and Its Three Return Shapes

A query starts from either an element already in the set or a raw vector. Prefer the element form when the query object is already stored — it sends a few bytes instead of several kilobytes.

// by element name — cheap
List<String> related = vectorSet.getSimilar(
    VectorSimilarArgs.element("doc-123").count(5));

// by raw vector — for a freshly embedded query
List<String> hits = vectorSet.getSimilar(
    VectorSimilarArgs.vector(0.15, -0.30, 0.55, 0.80).count(10));

Choose the return shape by what you will do with the result. getSimilar() gives names only and offers no way to threshold on distance; getSimilarEntries() adds scores; getSimilarEntriesWithAttributes() adds the stored attributes, which is the difference between one round trip and one per result.

List<ScoreAttributesEntry<String>> full =
    vectorSet.getSimilarEntriesWithAttributes(
        VectorSimilarArgs.element("doc-123")
            .count(10)
            .filter(".category == \"finance\" && .year >= 2025")
            .filterEffort(200));

for (ScoreAttributesEntry<String> entry : full) {
    String name = entry.getValue();
    Double score = entry.getScore();
    String attrs = entry.getAttributes();
}

The filter() expression is evaluated on the server during the search, not applied to the result afterwards — filtered candidates never cross the network. When a filter is selective and you are getting back fewer results than you asked for, filterEffort() is the parameter to raise.

The remaining query options: epsilon(double) widens the accepted distance boundary, explorationFactor(int) widens the graph traversal, useLinearScan() forces an exhaustive comparison for ground-truth checks, and useMainThread() executes on the main thread rather than a background one.

Running It Locally

Vector sets need Redis 8, so a container is the quickest way to a working instance:

docker run --rm -p 6379:6379 redis:8

A complete smoke test — seed a few 4-dimensional vectors, inspect the set, and query it:

import org.redisson.Redisson;
import org.redisson.api.RVectorSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.vector.VectorAddArgs;
import org.redisson.api.vector.VectorSimilarArgs;
import org.redisson.client.protocol.ScoredEntry;
import org.redisson.config.Config;
import java.util.List;

public class VectorSetDemo {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);

        RVectorSet catalog = redisson.getVectorSet("product-embeddings");

        catalog.add(VectorAddArgs.element("sku-501").vector(0.90, 0.10, 0.80, 0.20));
        catalog.add(VectorAddArgs.element("sku-777").vector(0.88, 0.14, 0.76, 0.25));
        catalog.add(VectorAddArgs.element("sku-902").vector(0.10, 0.95, 0.05, 0.88));

        System.out.println("elements:   " + catalog.size());        // VCARD  -> 3
        System.out.println("dimensions: " + catalog.dimensions());  // VDIM   -> 4
        System.out.println("contains:   " + catalog.contains("sku-501"));

        // VSIM returns the query element itself, so ask for one extra and drop it
        List<ScoredEntry<String>> similar = catalog.getSimilarEntries(
            VectorSimilarArgs.element("sku-501").count(3));

        similar.stream()
               .filter(e -> !e.getValue().equals("sku-501"))
               .forEach(e -> System.out.println(e.getValue() + "  " + e.getScore()));

        redisson.shutdown();
    }
}

Note the last detail, because it catches everyone once: a similarity query seeded from an element returns that element first, at distance zero. Request k+1 results and filter it out, or your top result is always the thing the user is already looking at.

Vector Set Commands: Frequently Asked Questions

What Redis Version Do Vector Sets Require?

Redis 8.0 or higher. The data type was introduced in Redis 8 and Redisson's RVectorSet documents the same requirement. On Redis 7 and earlier there is no vector set at all — the Query Engine, via Redis Stack, is the only route to vector search.

Do Vector Sets Work on Valkey?

No. Valkey forked from Redis at 7.4, before vector sets existed, and does not implement the V* command family. Valkey provides vector similarity search through the valkey-search module from version 8.1.1, which Redisson drives through the RSearch API rather than RVectorSet.

Why Does My VectorAddArgs Attributes Call Not Compile?

Because attributes() takes two arguments — the attributes object and a JsonCodec that serializes it — and there is no overload accepting a single JSON String. Pass Map.of(...) or your own type together with a codec such as new JacksonCodec<>(Map.class).

Why Is the Queried Element Its Own Top Result?

Because it is genuinely the nearest vector to itself, at distance zero. When you query with VectorSimilarArgs.element(...), request one more result than you need and filter the source element out of the list before rendering.

Does VEMB Return the Vector I Inserted?

Not exactly. Under the default Q8 quantization, stored values are 8-bit, so getVector() returns a dequantized approximation of the original. getRawVector() exposes the raw internal representation. If you need the exact original embedding, store it separately — a vector set is an index, not a system of record.

What Is the Difference Between VLINKS and VSIM?

VSIM answers "which elements are nearest to this one," which is the query your application makes. VLINKS exposes the HNSW graph edges for an element — which nodes the index connects it to — which is an introspection tool. Comparing the two is a useful way to tell whether unexpected results come from the index structure or from the query parameters.

Similar articles