page_type: content

title: Overview

role: overview

text:
When an older timestamp is inserted into a time series, the chunk of memory corresponding to the new sample’s time frame will potentially have to be retrieved from the main memory (you can read more about these chunks [here](https://redislabs.com/blog/redistimeseries-ga-making-4th-dimension-truly-immersive/)). When this chunk is a compressed chunk, it will also have to be decoded before we can insert/update to it. These are memory-intensive—and in the case of decoding, compute-intensive—operations that will influence the overall achievable ingestion rate.

Ingest performance is critical for us, which pushed  us to assess and be transparent about the impact of the out-of-order backfilled ratio on our overall high-performance TSDB.

To do so, we created a Go benchmark client that enabled us to control key factors that dictate overall system performance, like the out-of-order ratio, the compression of the series, the number of concurrent clients used, and command pipelining. For the full benchmark-driver configuration details and parameters, please refer to this [GitHub link](https://github.com/RedisTimeSeries/redistimeseries-ooo-benchmark).

Furthermore, all benchmark variations were run on Amazon Web Services instances, provisioned through our benchmark-testing infrastructure. Both the benchmarking client and database servers were running on separate c5.9xlarge instances. The tests were executed on a single-shard setup, with RedisTimeSeries version 1.4.

Below you can see the correlation between achievable ops/sec and out-of-order ratio for both compressed and uncompressed chunks.

title: Compressed chunks out-of-order/backfilled impact analysis

role: content

text:
With compressed chunks, given that a single out-of-order datapoint implies the full decompression from double delta of the entire chunk, you should expect higher overheads in out-of-order writes.

As a rule of thumb, to increase out-of-order compressed performance, reduce the chunk size as much as possible. Smaller chunks imply less computation on double-delta decompression and thus less overall impact, with the drawback of smaller compression ratio.

The graphs and tables below make these key points:

- If the database receives 1% of out-of-order samples with our current default chunk size in bytes (4096) the overall impact on the ingestion rate should be 10%.

- At larger out-of-order percentages, like 5%, 10%, or even 25%, the overall impact should be between 35% to 75% fewer ops/sec. At this level of out-of-order percentages, you should really consider reducing the chunk size.

- We've observed a maximum 95% drop in the achievable ops/sec even at 99% out-of-order ingestion. (Again, reducing the chunk size can cut the impact in half.)

title: Uncompressed chunks out-of-order/backfilled impact analysis

text:
As visible on the charts and tables below, the chunk size does not affect the overall out-of-order impact on ingestion (meaning that if I have a chunk size of 256 bytes and a chunk size of 4096 bytes, the expected impact that out-of-order ingestion is the same—as it should be).
Apart from that, we can observe the following key take-aways:

- If the database receives 1% of out-of-order samples, the overall impact in ingestion rate should be low or even unmeasurable.

- At higher out-of-order percentages, like  5%, 10%, or even 25%, the overall impact should be 5% to 19% fewer ops/sec.

- We've observed a maximum 45% drop in the achievable ops/sec, even at 99% out-of-order ingestion.


title: Use cases

summary: Time series use cases

title: Content

text:
**Monitoring (data center)**

Modern data centers have a lot of moving pieces, such as infrastructure (servers and networks) and software systems (applications and services) that need to be monitored around the clock.

Redis Time Series allows you to plan for new resources upfront, optimize the utilization of existing resources, reconstruct the circumstances that led to outages, and identify application performance issues by analyzing and reporting on the following metrics:

- Maximum CPU utilization per server
- Maximum network latency between two services
- Average IO bandwidth utilization of a storage system
- 99th percentile of the response time of a specific application outages

**Weather analysis (environment)**

Redis Time Series can be used to track environmental measurements such as the number of daily sunshine hours and hourly rainfall depth, over a period of many years. Seasonally, you can measure average rainfall depth, average daily temperature, and the maximum number of sunny hours per day, for example. Watch the increase of the maximum daily temperature over the years. Predict the expected temperature and rainfall depth in a specific location for a particular week of the year.

Multiple time series can be collected, each for a different location. By utilizing secondary indexes, measurements can be aggregated over given geographical regions (e.g., minimal and maximal daily temperature in Europe) or over locations with specific attributes (e.g., average rainfall depth in mountainous regions).

Example metrics include:

- Rain (cm)
- Temperature (C)
- Sunny periods (h)

**Analysis of the atmosphere (environment)**

The atmospheric concentration of CO2 is more important than ever before. Use TimeSeries to track average, maximum and minimum CO2 level per season and average yearly CO2 over the last decades. Example metrics include:

- Concentration of CO2 (ppm)
- Location

**Flight data recording (sensor data and IoT)**

Planes have a multitude of sensors. This sensor data is stored in a black box and also shared with external systems. TimeSeries can help you reconstruct the sequence of events over time, optimize operations and maintenance intervals, improve safety, and provide feedback to the equipment manufacturers about the part quality. Example metrics include:

- Altitude
- Flight path
- Engine temperature
- Level of vibrations
- Pressure

**Ship logbooks (sensor data and IoT)**

It's very common to keep track of ship voyages via (digital) logbooks. Use TimeSeries to calculate optimal routes using these metrics:

- Wind (km/h)
- Ocean conditions (classes)
- Speed (knots)
- Location (long, lat)

**Connected car (sensor data and IoT)**

Modern cars are exposing several metrics via a standard interface. Use TimeSeries to correlate average fuel consumption with the tire pressure, figure out how long to keep a car in the fleet, determine optimal maintenance intervals, and calculate tax savings by type of the road (taxable vs. nontaxable roads). Example metrics include:

- Acceleration
- Location (long, lat)
- Fuel level (liter)
- Distances (km)
- Speed (km/h)
- Tire pressure
- Distance until next maintenance check

**Smart metering (sensor data and IoT)**

Modern houses and facilities gather details about energy consumption/production. Use Redis Time Series to aggregate billing based on monthly consumption. Optimize the network by redirecting the energy delivery relative to the fluctuations in need. Provide recommendations on how to improve the energy consumption behavior. Example metrics include:

- Consumption per location
- Produced amount of electrical energy per location

**Quality of service (telecom)**

Mobile phone usage is increasing, producing a natural growth that just correlates to the increasing number of cellphones. However, there might also be spikes that correlate with specific events (for example, more messages around world championships).

Telecom providers need to ensure that they are providing the necessary infrastructure to deliver the right quality of service. This includes using mini towers for short-term peaks. Use TimeSeries to correlate traffic peaks to specific events, load balance traffic over several towers or mini towers, and predictively plan the infrastructure. Metrics include the amount of traffic per tower.

**Stock trading (finance)**

Stock trading is highly automated today. Algorithms, and not just human beings, are trading, from the amount of bids and asks for the trading of a stock to the extreme volumes of trades per second (millions of ops per second). Computer-driven trading requires millisecond response times. It's necessary to keep a lot of data points within a very short period of time (for example, price fluctuations per second within a minute). In addition, the long-term history needs to be kept to make statements about trends or for regulatory purposes.

Use Redis Time Series to identify correlations between the trading behavior and other events (for example, social network posts). Discover a developing market. Detect anomalies to discover insider trades. Example metrics include:

- Exact time and order of a trade by itself
- Type of the event (trade/bid)
- The stock price


title: Filter expressions

summary: Use filter expressions to refine vector similarity results with Redis vector sets

title: Overview

text:
Filtered search lets you combine vector similarity search with scalar filtering. You can associate JSON attributes with elements in a vector set, and then filter results using those attributes during [`VSIM`]() queries.

This allows queries such as:

[code example]

title: Assigning attributes

text:
You can associate attributes when adding a new vector using the `SETATTR` argument:

[code example]

Or update them later with the [`VSETATTR`]() command:

[code example]

You can retrieve attributes with the [`VGETATTR`]() command:

[code example]

title: Filtering during similarity search

text:
To filter by attributes, pass the `FILTER` option to the [`VSIM`]() command:

[code example]

This returns only elements that match both the vector similarity and the filter expression.

title: Expression syntax

text:
Expressions support familiar JavaScript-like syntax:

- Arithmetic: `+`, `-`, `*`, `/`, `%`, `**`
- Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`
- Logical: `and`, `or`, `not` (or `&&`, `||`, `!`)
- Containment: `in`
- Grouping: Parentheses `()`

Use dot notation to access attribute fields, for example, `.year`, `.rating`.

> Only top-level fields are supported (for example, `.genre`, but not `.movie.genre`).

title: Supported data types

role: compatibility

text:
- Numbers
- Strings
- Booleans (converted to 1 or 0)
- Arrays (for `in`)

If a field is missing or invalid, the element is skipped without error.

title: FILTER-EF

text:
The `FILTER-EF` option controls how many candidate nodes the engine inspects to find enough filtered results. The defaults is `COUNT * 100`.

[code example]

- Use a higher value for rare filters.
- Use `FILTER-EF 0` to scan as many as needed to fulfill the request.
- The engine will stop early if enough high-quality results are found.

title: Examples

role: example

text: [code example]

title: Tips

text:
- Missing attributes are treated as non-matching.
- Use `FILTER-EF` to tune recall vs performance.
- Combine multiple attributes for fine-grained filtering.

title: See also

role: related

text:
- [VSIM]()
- [VADD]()
- [VSETATTR]()
- [VGETATTR]()

code: VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.year >= 1980 and .rating > 7'

section_id: overview

code: VADD vset VALUES 3 1 1 1 a SETATTR '{"year": 1950}'

section_id: assigning-attributes

code: VSETATTR vset a '{"year": 1960}'

code: VGETATTR vset a

code: VSIM vset VALUES 3 0 0 0 FILTER '.year > 1950'

section_id: filtering-during-similarity-search

code: VSIM vset VALUES 3 0 0 0 COUNT 10 FILTER '.year > 2000' FILTER-EF 500

section_id: filter-ef

code:
# Filter by year range
VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.year >= 1980 and .year < 1990'

# Filter by genre and rating
VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.genre == "action" and .rating > 8.0'

# Use IN with array
VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '.director in ["Spielberg", "Nolan"]'

# Math and logic
VSIM movies VALUES 3 0.5 0.8 0.2 FILTER '(.year - 2000) ** 2 < 100 and .rating / 2 > 4'

section_id: examples


title: Redis vector sets

summary: Introduction to Redis vector sets

title: Filter expressions

summary: Learn how Redis vector sets behave under load and how to optimize for speed and recall

title: Performance

summary: Scale Redis vector sets to handle larger data sets and workloads

title: Scalability

summary: Learn how to optimize memory consumption in Redis vector sets

title: Memory optimization

summary: Diagnose and debug issues when working with Redis vector sets

title: Troubleshooting

title: Vector set command summary

text:
**13 commands in this group:**

[View all module commands](https://redis.io/commands/?group=vector_set)

| Command | Summary | Complexity | Since |
|---------|---------|------------|-------|
| [VADD](https://redis.io/commands/vadd/) | Add one or more elements to a vector set, or update its vector if it already exists | N/A | 8.0.0 |
| [VCARD](https://redis.io/commands/vcard/) | Return the number of elements in a vector set | N/A | 8.0.0 |
| [VDIM](https://redis.io/commands/vdim/) | Return the dimension of vectors in the vector set | N/A | 8.0.0 |
| [VEMB](https://redis.io/commands/vemb/) | Return the vector associated with an element | N/A | 8.0.0 |
| [VGETATTR](https://redis.io/commands/vgetattr/) | Retrieve the JSON attributes of elements | N/A | 8.0.0 |
| [VINFO](https://redis.io/commands/vinfo/) | Return information about a vector set | N/A | 8.0.0 |
| [VISMEMBER](https://redis.io/commands/vismember/) | Check if an element exists in a vector set | N/A | 8.2.0 |
| [VLINKS](https://redis.io/commands/vlinks/) | Return the neighbors of an element at each layer in the HNSW graph | N/A | 8.0.0 |
| [VRANDMEMBER](https://redis.io/commands/vrandmember/) | Return one or multiple random members from a vector set | N/A | 8.0.0 |
| [VRANGE](https://redis.io/commands/vrange/) | Return vector set elements in a lex range | N/A | 8.4.0 |
| [VREM](https://redis.io/commands/vrem/) | Remove an element from a vector set | N/A | 8.0.0 |
| [VSETATTR](https://redis.io/commands/vsetattr/) | Associate or remove the JSON attributes of elements | N/A | 8.0.0 |
| [VSIM](https://redis.io/commands/vsim/) | Return elements by vector similarity | N/A | 8.0.0 |

Vector sets are a data type similar to sorted sets, but instead of a score, vector set elements have a string representation of a vector.
Vector sets allow you to add items to a set, and then either:

* retrieve a subset of items that are the most similar to a specified vector, or
* retrieve a subset of items that are the most similar to the vector of an element that is already part of the vector set.

Vector sets also provide for optional [filtered search](). You can associate attributes with all or some elements in a vector set, and then use the `FILTER` option of the [`VSIM`]() command to retrieve items similar to a given vector while applying simple mathematical filters to those attributes. Here's a sample filter: `".year > 1950"`.

title: Endianness considerations for FP32 format

text:
When using the FP32 blob format with vector set commands like [`VADD`]() and [`VSIM`](), the binary data must be encoded in little-endian byte order. This is important for cross-platform compatibility, as some ARM variants and other architectures may use different endianness.

If your platform uses big-endian or mixed-endian encoding, you have two options:
- Manually convert the byte order to little-endian before passing the blob to Redis.
- Use the `VALUES` syntax instead, which accepts floating-point numbers as strings and is platform-independent.

title: Examples

text:
The following examples give an overview of how to use vector sets. For clarity,
we will use a set of two-dimensional vectors that represent points in the
Cartesian coordinate plane. However, in real use cases, the vectors will typically
represent *text embeddings* and have hundreds of dimensions. See
[Redis for AI]() for more information about using text
embeddings.

The points we will use are A: (1.0, 1.0), B: (-1.0, -1.0), C: (-1.0, 1.0), D: (1.0. -1.0), and
E: (1.0, 0), shown in the diagram below.

title: Basic operations

text:
Start by adding the point vectors to a set called `points` using
[`VADD`](). This also creates the vector set object.
The [`TYPE`]() command returns a type of `vectorset`
for this object.

#### Code Examples

Foundational: Use VADD to create a new vector set and populate it with vectors

**Commands:** VADD, TYPE

**Complexity:**
- TYPE: O(1)

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Get the number of elements in the set (also known as the *cardinality* of the set)
using [`VCARD`]() and the number of dimensions of
the vectors using [`VDIM`]():

#### Code Examples

Metadata retrieval: Use VCARD to get the number of elements and VDIM to get vector dimensions when you need to inspect vector set properties

**Builds upon:** vadd

**Commands:** VCARD, VDIM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Get the coordinate values from the elements using [`VEMB`]().
Note that the values will not typically be the exact values you supplied when you added
the vector because
[quantization]()
is applied to improve performance.

#### Code Examples

Vector retrieval: Use VEMB to retrieve the approximate vector values of elements when you need to inspect the actual vector data stored in the set

**Builds upon:** vadd

**Commands:** VEMB

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Remove an unwanted element with [`VREM`]()

#### Code Examples

Element removal: Use VREM to delete elements from a vector set when you need to remove vectors from the collection

**Builds upon:** vadd

**Commands:** VADD, VCARD, VREM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Set and retrieve an element's JSON attribute data using
[`VSETATTR`]()
and [`VGETATTR`](). You can also pass an empty string
to `VSETATTR` to delete the attribute data:

#### Code Examples

Attribute management: Use VSETATTR to store JSON attributes on elements and VGETATTR to retrieve them when you need to associate metadata with vectors

**Builds upon:** vadd

**Commands:** VSETATTR, VGETATTR

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

title: Vector similarity search

text:
Use [`VSIM`]() to rank the points in order of their vector distance from a sample point:

#### Code Examples

Similarity search: Use VSIM to find elements most similar to a query vector when you need to perform vector similarity searches

**Difficulty:** Intermediate

**Builds upon:** vadd

**Commands:** VSIM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Find the four elements that are closest to point A and show their distance "scores":

#### Code Examples

Similarity with options: Use VSIM with ELE, WITHSCORES, and COUNT options to find similar elements with scores and limits when you need detailed similarity results

**Difficulty:** Intermediate

**Builds upon:** vadd

**Commands:** VSIM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

Add some JSON attributes and use
[filter expressions]()
to include them in the search:

#### Code Examples

Filtered similarity search: Use VSIM with FILTER option to apply attribute-based conditions to similarity results when you need to combine vector similarity with attribute filtering

**Difficulty:** Advanced

**Builds upon:** vadd

**Commands:** VSETATTR, VSIM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

title: More information

text:
See the other pages in this section to learn more about the features
and performance parameters of vector sets.

code:
> VADD points VALUES 2 1.0 1.0 pt:A
(integer) 1
> VADD points VALUES 2 -1.0 -1.0 pt:B
(integer) 1
> VADD points VALUES 2 -1.0 1.0 pt:C
(integer) 1
> VADD points VALUES 2 1.0 -1.0 pt:D
(integer) 1
> VADD points VALUES 2 1.0 0 pt:E
(integer) 1
> TYPE points
vectorset

section_id: basic-operations

code:
bool r1 = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:A", new float[] { 1f, 1f }, null));
        Console.WriteLine(r1); // >>> True

        bool r2 = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:B", new float[] { -1f, -1f }, null));
        Console.WriteLine(r2); // >>> True

        bool r3 = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:C", new float[] { -1f, 1f }, null));
        Console.WriteLine(r3); // >>> True

        bool r4 = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:D", new float[] { 1f, -1f }, null));
        Console.WriteLine(r4); // >>> True

        bool r5 = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:E", new float[] { 1f, 0f }, null));
        Console.WriteLine(r5); // >>> True

code:
res1, err := rdb.VAdd(ctx, "points", "pt:A",
		&redis.VectorValues{Val: []float64{1.0, 1.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> true

	res2, err := rdb.VAdd(ctx, "points", "pt:B",
		&redis.VectorValues{Val: []float64{-1.0, -1.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> true

	res3, err := rdb.VAdd(ctx, "points", "pt:C",
		&redis.VectorValues{Val: []float64{-1.0, 1.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> true

	res4, err := rdb.VAdd(ctx, "points", "pt:D",
		&redis.VectorValues{Val: []float64{1.0, -1.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4) // >>> true

	res5, err := rdb.VAdd(ctx, "points", "pt:E",
		&redis.VectorValues{Val: []float64{1.0, 0.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res5) // >>> true

	res6, err := rdb.Type(ctx, "points").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res6) // >>> vectorset

code:
CompletableFuture<Boolean> addPointA = asyncCommands.vadd("points", "pt:A", 1.0, 1.0).thenApply(result -> {
                System.out.println(result); // >>> true
                return result;
            }).toCompletableFuture();

            CompletableFuture<Boolean> addPointB = asyncCommands.vadd("points", "pt:B", -1.0, -1.0).thenApply(result -> {
                System.out.println(result); // >>> true
                return result;
            }).toCompletableFuture();

            CompletableFuture<Boolean> addPointC = asyncCommands.vadd("points", "pt:C", -1.0, 1.0).thenApply(result -> {
                System.out.println(result); // >>> true
                return result;
            }).toCompletableFuture();

            CompletableFuture<Boolean> addPointD = asyncCommands.vadd("points", "pt:D", 1.0, -1.0).thenApply(result -> {
                System.out.println(result); // >>> true
                return result;
            }).toCompletableFuture();

            CompletableFuture<Boolean> addPointE = asyncCommands.vadd("points", "pt:E", 1.0, 0.0).thenApply(result -> {
                System.out.println(result); // >>> true
                return result;
            }).toCompletableFuture();

            // Chain checkDataType after all vadd operations complete
            CompletableFuture<Void> vaddOperations = CompletableFuture
                    .allOf(addPointA, addPointB, addPointC, addPointD, addPointE)
                    .thenCompose(ignored -> asyncCommands.type("points")).thenAccept(result -> {
                        System.out.println(result); // >>> vectorset
                    }).toCompletableFuture();

code:
Mono<Boolean> addPointA = reactiveCommands.vadd("points", "pt:A", 1.0, 1.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Boolean> addPointB = reactiveCommands.vadd("points", "pt:B", -1.0, -1.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Boolean> addPointC = reactiveCommands.vadd("points", "pt:C", -1.0, 1.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Boolean> addPointD = reactiveCommands.vadd("points", "pt:D", 1.0, -1.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Boolean> addPointE = reactiveCommands.vadd("points", "pt:E", 1.0, 0.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Void> vaddOperations = Mono.when(addPointA, addPointB, addPointC, addPointD, addPointE)
                    .then(reactiveCommands.type("points")).doOnNext(result -> {
                        System.out.println(result); // >>> vectorset
                    }).then();

code:
boolean res1 = jedis.vadd("points", new float[] { 1.0f, 1.0f }, "pt:A");
      System.out.println(res1); // >>> true

      boolean res2 = jedis.vadd("points", new float[] { -1.0f, -1.0f }, "pt:B");
      System.out.println(res2); // >>> true

      boolean res3 = jedis.vadd("points", new float[] { -1.0f, 1.0f }, "pt:C");
      System.out.println(res3); // >>> true

      boolean res4 = jedis.vadd("points", new float[] { 1.0f, -1.0f }, "pt:D");
      System.out.println(res4); // >>> true

      boolean res5 = jedis.vadd("points", new float[] { 1.0f, 0.0f }, "pt:E");
      System.out.println(res5); // >>> true

      String res6 = jedis.type("points");
      System.out.println(res6); // >>> vectorset

code:
const res1 = await client.vAdd("points", [1.0, 1.0], "pt:A");
console.log(res1);  // >>> true

const res2 = await client.vAdd("points", [-1.0, -1.0], "pt:B");
console.log(res2);  // >>> true

const res3 = await client.vAdd("points", [-1.0, 1.0], "pt:C");
console.log(res3);  // >>> true

const res4 = await client.vAdd("points", [1.0, -1.0], "pt:D");
console.log(res4);  // >>> true

const res5 = await client.vAdd("points", [1.0, 0], "pt:E");
console.log(res5);  // >>> true

const res6 = await client.type("points");
console.log(res6);  // >>> vectorset

code:
res1 = r.vset().vadd("points", [1.0, 1.0], "pt:A")
print(res1)  # >>> 1

res2 = r.vset().vadd("points", [-1.0, -1.0], "pt:B")
print(res2)  # >>> 1

res3 = r.vset().vadd("points", [-1.0, 1.0], "pt:C")
print(res3)  # >>> 1

res4 = r.vset().vadd("points", [1.0, -1.0], "pt:D")
print(res4)  # >>> 1

res5 = r.vset().vadd("points", [1.0, 0], "pt:E")
print(res5)  # >>> 1

res6 = r.type("points")
print(res6)  # >>> vectorset

code:
> VCARD points
(integer) 5
> VDIM points
(integer) 2

code:
long card = db.VectorSetLength("points");
        Console.WriteLine(card); // >>> 5

        int dim = db.VectorSetDimension("points");
        Console.WriteLine(dim); // >>> 2

code:
res7, err := rdb.VCard(ctx, "points").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res7) // >>> 5

	res8, err := rdb.VDim(ctx, "points").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res8) // >>> 2

code:
CompletableFuture<Void> getCardinality = asyncCommands.vcard("points").thenAccept(result -> {
                System.out.println(result); // >>> 5
            }).toCompletableFuture();

            CompletableFuture<Void> getDimensions = asyncCommands.vdim("points").thenAccept(result -> {
                System.out.println(result); // >>> 2
            }).toCompletableFuture();

code:
Mono<Long> getCardinality = reactiveCommands.vcard("points").doOnNext(result -> {
                System.out.println(result); // >>> 5
            });

            Mono<Long> getDimensions = reactiveCommands.vdim("points").doOnNext(result -> {
                System.out.println(result); // >>> 2
            });

code:
long res7 = jedis.vcard("points");
      System.out.println(res7); // >>> 5

      long res8 = jedis.vdim("points");
      System.out.println(res8); // >>> 2

code:
const res7 = await client.vCard("points");
console.log(res7);  // >>> 5

const res8 = await client.vDim("points");
console.log(res8);  // >>> 2

code:
$res7 = $r->vcard('points');
        echo $res7 . PHP_EOL;
        // >>> 5

        $res8 = $r->vdim('points');
        echo $res8 . PHP_EOL;
        // >>> 2

code:
res7 = r.vset().vcard("points")
print(res7)  # >>> 5

res8 = r.vset().vdim("points")
print(res8)  # >>> 2

code:
using (Lease<float>? eA = db.VectorSetGetApproximateVector("points", "pt:A"))
        {
            Span<float> a = eA!.Span;
            Console.WriteLine($"[{a[0]}, {a[1]}]"); // >>> [0.9999999403953552, 0.9999999403953552]
        }
        using (Lease<float>? eB = db.VectorSetGetApproximateVector("points", "pt:B"))
        {
            Span<float> b = eB!.Span;
            Console.WriteLine($"[{b[0]}, {b[1]}]"); // >>> [-0.9999999403953552, -0.9999999403953552]
        }
        using (Lease<float>? eC = db.VectorSetGetApproximateVector("points", "pt:C"))
        {
            Span<float> c = eC!.Span;
            Console.WriteLine($"[{c[0]}, {c[1]}]"); // >>> [-0.9999999403953552, 0.9999999403953552]
        }
        using (Lease<float>? eD = db.VectorSetGetApproximateVector("points", "pt:D"))
        {
            Span<float> d = eD!.Span;
            Console.WriteLine($"[{d[0]}, {d[1]}]"); // >>> [0.9999999403953552, -0.9999999403953552]
        }
        using (Lease<float>? eE = db.VectorSetGetApproximateVector("points", "pt:E"))
        {
            Span<float> e = eE!.Span;
            Console.WriteLine($"[{e[0]}, {e[1]}]"); // >>> [1, 0]
        }

code:
CompletableFuture<Void> getEmbeddingA = asyncCommands.vemb("points", "pt:A").thenAccept(result -> {
                System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]
            }).toCompletableFuture();

            CompletableFuture<Void> getEmbeddingB = asyncCommands.vemb("points", "pt:B").thenAccept(result -> {
                System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]
            }).toCompletableFuture();

            CompletableFuture<Void> getEmbeddingC = asyncCommands.vemb("points", "pt:C").thenAccept(result -> {
                System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]
            }).toCompletableFuture();

            CompletableFuture<Void> getEmbeddingD = asyncCommands.vemb("points", "pt:D").thenAccept(result -> {
                System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]
            }).toCompletableFuture();

            CompletableFuture<Void> getEmbeddingE = asyncCommands.vemb("points", "pt:E").thenAccept(result -> {
                System.out.println(result); // >>> [1.0, 0.0]
            }).toCompletableFuture();

code:
Mono<java.util.List<Double>> getEmbeddingA = reactiveCommands.vemb("points", "pt:A").collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [0.9999999403953552, 0.9999999403953552]
                    });

            Mono<java.util.List<Double>> getEmbeddingB = reactiveCommands.vemb("points", "pt:B").collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [-0.9999999403953552, -0.9999999403953552]
                    });

            Mono<java.util.List<Double>> getEmbeddingC = reactiveCommands.vemb("points", "pt:C").collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [-0.9999999403953552, 0.9999999403953552]
                    });

            Mono<java.util.List<Double>> getEmbeddingD = reactiveCommands.vemb("points", "pt:D").collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [0.9999999403953552, -0.9999999403953552]
                    });

            Mono<java.util.List<Double>> getEmbeddingE = reactiveCommands.vemb("points", "pt:E").collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [1.0, 0.0]
                    });

code:
List<Double> res9 = jedis.vemb("points", "pt:A");
      System.out.println(res9); // >>> [0.9999999..., 0.9999999...]

      List<Double> res10 = jedis.vemb("points", "pt:B");
      System.out.println(res10); // >>> [-0.9999999..., -0.9999999...]

      List<Double> res11 = jedis.vemb("points", "pt:C");
      System.out.println(res11); // >>> [-0.9999999..., 0.9999999...]

      List<Double> res12 = jedis.vemb("points", "pt:D");
      System.out.println(res12); // >>> [0.9999999..., -0.9999999...]

      List<Double> res13 = jedis.vemb("points", "pt:E");
      System.out.println(res13); // >>> [1, 0]

code:
> VADD points VALUES 2 0 0 pt:F
(integer) 1
> VCARD points
(integer) 6
> VREM points pt:F
(integer) 1
> VCARD points
(integer) 5

code:
bool addF = db.VectorSetAdd("points", VectorSetAddRequest.Member("pt:F", new float[] { 0f, 0f }, null));
        Console.WriteLine(addF); // >>> True

        long card1 = db.VectorSetLength("points");
        Console.WriteLine(card1); // >>> 6

        bool remF = db.VectorSetRemove("points", "pt:F");
        Console.WriteLine(remF); // >>> True

        long card2 = db.VectorSetLength("points");
        Console.WriteLine(card2); // >>> 5

code:
res18, err := rdb.VAdd(ctx, "points", "pt:F",
		&redis.VectorValues{Val: []float64{0.0, 0.0}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res18) // >>> true

	res19, err := rdb.VCard(ctx, "points").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res19) // >>> 6

	res20, err := rdb.VRem(ctx, "points", "pt:F").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res20) // >>> true

	res21, err := rdb.VCard(ctx, "points").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res21) // >>> 5

code:
CompletableFuture<Void> addTempPointF = asyncCommands.vadd("points", "pt:F", 0.0, 0.0).thenAccept(result -> {
                System.out.println(result); // >>> true
            }).toCompletableFuture();

            CompletableFuture<Void> checkCardinalityBefore = asyncCommands.vcard("points").thenAccept(result -> {
                System.out.println(result); // >>> 6
            }).toCompletableFuture();

            CompletableFuture<Void> removePointF = asyncCommands.vrem("points", "pt:F").thenAccept(result -> {
                System.out.println(result); // >>> true
            }).toCompletableFuture();

            CompletableFuture<Void> checkCardinalityAfter = asyncCommands.vcard("points").thenAccept(result -> {
                System.out.println(result); // >>> 5
            }).toCompletableFuture();

code:
Mono<Boolean> addTempPointF = reactiveCommands.vadd("points", "pt:F", 0.0, 0.0).doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Long> checkCardinalityBefore = reactiveCommands.vcard("points").doOnNext(result -> {
                System.out.println(result); // >>> 6
            });

            Mono<Boolean> removePointF = reactiveCommands.vrem("points", "pt:F").doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<Long> checkCardinalityAfter = reactiveCommands.vcard("points").doOnNext(result -> {
                System.out.println(result); // >>> 5
            });

code:
boolean res18 = jedis.vadd("points", new float[] { 0.0f, 0.0f }, "pt:F");
      System.out.println(res18); // >>> true

      long res19 = jedis.vcard("points");
      System.out.println(res19); // >>> 6

      boolean res20 = jedis.vrem("points", "pt:F");
      System.out.println(res20); // >>> true

      long res21 = jedis.vcard("points");
      System.out.println(res21); // >>> 5

code:
const res18 = await client.vAdd("points", [0, 0], "pt:F");
console.log(res18);  // >>> true

const res19 = await client.vCard("points");
console.log(res19);  // >>> 6

const res20 = await client.vRem("points", "pt:F");
console.log(res20);  // >>> true

const res21 = await client.vCard("points");
console.log(res21);  // >>> 5

code:
$res18 = $r->vadd('points', [0, 0], 'pt:F');
        echo $res18 . PHP_EOL;
        // >>> 1

        $res19 = $r->vcard('points');
        echo $res19 . PHP_EOL;
        // >>> 6

        $res20 = $r->vrem('points', 'pt:F');
        echo $res20 . PHP_EOL;
        // >>> 1

        $res21 = $r->vcard('points');
        echo $res21 . PHP_EOL;
        // >>> 5

code:
res18 = r.vset().vadd("points", [0, 0], "pt:F")
print(res18)  # >>> 1

res19 = r.vset().vcard("points")
print(res19)  # >>> 6

res20 = r.vset().vrem("points", "pt:F")
print(res20)  # >>> 1

res21 = r.vset().vcard("points")
print(res21)  # >>> 5

code:
> VSETATTR points pt:A "{\"name\": \"Point A\", \"description\": \"First point added\"}"
(integer) 1
> VGETATTR points pt:A
"{\"name\": \"Point A\", \"description\": \"First point added\"}"
> VSETATTR points pt:A ""
(integer) 1
> VGETATTR points pt:A
(nil)

code:
string attrJson = "{\"name\":\"Point A\",\"description\":\"First point added\"}";
        bool setAttr1 = db.VectorSetSetAttributesJson("points", "pt:A", attrJson);
        Console.WriteLine(setAttr1); // >>> True

        string? getAttr1 = db.VectorSetGetAttributesJson("points", "pt:A");
        Console.WriteLine(getAttr1); // >>> {"name":"Point A","description":"First point added"}

        bool clearAttr = db.VectorSetSetAttributesJson("points", "pt:A", "");
        Console.WriteLine(clearAttr); // >>> True

        string? getAttr2 = db.VectorSetGetAttributesJson("points", "pt:A");
        Console.WriteLine(getAttr2 is null ? "None" : getAttr2); // >>> None

code:
attrs := map[string]interface{}{
		"name":        "Point A",
		"description": "First point added",
	}

	res14, err := rdb.VSetAttr(ctx, "points", "pt:A", attrs).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res14) // >>> true

	res15, err := rdb.VGetAttr(ctx, "points", "pt:A").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res15)
	// >>> {"description":"First point added","name":"Point A"}

	res16, err := rdb.VClearAttributes(ctx, "points", "pt:A").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res16) // >>> true

	// `VGetAttr()` returns an error if the attribute doesn't exist.
	_, err = rdb.VGetAttr(ctx, "points", "pt:A").Result()

	if err != nil {
		fmt.Println(err)
	}

code:
CompletableFuture<Void> setAttributeA = asyncCommands
                    .vsetattr("points", "pt:A", "{\"name\": \"Point A\", \"description\": \"First point added\"}")
                    .thenAccept(result -> {
                        System.out.println(result); // >>> true
                    }).toCompletableFuture();

            CompletableFuture<Void> getAttributeA = asyncCommands.vgetattr("points", "pt:A").thenAccept(result -> {
                System.out.println(result); // >>> {"name": "Point A", "description": "First point added"}
            }).toCompletableFuture();

            CompletableFuture<Void> clearAttributeA = asyncCommands.vsetattr("points", "pt:A", "").thenAccept(result -> {
                System.out.println(result); // >>> true
            }).toCompletableFuture();

            CompletableFuture<Void> verifyAttributeCleared = asyncCommands.vgetattr("points", "pt:A").thenAccept(result -> {
                System.out.println(result); // >>> null
            }).toCompletableFuture();

code:
Mono<Boolean> setAttributeA = reactiveCommands
                    .vsetattr("points", "pt:A", "{\"name\": \"Point A\", \"description\": \"First point added\"}")
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    });

            Mono<String> getAttributeA = reactiveCommands.vgetattr("points", "pt:A").doOnNext(result -> {
                System.out.println(result); // >>> {"name": "Point A", "description": "First point added"}
            });

            Mono<Boolean> clearAttributeA = reactiveCommands.vsetattr("points", "pt:A", "").doOnNext(result -> {
                System.out.println(result); // >>> true
            });

            Mono<String> verifyAttributeCleared = reactiveCommands.vgetattr("points", "pt:A").doOnNext(result -> {
                System.out.println(result); // >>> null
            });

code:
boolean res14 = jedis.vsetattr("points", "pt:A",
        "{\"name\":\"Point A\",\"description\":\"First point added\"}");
      System.out.println(res14); // >>> true

      String res15 = jedis.vgetattr("points", "pt:A");
      System.out.println(res15);
      // >>> {"name":"Point A","description":"First point added"}

      boolean res16 = jedis.vsetattr("points", "pt:A", "");
      System.out.println(res16); // >>> true

      String res17 = jedis.vgetattr("points", "pt:A");
      System.out.println(res17); // >>> null

code:
const res14 = await client.vSetAttr("points", "pt:A", {
  name: "Point A",
  description: "First point added"
});
console.log(res14);  // >>> true

const res15 = await client.vGetAttr("points", "pt:A");
console.log(res15);
// >>> {name: 'Point A', description: 'First point added'}

const res16 = await client.vSetAttr("points", "pt:A", "");
console.log(res16);  // >>> true

const res17 = await client.vGetAttr("points", "pt:A");
console.log(res17);  // >>> null

code:
$res14 = $r->vsetattr('points', 'pt:A', '{
            "name": "Point A",
            "description": "First point added"
        }');
        echo $res14 . PHP_EOL;
        // >>> 1

        $res15 = $r->vgetattr('points', 'pt:A');
        echo json_encode($res15) . PHP_EOL;
        // >>> {"name":"Point A","description":"First point added"}

        $res16 = $r->vsetattr('points', 'pt:A', '');
        echo $res16 . PHP_EOL;
        // >>> 1

        $res17 = $r->vgetattr('points', 'pt:A');
        echo json_encode($res17) . PHP_EOL;
        // >>> null

code:
res14 = r.vset().vsetattr("points", "pt:A", {
    "name": "Point A",
    "description": "First point added"
})
print(res14)  # >>> 1

res15 = r.vset().vgetattr("points", "pt:A")
print(res15)
# >>> {'name': 'Point A', 'description': 'First point added'}

res16 = r.vset().vsetattr("points", "pt:A", "")
print(res16)  # >>> 1

res17 = r.vset().vgetattr("points", "pt:A")
print(res17)  # >>> None

section_id: vector-similarity-search

code:
VectorSetSimilaritySearchRequest qBasic = VectorSetSimilaritySearchRequest.ByVector(new float[] { 0.9f, 0.1f });
        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch("points", qBasic))
        {
            VectorSetSimilaritySearchResult[] items = res!.Span.ToArray();
            string[] ordered = items.Select(x => (string?)x.Member).Where(s => s is not null).Select(s => s!).ToArray();
            Console.WriteLine("[" + string.Join(", ", ordered.Select(s => $"'{s}'")) + "]");
            // >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']
        }

code:
res22, err := rdb.VSim(ctx, "points",
		&redis.VectorValues{Val: []float64{0.9, 0.1}},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res22) // >>> [pt:E pt:A pt:D pt:C pt:B]

code:
CompletableFuture<Void> basicSimilaritySearch = asyncCommands.vsim("points", 0.9, 0.1).thenAccept(result -> {
                System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]
            }).toCompletableFuture();

code:
Mono<java.util.List<String>> basicSimilaritySearch = reactiveCommands.vsim("points", 0.9, 0.1).collectList()
                    .doOnNext(result -> {
                        System.out.println(result); // >>> [pt:E, pt:A, pt:D, pt:C, pt:B]
                    });

code:
List<String> res22 = jedis.vsim("points", new float[] { 0.9f, 0.1f });
      System.out.println(res22);
      // >>> ["pt:E", "pt:A", "pt:D", "pt:C", "pt:B"]

code:
const res22 = await client.vSim("points", [0.9, 0.1]);
console.log(res22);
// >>> ['pt:E', 'pt:A', 'pt:D', 'pt:C', 'pt:B']

code:
VectorSetSimilaritySearchRequest qOpts = VectorSetSimilaritySearchRequest.ByMember("pt:A");
        qOpts.WithScores = true;
        qOpts.Count = 4;
        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch("points", qOpts))
        {
            VectorSetSimilaritySearchResult[] items = res!.Span.ToArray();
            Dictionary<string, double> dict = items
                .Select(i => new { Key = (string?)i.Member, i.Score })
                .Where(x => x.Key is not null)
                .ToDictionary(x => x.Key!, x => x.Score);
            Console.WriteLine("{" + string.Join(", ", dict.Select(kv => $"'{kv.Key}': {kv.Value}")) + "}");
            // >>> {'pt:A': 1.0, 'pt:E': 0.8535534143447876, 'pt:D': 0.5, 'pt:C': 0.5}
        }

code:
res23, err := rdb.VSimWithArgsWithScores(
		ctx,
		"points",
		&redis.VectorRef{Name: "pt:A"},
		&redis.VSimArgs{Count: 4},
	).Result()

	if err != nil {
		panic(err)
	}

	sort.Slice(res23, func(i, j int) bool {
		return res23[i].Name < res23[j].Name
	})

	fmt.Println(res23)
	// >>> [{pt:A 1} {pt:C 0.5} {pt:D 0.5} {pt:E 0.8535534143447876}]

code:
VSimArgs vsimArgs = new VSimArgs();
            vsimArgs.count(4L);
            CompletableFuture<Void> similaritySearchWithScore = asyncCommands.vsimWithScore("points", vsimArgs, "pt:A")
                    .thenAccept(result -> {
                        System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}
                    }).toCompletableFuture();

code:
VSimArgs vsimArgs = new VSimArgs();
            vsimArgs.count(4L);
            Mono<java.util.Map<String, Double>> similaritySearchWithScore = reactiveCommands
                    .vsimWithScore("points", vsimArgs, "pt:A").doOnNext(result -> {
                        System.out.println(result); // >>> {pt:A=1.0, pt:E=0.8535534143447876, pt:D=0.5, pt:C=0.5}
                    });

code:
Map<String, Double> res23 = jedis.vsimByElementWithScores("points", "pt:A",
        new VSimParams().count(4));
      System.out.println(res23);
      // >>> {pt:A=1.0, pt:E≈0.85355, pt:D=0.5, pt:C=0.5}

code:
const res23 = await client.vSimWithScores("points", "pt:A", { COUNT: 4 });
console.log(res23);
// >>> {pt:A: 1.0, pt:E: 0.8535534143447876, pt:D: 0.5, pt:C: 0.5}

code:
> VSETATTR points pt:A "{\"size\":\"large\",\"price\": 18.99}"
(integer) 1
> VSETATTR points pt:B "{\"size\":\"large\",\"price\": 35.99}"
(integer) 1
> VSETATTR points pt:C "{\"size\":\"large\",\"price\": 25.99}"
(integer) 1
> VSETATTR points pt:D "{\"size\":\"small\",\"price\": 21.00}"
(integer) 1
> VSETATTR points pt:E "{\"size\":\"small\",\"price\": 17.75}"
(integer) 1

# Return elements in order of distance from point A whose
# `size` attribute is `large`.
> VSIM points ELE pt:A FILTER '.size == "large"'
1) "pt:A"
2) "pt:C"
3) "pt:B"

# Return elements in order of distance from point A whose size is
# `large` and whose price is greater than 20.00.
> VSIM points ELE pt:A FILTER '.size == "large" && .price > 20.00'
1) "pt:C"
2) "pt:B"

code:
bool okA = db.VectorSetSetAttributesJson("points", "pt:A", "{\"size\":\"large\",\"price\":18.99}");
        Console.WriteLine(okA); // >>> True
        bool okB = db.VectorSetSetAttributesJson("points", "pt:B", "{\"size\":\"large\",\"price\":35.99}");
        Console.WriteLine(okB); // >>> True
        bool okC = db.VectorSetSetAttributesJson("points", "pt:C", "{\"size\":\"large\",\"price\":25.99}");
        Console.WriteLine(okC); // >>> True
        bool okD = db.VectorSetSetAttributesJson("points", "pt:D", "{\"size\":\"small\",\"price\":21.00}");
        Console.WriteLine(okD); // >>> True
        bool okE = db.VectorSetSetAttributesJson("points", "pt:E", "{\"size\":\"small\",\"price\":17.75}");
        Console.WriteLine(okE); // >>> True

        VectorSetSimilaritySearchRequest qFilt1 = VectorSetSimilaritySearchRequest.ByMember("pt:A");
        qFilt1.FilterExpression = ".size == \"large\"";
        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch("points", qFilt1))
        {
            string[] ids = res!.Span.ToArray()
                .Select(i => (string?)i.Member)
                .Where(s => s is not null)
                .Select(s => s!)
                .ToArray();
            Console.WriteLine("[" + string.Join(", ", ids.Select(s => $"'{s}'")) + "]");
            // >>> ['pt:A', 'pt:C', 'pt:B']

        }

        VectorSetSimilaritySearchRequest qFilt2 = VectorSetSimilaritySearchRequest.ByMember("pt:A");
        qFilt2.FilterExpression = ".size == \"large\" && .price > 20.00";
        using (Lease<VectorSetSimilaritySearchResult>? res = db.VectorSetSimilaritySearch("points", qFilt2))
        {
            string[] ids = res!.Span.ToArray()
                .Select(i => (string?)i.Member)
                .Where(s => s is not null)
                .Select(s => s!)
                .ToArray();
            Console.WriteLine("[" + string.Join(", ", ids.Select(s => $"'{s}'")) + "]");
            // >>> ['pt:C', 'pt:B']
        }

code:
// Set attributes for filtering
	res24, err := rdb.VSetAttr(ctx, "points", "pt:A",
		map[string]interface{}{
			"size":  "large",
			"price": 18.99,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res24) // >>> true

	res25, err := rdb.VSetAttr(ctx, "points", "pt:B",
		map[string]interface{}{
			"size":  "large",
			"price": 35.99,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res25) // >>> true

	res26, err := rdb.VSetAttr(ctx, "points", "pt:C",
		map[string]interface{}{
			"size":  "large",
			"price": 25.99,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res26) // >>> true

	res27, err := rdb.VSetAttr(ctx, "points", "pt:D",
		map[string]interface{}{
			"size":  "small",
			"price": 21.00,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res27) // >>> true

	res28, err := rdb.VSetAttr(ctx, "points", "pt:E",
		map[string]interface{}{
			"size":  "small",
			"price": 17.75,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res28) // >>> true

	// Return elements in order of distance from point A whose
	// `size` attribute is `large`.
	res29, err := rdb.VSimWithArgs(ctx, "points",
		&redis.VectorRef{Name: "pt:A"},
		&redis.VSimArgs{Filter: `.size == "large"`},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res29) // >>> [pt:A pt:C pt:B]

	// Return elements in order of distance from point A whose size is
	// `large` and whose price is greater than 20.00.
	res30, err := rdb.VSimWithArgs(ctx, "points",
		&redis.VectorRef{Name: "pt:A"},
		&redis.VSimArgs{Filter: `.size == "large" && .price > 20.00`},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res30) // >>> [pt:C pt:B]

code:
CompletableFuture<Void> filteredSimilaritySearch = asyncCommands
                    .vsetattr("points", "pt:A", "{\"size\": \"large\", \"price\": 18.99}").thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vsetattr("points", "pt:B", "{\"size\": \"large\", \"price\": 35.99}");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vsetattr("points", "pt:C", "{\"size\": \"large\", \"price\": 25.99}");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vsetattr("points", "pt:D", "{\"size\": \"small\", \"price\": 21.00}");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vsetattr("points", "pt:E", "{\"size\": \"small\", \"price\": 17.75}");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true

                        // Return elements in order of distance from point A whose size attribute is large.
                        VSimArgs filterArgs = new VSimArgs();
                        filterArgs.filter(".size == \"large\"");
                        return asyncCommands.vsim("points", filterArgs, "pt:A");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> [pt:A, pt:C, pt:B]

                        // Return elements in order of distance from point A whose size is large and price > 20.00.
                        VSimArgs filterArgs2 = new VSimArgs();
                        filterArgs2.filter(".size == \"large\" && .price > 20.00");
                        return asyncCommands.vsim("points", filterArgs2, "pt:A");
                    }).thenAccept(result -> {
                        System.out.println(result); // >>> [pt:C, pt:B]
                    }).toCompletableFuture();

code:
Mono<Void> filteredSimilaritySearch = reactiveCommands
                    .vsetattr("points", "pt:A", "{\"size\": \"large\", \"price\": 18.99}").doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vsetattr("points", "pt:B", "{\"size\": \"large\", \"price\": 35.99}"))
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vsetattr("points", "pt:C", "{\"size\": \"large\", \"price\": 25.99}"))
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vsetattr("points", "pt:D", "{\"size\": \"small\", \"price\": 21.00}"))
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vsetattr("points", "pt:E", "{\"size\": \"small\", \"price\": 17.75}"))
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> {
                        // Return elements in order of distance from point A whose size attribute is large.
                        VSimArgs filterArgs = new VSimArgs();
                        filterArgs.filter(".size == \"large\"");
                        return reactiveCommands.vsim("points", filterArgs, "pt:A").collectList();
                    }).doOnNext(result -> {
                        System.out.println(result); // >>> [pt:A, pt:C, pt:B]
                    }).flatMap(result -> {
                        // Return elements in order of distance from point A whose size is large and price > 20.00.
                        VSimArgs filterArgs2 = new VSimArgs();
                        filterArgs2.filter(".size == \"large\" && .price > 20.00");
                        return reactiveCommands.vsim("points", filterArgs2, "pt:A").collectList();
                    }).doOnNext(result -> {
                        System.out.println(result); // >>> [pt:C, pt:B]
                    }).then();

code:
boolean res24 = jedis.vsetattr("points", "pt:A", "{\"size\":\"large\",\"price\":18.99}");
      System.out.println(res24); // >>> true

      boolean res25 = jedis.vsetattr("points", "pt:B", "{\"size\":\"large\",\"price\":35.99}");
      System.out.println(res25); // >>> true

      boolean res26 = jedis.vsetattr("points", "pt:C", "{\"size\":\"large\",\"price\":25.99}");
      System.out.println(res26); // >>> true

      boolean res27 = jedis.vsetattr("points", "pt:D", "{\"size\":\"small\",\"price\":21.00}");
      System.out.println(res27); // >>> true

      boolean res28 = jedis.vsetattr("points", "pt:E", "{\"size\":\"small\",\"price\":17.75}");
      System.out.println(res28); // >>> true

      List<String> res29 = jedis.vsimByElement("points", "pt:A",
        new VSimParams().filter(".size == \"large\""));
      System.out.println(res29); // >>> ["pt:A", "pt:C", "pt:B"]

      List<String> res30 = jedis.vsimByElement("points", "pt:A",
        new VSimParams().filter(".size == \"large\" && .price > 20.00"));
      System.out.println(res30); // >>> ["pt:C", "pt:B"]

code:
const res24 = await client.vSetAttr("points", "pt:A", {
  size: "large",
  price: 18.99
});
console.log(res24);  // >>> true

const res25 = await client.vSetAttr("points", "pt:B", {
  size: "large",
  price: 35.99
});
console.log(res25);  // >>> true

const res26 = await client.vSetAttr("points", "pt:C", {
  size: "large",
  price: 25.99
});
console.log(res26);  // >>> true

const res27 = await client.vSetAttr("points", "pt:D", {
  size: "small",
  price: 21.00
});
console.log(res27);  // >>> true

const res28 = await client.vSetAttr("points", "pt:E", {
  size: "small",
  price: 17.75
});
console.log(res28);  // >>> true

// Return elements in order of distance from point A whose
// `size` attribute is `large`.
const res29 = await client.vSim("points", "pt:A", {
  FILTER: '.size == "large"'
});
console.log(res29);  // >>> ['pt:A', 'pt:C', 'pt:B']

// Return elements in order of distance from point A whose size is
// `large` and whose price is greater than 20.00.
const res30 = await client.vSim("points", "pt:A", {
  FILTER: '.size == "large" && .price > 20.00'
});
console.log(res30);  // >>> ['pt:C', 'pt:B']

code:
$res24 = $r->vsetattr('points', 'pt:A', [
            'size' => 'large',
            'price' => 18.99
        ]);
        echo $res24 . PHP_EOL;
        // >>> 1

        $res25 = $r->vsetattr('points', 'pt:B', [
            'size' => 'large',
            'price' => 35.99
        ]);
        echo $res25 . PHP_EOL;
        // >>> 1

        $res26 = $r->vsetattr('points', 'pt:C', [
            'size' => 'large',
            'price' => 25.99
        ]);
        echo $res26 . PHP_EOL;
        // >>> 1

        $res27 = $r->vsetattr('points', 'pt:D', [
            'size' => 'small',
            'price' => 21.00
        ]);
        echo $res27 . PHP_EOL;
        // >>> 1

        $res28 = $r->vsetattr('points', 'pt:E', [
            'size' => 'small',
            'price' => 17.75
        ]);
        echo $res28 . PHP_EOL;
        // >>> 1

        // Return elements in order of distance from point A whose
        // `size` attribute is `large`.
        $res29 = $r->vsim(
            'points', 'pt:A',true, null, null, null, null,
            '.size == "large"'
        );
        echo json_encode($res29) . PHP_EOL;
        // >>> ["pt:A","pt:C","pt:B"]

        // Return elements in order of distance from point A whose size is
        // `large` and whose price is greater than 20.00.
        $res30 = $r->vsim(
            'points', 'pt:A',true, null, null, null, null,
            '.size == "large" && .price > 20.00'
        );
        echo json_encode($res30) . PHP_EOL;
        // >>> ["pt:C","pt:B"]

code:
res24 = r.vset().vsetattr("points", "pt:A", {
    "size": "large",
    "price": 18.99
})
print(res24)  # >>> 1

res25 = r.vset().vsetattr("points", "pt:B", {
    "size": "large",
    "price": 35.99
})
print(res25)  # >>> 1

res26 = r.vset().vsetattr("points", "pt:C", {
    "size": "large",
    "price": 25.99
})
print(res26)  # >>> 1

res27 = r.vset().vsetattr("points", "pt:D", {
    "size": "small",
    "price": 21.00
})
print(res27)  # >>> 1

res28 = r.vset().vsetattr("points", "pt:E", {
    "size": "small",
    "price": 17.75
})
print(res28)  # >>> 1

# Return elements in order of distance from point A whose
# `size` attribute is `large`.
res29 = r.vset().vsim(
    "points", "pt:A",
    filter='.size == "large"'
)
print(res29)  # >>> ['pt:A', 'pt:C', 'pt:B']

# Return elements in order of distance from point A whose size is
# `large` and whose price is greater than 20.00.
res30 = r.vset().vsim(
    "points", "pt:A",
    filter='.size == "large" && .price > 20.00'
)
print(res30)  # >>> ['pt:C', 'pt:B']


title: Memory optimization

title: Overview

text: Redis vector sets are efficient, but vector similarity indexing and graph traversal require memory tradeoffs. This guide helps you manage memory use through quantization, graph tuning, and attribute choices.

title: Quantization modes

text:
Vector sets support three quantization levels:

| Mode       | Memory usage  | Recall | Notes                           |
|------------|---------------|--------|---------------------------------|
| `Q8`       | 4x smaller    | High   | Default, fast and accurate      |
| `BIN`      | 32x smaller   | Lower  | Fastest, best for coarse search |
| `NOQUANT`  | Full size     | Highest| Best precision, slowest         |

Use `Q8` unless your use case demands either ultra-precision (use `NOQUANT`) or ultra-efficiency (use `BIN`).

title: Graph structure memory

text:
HNSW graphs store multiple connections per node. Each node:

- Has an average of `M * 2 + M * 0.33` pointers (default M = 16).
- Stores pointers using 8 bytes each.
- Allocates ~1.33 layers per node.

> A single node with M = 64 may consume ~1 KB in links alone.

To reduce memory:

- Lower `M` to shrink per-node connections.
- Avoid unnecessarily large values for `M` unless recall needs to be improved.

title: Attribute and label size

text:
Each node stores:

- A string label (element name)
- Optional JSON attribute string

Tips:

- Use short, fixed-length strings for labels.
- Keep attribute JSON minimal and flat. For example, use `{"year":2020}` instead of nested data.

title: Vector dimension

text:
High-dimensional vectors increase storage:

- 300 components at `FP32` = 1200 bytes/vector
- 300 components at `Q8` = 300 bytes/vector

You can reduce this using the `REDUCE` option during [`VADD`](), which applies [random projection](https://en.wikipedia.org/wiki/Random_projection):

#### Code Examples

Dimension reduction: Use the REDUCE option with VADD to apply random projection and reduce vector dimensions when you need to optimize memory usage while maintaining search quality

**Difficulty:** Advanced

**Commands:** VDIM

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

This projects a 300-dimensional vector into 100 dimensions, reducing size and improving speed at the cost of some recall.

title: Summary

text:
| Strategy            | Effect                                   |
|---------------------|------------------------------------------|
| Use `Q8`            | Best tradeoff for most use cases         |
| Use `BIN`           | Minimal memory, fastest search           |
| Lower `M`           | Shrinks HNSW link graph size             |
| Reduce dimensions   | Cuts memory per vector                   |
| Minimize JSON       | Smaller attributes, less memory per node |

title: See also

text:
- [Performance]()
- [Scalability]()

code:
>VADD setNotReduced VALUES 300 ... element
(integer) 1
> VDIM setNotReduced
(integer) 300

>VADD setReduced REDUCE 100 VALUES 300 ... element
(integer) 1
> VDIM setReduced
(integer) 100

section_id: vector-dimension

code:
float[] values = Enumerable.Range(0, 300).Select(x => (float)(x / 299.0)).ToArray();
        bool addedNotReduced = db.VectorSetAdd("setNotReduced", VectorSetAddRequest.Member("element", values, null));
        Console.WriteLine(addedNotReduced); // >>> True
        Console.WriteLine(db.VectorSetDimension("setNotReduced")); // >>> 300

        VectorSetAddRequest addReduced = VectorSetAddRequest.Member("element", values, null);
        addReduced.ReducedDimensions = 100;
        bool addedReduced = db.VectorSetAdd("setReduced", addReduced);
        Console.WriteLine(addedReduced); // >>> True
        Console.WriteLine(db.VectorSetDimension("setReduced")); // >>> 100

code:
// Create a vector with 300 dimensions
	values := make([]float64, 300)

	for i := 0; i < 300; i++ {
		values[i] = float64(i) / 299
	}

	vecLarge := &redis.VectorValues{Val: values}

	// Add without reduction
	res1, err := rdb.VAdd(ctx, "setNotReduced", "element", vecLarge).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> true

	dim1, err := rdb.VDim(ctx, "setNotReduced").Result()

	if err != nil {
		panic(err)
	}

	fmt.Printf("Dimension without reduction: %d\n", dim1)
	// >>> Dimension without reduction: 300

	// Add with reduction to 100 dimensions
	res2, err := rdb.VAddWithArgs(ctx, "setReduced", "element", vecLarge,
		&redis.VAddArgs{
			Reduce: 100,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> true

	dim2, err := rdb.VDim(ctx, "setReduced").Result()

	if err != nil {
		panic(err)
	}

	fmt.Printf("Dimension after reduction: %d\n", dim2)
	// >>> Dimension after reduction: 100

code:
// Create a list of 300 arbitrary values.
            Double[] values = new Double[300];
            for (int i = 0; i < 300; i++) {
                values[i] = (double) i / 299;
            }

            CompletableFuture<Void> dimensionalityReductionOperations = asyncCommands.vadd("setNotReduced", "element", values)
                    .thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vdim("setNotReduced");
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> 300
                        return asyncCommands.vadd("setReduced", 100, "element", values);
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vdim("setReduced");
                    }).thenAccept(result -> {
                        System.out.println(result); // >>> 100
                    }).toCompletableFuture();

code:
// Create a list of 300 arbitrary values.
            Double[] values = new Double[300];
            for (int i = 0; i < 300; i++) {
                values[i] = (double) i / 299;
            }

            Mono<Void> dimensionalityReductionOperations = reactiveCommands.vadd("setNotReduced", "element", values)
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vdim("setNotReduced")).doOnNext(result -> {
                        System.out.println(result); // >>> 300
                    }).flatMap(result -> reactiveCommands.vadd("setReduced", 100, "element", values)).doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vdim("setReduced")).doOnNext(result -> {
                        System.out.println(result); // >>> 100
                    }).then();

code:
float[] values = new float[300];
      for (int i = 0; i < 300; i++)
        values[i] = i / 299.0f;

      boolean res37 = jedis.vadd("setNotReduced", values, "element");
      System.out.println(res37); // >>> true

      long res38 = jedis.vdim("setNotReduced");
      System.out.println(res38); // >>> 300

      boolean res39 = jedis.vadd("setReduced", values, "element", 100, new VAddParams());
      System.out.println(res39); // >>> true

      long res40 = jedis.vdim("setReduced");
      System.out.println(res40); // >>> 100

code:
// Create a list of 300 arbitrary values.
const values = Array.from({length: 300}, (_, x) => x / 299);

const res37 = await client.vAdd("setNotReduced", values, "element");
console.log(res37);  // >>> true

const res38 = await client.vDim("setNotReduced");
console.log(res38);  // >>> 300

const res39 = await client.vAdd("setReduced", values, "element", {
  REDUCE: 100
});
console.log(res39);  // >>> true

const res40 = await client.vDim("setReduced");
console.log(res40);  // >>> 100

code:
$values = array();

        for ($i = 0; $i < 300; $i++) {
            $values[] = $i / 299.0;
        }

        $res37 = $r->vadd('setNotReduced', $values, 'element');
        echo $res37 . PHP_EOL;
        // >>> 1

        $res38 = $r->vdim('setNotReduced');
        echo $res38 . PHP_EOL;
        // >>> 300

        $res39 = $r->vadd('setReduced', $values, 'element', 100);
        echo $res39 . PHP_EOL;
        // >>> 1

        $res40 = $r->vdim('setReduced');
        echo $res40 . PHP_EOL;
        // >>> 100

code:
# Create a list of 300 arbitrary values.
values = [x / 299 for x in range(300)]

res37 = r.vset().vadd(
    "setNotReduced",
    values,
    "element"
)
print(res37)  # >>> 1

res38 = r.vset().vdim("setNotReduced")
print(res38)  # >>> 300

res39 = r.vset().vadd(
    "setReduced",
    values,
    "element",
    reduce_dim=100
)
print(res39)  # >>> 1

res40 = r.vset().vdim("setReduced")  # >>> 100
print(res40)


title: Performance

title: Query performance

text:
Vector similarity queries using the [`VSIM`]() are threaded by default. Redis uses up to 32 threads to process these queries in parallel.

- `VSIM` performance scales nearly linearly with available CPU cores.
- Expect ~50,000 similarity queries per second for a 3M-item set with 300-dim vectors using int8 quantization.
- Performance depends heavily on the `EF` parameter:
  - Higher `EF` improves recall, but slows down search.
  - Lower `EF` returns faster results with reduced accuracy.

title: Insertion performance

text:
Inserting vectors with the [`VADD`]() command is more computationally expensive than querying:

- Insertion is single-threaded by default.
- Use the `CAS` option to offload candidate graph search to a background thread.
- Expect a few thousand insertions per second on a single node.

title: Quantization effects

text:
Quantization greatly impacts both speed and memory:

- `Q8` (default): 4x smaller than `FP32`, high recall, high speed
- `BIN` (binary): 32x smaller than `FP32`, lower recall, fastest search
- `NOQUANT` (`FP32`): Full precision, slower performance, highest memory use

Use the quantization mode that best fits your tradeoff between precision and efficiency.
The examples below show how the different modes affect a simple vector.
Note that even with `NOQUANT` mode, the values change slightly,
due to floating point rounding.

#### Code Examples

Quantization modes: Compare Q8, NOQUANT, and BIN quantization modes using VADD and VEMB to understand the tradeoffs between precision, memory, and performance

**Difficulty:** Advanced

**Commands:** VADD, VEMB

**Redis CLI:**

[code example]

**Available in:** Redis CLI, C# (Synchronous), Go, Java (Asynchronous - Lettuce), Java (Reactive - Lettuce), Java (Synchronous - Jedis), JavaScript (Node.js), PHP, Python

**C# (Synchronous):**

[code example]

**Go:**

[code example]

**Java (Asynchronous - Lettuce):**

[code example]

**Java (Reactive - Lettuce):**

[code example]

**Java (Synchronous - Jedis):**

[code example]

**JavaScript (Node.js):**

[code example]

**PHP:**

[code example]

**Python:**

[code example]

title: Deletion performance

text:
Deleting large vector sets using the [`DEL`]() can cause latency spikes:

- Redis must unlink and restructure many graph nodes.
- Latency is most noticeable when deleting millions of elements.

title: Save and load performance

text:
Vector sets save and load the full HNSW graph structure:

- When reloading from disk is fast and there's no need to rebuild the graph.

Example: A 3M vector set with 300 components loads in ~15 seconds.

title: Summary of tuning tips

text:
| Factor     | Effect on performance               | Tip                                            |
|------------|-------------------------------------|------------------------------------------------|
| `EF`       | Slower queries but higher recall    | Start low (for example, 200) and tune upward           |
| `M`        | More memory per node, better recall | Use defaults unless recall is too low          |
| Quant type | Binary is fastest, `FP32` is slowest| Use `Q8` or `BIN` unless full precision needed |
| `CAS`      | Faster insertions with threading    | Use when high write throughput is needed       |

title: See also

text:
- [Memory usage]()
- [Scalability]()
- [Filtered search]()

code:
> VADD quantSetQ8 VALUES 2 1.262185 1.958231 quantElement Q8
(integer) 1
> VEMB quantSetQ8 quantElement
1) "1.2643694877624512"
2) "1.958230972290039"

> VADD quantSetNoQ VALUES 2 1.262185 1.958231 quantElement NOQUANT
(integer) 1
> VEMB quantSetNoQ quantElement
1) "1.262184977531433"
2) "1.958230972290039"

> VADD quantSetBin VALUES 2 1.262185 1.958231 quantElement BIN
(integer) 1
> VEMB quantSetBin quantElement
1) "1"
2) "1"

section_id: quantization-effects

code:
VectorSetAddRequest addInt8 = VectorSetAddRequest.Member("quantElement", new float[] { 1.262185f, 1.958231f }, null);
        addInt8.Quantization = VectorSetQuantization.Int8;
        bool q8Added = db.VectorSetAdd("quantSetQ8", addInt8);
        Console.WriteLine(q8Added); // >>> True
        using (Lease<float>? eInt8 = db.VectorSetGetApproximateVector("quantSetQ8", "quantElement"))
        {
            Span<float> v = eInt8!.Span;
            Console.WriteLine($"Q8: [{v[0]}, {v[1]}]");
            // >>> Q8: [1.2643694877624512, 1.958230972290039]
        }

        VectorSetAddRequest addNone = VectorSetAddRequest.Member("quantElement", new float[] { 1.262185f, 1.958231f }, null);
        addNone.Quantization = VectorSetQuantization.None;
        bool noQuantAdded = db.VectorSetAdd("quantSetNoQ", addNone);
        Console.WriteLine(noQuantAdded); // >>> True
        using (Lease<float>? eNone = db.VectorSetGetApproximateVector("quantSetNoQ", "quantElement"))
        {
            Span<float> v = eNone!.Span;
            Console.WriteLine($"NOQUANT: [{v[0]}, {v[1]}]");
            // >>> NOQUANT: [1.262184977531433, 1.958230972290039]
        }

        VectorSetAddRequest addBinary = VectorSetAddRequest.Member("quantElement", new float[] { 1.262185f, 1.958231f }, null);
        addBinary.Quantization = VectorSetQuantization.Binary;
        bool binAdded = db.VectorSetAdd("quantSetBin", addBinary);
        Console.WriteLine(binAdded); // >>> True
        using (Lease<float>? eBinary = db.VectorSetGetApproximateVector("quantSetBin", "quantElement"))
        {
            Span<float> v = eBinary!.Span;
            Console.WriteLine($"BIN: [{v[0]}, {v[1]}]");
            // >>> BIN: [1, 1]
        }

code:
// Add with Q8 quantization
	vecQ := &redis.VectorValues{Val: []float64{1.262185, 1.958231}}

	res1, err := rdb.VAddWithArgs(ctx, "quantSetQ8", "quantElement", vecQ,
		&redis.VAddArgs{
			Q8: true,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res1) // >>> true

	embQ8, err := rdb.VEmb(ctx, "quantSetQ8", "quantElement", false).Result()

	if err != nil {
		panic(err)
	}

	fmt.Printf("Q8 embedding: %v\n", embQ8)
	// >>> Q8 embedding: [1.2621850967407227 1.9582309722900391]

	// Add with NOQUANT option
	res2, err := rdb.VAddWithArgs(ctx, "quantSetNoQ", "quantElement", vecQ,
		&redis.VAddArgs{
			NoQuant: true,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res2) // >>> true

	embNoQ, err := rdb.VEmb(ctx, "quantSetNoQ", "quantElement", false).Result()

	if err != nil {
		panic(err)
	}

	fmt.Printf("NOQUANT embedding: %v\n", embNoQ)
	// >>> NOQUANT embedding: [1.262185 1.958231]

	// Add with BIN quantization
	res3, err := rdb.VAddWithArgs(ctx, "quantSetBin", "quantElement", vecQ,
		&redis.VAddArgs{
			Bin: true,
		},
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res3) // >>> true

	embBin, err := rdb.VEmb(ctx, "quantSetBin", "quantElement", false).Result()

	if err != nil {
		panic(err)
	}

	fmt.Printf("BIN embedding: %v\n", embBin)
	// >>> BIN embedding: [1 1]

code:
VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);
            CompletableFuture<Void> quantizationOperations = asyncCommands
                    .vadd("quantSetQ8", "quantElement", q8Args, 1.262185, 1.958231).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vemb("quantSetQ8", "quantElement");
                    }).thenCompose(result -> {
                        System.out.println("Q8: " + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]

                        VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);
                        return asyncCommands.vadd("quantSetNoQ", "quantElement", noQuantArgs, 1.262185, 1.958231);
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vemb("quantSetNoQ", "quantElement");
                    }).thenCompose(result -> {
                        System.out.println("NOQUANT: " + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]

                        VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);
                        return asyncCommands.vadd("quantSetBin", "quantElement", binArgs, 1.262185, 1.958231);
                    }).thenCompose(result -> {
                        System.out.println(result); // >>> true
                        return asyncCommands.vemb("quantSetBin", "quantElement");
                    }).thenAccept(result -> {
                        System.out.println("BIN: " + result); // >>> BIN: [1.0, 1.0]
                    }).toCompletableFuture();

code:
VAddArgs q8Args = VAddArgs.Builder.quantizationType(QuantizationType.Q8);
            Mono<Void> quantizationOperations = reactiveCommands.vadd("quantSetQ8", "quantElement", q8Args, 1.262185, 1.958231)
                    .doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vemb("quantSetQ8", "quantElement").collectList()).doOnNext(result -> {
                        System.out.println("Q8: " + result); // >>> Q8: [1.2643694877624512, 1.958230972290039]
                    }).flatMap(result -> {
                        VAddArgs noQuantArgs = VAddArgs.Builder.quantizationType(QuantizationType.NO_QUANTIZATION);
                        return reactiveCommands.vadd("quantSetNoQ", "quantElement", noQuantArgs, 1.262185, 1.958231);
                    }).doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vemb("quantSetNoQ", "quantElement").collectList())
                    .doOnNext(result -> {
                        System.out.println("NOQUANT: " + result); // >>> NOQUANT: [1.262184977531433, 1.958230972290039]
                    }).flatMap(result -> {
                        VAddArgs binArgs = VAddArgs.Builder.quantizationType(QuantizationType.BINARY);
                        return reactiveCommands.vadd("quantSetBin", "quantElement", binArgs, 1.262185, 1.958231);
                    }).doOnNext(result -> {
                        System.out.println(result); // >>> true
                    }).flatMap(result -> reactiveCommands.vemb("quantSetBin", "quantElement").collectList())
                    .doOnNext(result -> {
                        System.out.println("BIN: " + result); // >>> BIN: [1.0, 1.0]
                    }).then();

code:
boolean res31 = jedis.vadd("quantSetQ8", new float[] { 1.262185f, 1.958231f }, "quantElement",
        new VAddParams().q8());
      System.out.println(res31); // >>> true

      List<Double> res32 = jedis.vemb("quantSetQ8", "quantElement");
      System.out.println("Q8: " + res32);
      // >>> Q8: [~1.264, ~1.958]

      boolean res33 = jedis.vadd("quantSetNoQ", new float[] { 1.262185f, 1.958231f },
        "quantElement", new VAddParams().noQuant());
      System.out.println(res33); // >>> true

      List<Double> res34 = jedis.vemb("quantSetNoQ", "quantElement");
      System.out.println("NOQUANT: " + res34);
      // >>> NOQUANT: [~1.262185, ~1.958231]

      boolean res35 = jedis.vadd("quantSetBin", new float[] { 1.262185f, 1.958231f },
        "quantElement", new VAddParams().bin());
      System.out.println(res35); // >>> true

      List<Double> res36 = jedis.vemb("quantSetBin", "quantElement");
      System.out.println("BIN: " + res36);
      // >>> BIN: [1, 1]

code:
const res31 = await client.vAdd("quantSetQ8", [1.262185, 1.958231], "quantElement", {
  QUANT: 'Q8'
});
console.log(res31);  // >>> true

const res32 = await client.vEmb("quantSetQ8", "quantElement");
console.log(`Q8: ${res32}`);
// >>> Q8: [1.2643694877624512, 1.958230972290039]

const res33 = await client.vAdd("quantSetNoQ", [1.262185, 1.958231], "quantElement", {
  QUANT: 'NOQUANT'
});
console.log(res33);  // >>> true

const res34 = await client.vEmb("quantSetNoQ", "quantElement");
console.log(`NOQUANT: ${res34}`);
// >>> NOQUANT: [1.262184977531433, 1.958230972290039]

const res35 = await client.vAdd("quantSetBin", [1.262185, 1.958231], "quantElement", {
  QUANT: 'BIN'
});
console.log(res35);  // >>> true

const res36 = await client.vEmb("quantSetBin", "quantElement");
console.log(`BIN: ${res36}`);
// >>> BIN: [1, 1]

code:
$res31 = $r->vadd('quantSetQ8', [1.262185, 1.958231], 'quantElement', null, false,
            Predis\Command\Redis\VADD::QUANT_Q8
        );
        echo $res31 . PHP_EOL;
        // >>> 1

        $res32 = $r->vemb('quantSetQ8', 'quantElement');
        echo json_encode($res32) . PHP_EOL;
        // >>> [1.2643694877624512, 1.958230972290039]

        $res33 = $r->vadd('quantSetNoQ', [1.262185, 1.958231], 'quantElement', null, false,
            Predis\Command\Redis\VADD::QUANT_NOQUANT
        );
        echo $res33 . PHP_EOL;
        // >>> 1

        $res34 = $r->vemb('quantSetNoQ', 'quantElement');
        echo json_encode($res34) . PHP_EOL;
        // >>> [1.262184977531433, 1.958230972290039]

        $res35 = $r->vadd('quantSetBin', [1.262185, 1.958231], 'quantElement', null, false,
            Predis\Command\Redis\VADD::QUANT_BIN
        );
        echo $res35 . PHP_EOL;
        // >>> 1

        $res36 = $r->vemb('quantSetBin', 'quantElement');
        echo json_encode($res36) . PHP_EOL;
        // >>> [1, 1]

code:
# Import `QuantizationOptions` enum using:
#
# from redis.commands.vectorset.commands import (
#   QuantizationOptions
# )
res31 = r.vset().vadd(
    "quantSetQ8", [1.262185, 1.958231],
    "quantElement",
    quantization=QuantizationOptions.Q8
)
print(res31)  # >>> 1

res32 = r.vset().vemb("quantSetQ8", "quantElement")
print(f"Q8: {res32}")
# >>> Q8: [1.2643694877624512, 1.958230972290039]

res33 = r.vset().vadd(
    "quantSetNoQ", [1.262185, 1.958231],
    "quantElement",
    quantization=QuantizationOptions.NOQUANT
)
print(res33)  # >>> 1

res34 = r.vset().vemb("quantSetNoQ", "quantElement")
print(f"NOQUANT: {res34}")
# >>> NOQUANT: [1.262184977531433, 1.958230972290039]

res35 = r.vset().vadd(
    "quantSetBin", [1.262185, 1.958231],
    "quantElement",
    quantization=QuantizationOptions.BIN
)
print(res35)  # >>> 1

res36 = r.vset().vemb("quantSetBin", "quantElement")
print(f"BIN: {res36}")
# >>> BIN: [1, 1]


title: Scalability

title: Multi-instance scalability

text: Vector sets can scale horizontally by sharding your data across multiple Redis instances. This is done by partitioning the dataset manually across keys and nodes.

title: Example strategy

text:
You can shard data using a consistent hash:

[code example]

Then add elements into different keys:

[code example]

To run a similarity search across all shards, send [`VSIM`]() commands to each key and then merge the results client-side:

[code example]

Then combine and sort the results by score.

title: Key properties

text:
- Write operations ([`VADD`](), [`VREM`]()) scale linearly—you can insert in parallel across instances.
- Read operations ([`VSIM`]()) do not scale linearly—you must query all shards for a full result set.
- Smaller vector sets yield faster queries, so distributing them helps reduce query time per node.
- Merging results client-side keeps logic simple and doesn't add server-side overhead.

title: Availability benefits

text:
This sharding model also improves fault tolerance:

- If one instance is down, you can still retrieve partial results from others.
- Use timeouts and partial fallbacks to increase resilience.

title: Latency considerations

text:
To avoid additive latency across N instances:

- Send queries to all shards in parallel.
- Wait for the slowest response.

This makes total latency close to the worst-case shard time, not the sum of all times.

title: Summary

text:
| Goal                      | Approach                                          |
|---------------------------|---------------------------------------------------|
| Scale inserts             | Split data across keys and instances              |
| Scale reads               | Query all shards and merge results                |
| High availability         | Accept partial results when some shards fail      |
| Maintain performance      | Use smaller shards for faster per-node traversal  |

title: See also

text:
- [Performance]()
- [Filtered search]()
- [Memory usage]()

code:
key_index = crc32(item) % 3
key = f"vset:{key_index}"

section_id: example-strategy

code:
VADD vset:0 VALUES 3 0.1 0.2 0.3 item1
VADD vset:1 VALUES 3 0.4 0.5 0.6 item2

code:
VSIM vset:0 VALUES ... WITHSCORES
VSIM vset:1 VALUES ... WITHSCORES
VSIM vset:2 VALUES ... WITHSCORES


title: Troubleshooting

title: Common challenges

text: Vector sets are approximate by design. That makes debugging trickier than with exact match queries. This section helps you understand issues with recall, filtering, and graph structure.

title: Low recall or missing results

text:
If [`VSIM`]() doesn't return expected items:

- Increase the `EF` parameter:

  [code example]

- Check quantization mode. Binary quantization (`BIN`) trades accuracy for speed.
- Use `TRUTH` to compare results against a linear scan:

  [code example]

  This gives you the most accurate results for validation, but it's slow.

title: Filtering issues

text:
Filters silently exclude items if:

- A field is missing from the element’s attributes
- The JSON is invalid
- A type doesn’t match the expression (for example, `.rating > 8` when `.rating` is a string)

Try retrieving the attributes with [`VGETATTR`]():

[code example]

Double-check field names, JSON validity, and value types.

title: Unexpected memory usage

text:
Memory issues may arise from:

- Large vectors (use `REDUCE` to project down)
- High `M` values inflating link graphs
- Large or deeply nested JSON attributes
- Storing raw `FP32` vectors (`NOQUANT`)

Use default `Q8` quantization and compact attributes to save space.

title: Inspecting the graph

text:
Use [`VLINKS`]() to examine a node’s connections:

[code example]

- Helps you verify whether isolated or weakly connected nodes exist.
- Useful for explaining poor recall.

title: Deletion spikes

text: Large sets deleted using the `DEL` command can briefly spike latency as Redis reclaims memory and rebuilds HNSW linkages.

title: Replication quirks

text:
- `VADD` with `REDUCE` does not replicate the random projection matrix.
- Replicas will produce different projected vectors for the same inputs.

This doesn't affect similarity searches but does affect [`VEMB`]() output.

title: Summary

text:
| Symptom                          | Try this                                                  |
|----------------------------------|-----------------------------------------------------------|
| Poor recall                      | Use higher `EF`, check quantization, use `TRUTH`          |
| Filters exclude too much         | Validate attributes with `VGETATTR`, simplify expressions |
| Memory spikes                    | Use `REDUCE`, `Q8`, smaller `M`, compact JSON             |
| Replication mismatch with REDUCE | Avoid relying on projected vectors from replicas          |

title: See also

text:
- [Filtered Search]()
- [Memory Usage]()
- [Performance]()

code: VSIM myset VALUES 3 ... COUNT 10 EF 1000

section_id: low-recall-or-missing-results

code: VSIM myset VALUES 3 ... COUNT 10 TRUTH

code: VGETATTR myset myelement

section_id: filtering-issues

code: VLINKS myset myelement WITHSCORES

section_id: inspecting-the-graph