Skip to content

Commit d8014e6

Browse files
committed
Add VectorSet API for AI/ML similarity search
New partial files: IRedisDatabase.VectorSet.cs + RedisDatabase.VectorSet.cs 10 methods covering Redis 8.0 VectorSet operations: - VectorSetAddAsync — add vectors with optional attributes - VectorSetSimilaritySearchAsync — similarity search (core AI/ML operation) - VectorSetRemoveAsync — remove members - VectorSetContainsAsync — check membership - VectorSetLengthAsync — get cardinality - VectorSetDimensionAsync — get vector dimensions - VectorSetGetAttributesJsonAsync — get JSON metadata - VectorSetSetAttributesJsonAsync — set JSON metadata - VectorSetInfoAsync — get VectorSet info - VectorSetRandomMemberAsync — random sampling New documentation: doc/vectorset.md with Mermaid diagram, usage examples for RAG, recommendations, and semantic search. Closes #631
1 parent dc8073c commit d8014e6

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed

doc/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* [Hash Operations](usage/README.md)
3434
* [Hash Field Expiry](hash-field-expiry.md) (Redis 7.4+)
3535
* [GeoSpatial Indexes](geospatial.md)
36+
* [VectorSet — AI/ML Similarity Search](vectorset.md) (Redis 8.0+)
3637
* [Redis Streams](streams.md)
3738
* [Pub/Sub Messaging](pubsub.md)
3839
* [Custom Serializer](usage/custom-serializer.md)

doc/vectorset.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# VectorSet (AI/ML Similarity Search)
2+
3+
Redis 8.0 introduced VectorSet — a native data structure for storing and searching high-dimensional vectors. This is ideal for AI/ML applications like RAG, recommendations, and semantic search.
4+
5+
> **Requires Redis 8.0+**
6+
7+
## Overview
8+
9+
```mermaid
10+
graph LR
11+
A[AI Model] -->|Generate Embedding| V[float array]
12+
V -->|VectorSetAddAsync| R[("Redis VectorSet")]
13+
Q[Query Text] -->|Generate Embedding| QV[float array]
14+
QV -->|VectorSetSimilaritySearchAsync| R
15+
R -->|Top K Results| Results[Similar Items]
16+
```
17+
18+
## Adding Vectors
19+
20+
```csharp
21+
// Add a vector with a member name
22+
var embedding = await aiModel.GetEmbeddingAsync("Red running shoes, size 42");
23+
24+
await redis.VectorSetAddAsync("products",
25+
VectorSetAddRequest.Create("shoe-123", embedding));
26+
27+
// Add with JSON attributes (metadata)
28+
await redis.VectorSetAddAsync("products",
29+
VectorSetAddRequest.Create("shoe-456", embedding)
30+
.WithAttributes("""{"category":"shoes","price":79.99,"brand":"Nike"}"""));
31+
```
32+
33+
## Similarity Search
34+
35+
```csharp
36+
// Find the 5 most similar items to a query vector
37+
var queryEmbedding = await aiModel.GetEmbeddingAsync("comfortable sneakers for running");
38+
39+
var results = await redis.VectorSetSimilaritySearchAsync("products",
40+
VectorSetSimilaritySearchRequest.Create(queryEmbedding, count: 5));
41+
42+
foreach (var result in results)
43+
{
44+
Console.WriteLine($"{result.Member}: score={result.Score:F4}");
45+
46+
// Get attributes for each result
47+
var attrs = await redis.VectorSetGetAttributesJsonAsync("products", result.Member!);
48+
Console.WriteLine($" Attributes: {attrs}");
49+
}
50+
```
51+
52+
## Managing Vectors
53+
54+
```csharp
55+
// Check if a member exists
56+
var exists = await redis.VectorSetContainsAsync("products", "shoe-123");
57+
58+
// Get cardinality
59+
var count = await redis.VectorSetLengthAsync("products");
60+
61+
// Get vector dimensions
62+
var dims = await redis.VectorSetDimensionAsync("products");
63+
64+
// Get a random member
65+
var random = await redis.VectorSetRandomMemberAsync("products");
66+
67+
// Get info about the VectorSet
68+
var info = await redis.VectorSetInfoAsync("products");
69+
70+
// Remove a member
71+
await redis.VectorSetRemoveAsync("products", "shoe-123");
72+
```
73+
74+
## Attributes (Metadata)
75+
76+
```csharp
77+
// Set JSON attributes on a member
78+
await redis.VectorSetSetAttributesJsonAsync("products", "shoe-123",
79+
"""{"category":"shoes","price":99.99,"sizes":[40,41,42]}""");
80+
81+
// Get JSON attributes
82+
var json = await redis.VectorSetGetAttributesJsonAsync("products", "shoe-123");
83+
```
84+
85+
## Use Cases
86+
87+
### RAG (Retrieval-Augmented Generation)
88+
```csharp
89+
// Index documents
90+
foreach (var doc in documents)
91+
{
92+
var embedding = await aiModel.GetEmbeddingAsync(doc.Content);
93+
await redis.VectorSetAddAsync("docs",
94+
VectorSetAddRequest.Create(doc.Id, embedding)
95+
.WithAttributes($"""{{ "title": "{doc.Title}" }}"""));
96+
}
97+
98+
// Query: find relevant context for a prompt
99+
var queryEmb = await aiModel.GetEmbeddingAsync(userQuestion);
100+
var context = await redis.VectorSetSimilaritySearchAsync("docs",
101+
VectorSetSimilaritySearchRequest.Create(queryEmb, count: 3));
102+
```
103+
104+
### Recommendations
105+
```csharp
106+
// Find products similar to what the user just viewed
107+
var viewedProduct = await redis.VectorSetGetApproximateVectorAsync("products", productId);
108+
// Use the vector to find similar items
109+
```
110+
111+
### Semantic Search
112+
```csharp
113+
// Search by meaning, not keywords
114+
var searchEmb = await aiModel.GetEmbeddingAsync("something warm for winter");
115+
var results = await redis.VectorSetSimilaritySearchAsync("clothing",
116+
VectorSetSimilaritySearchRequest.Create(searchEmb, count: 20));
117+
```
118+
119+
## Performance Notes
120+
121+
- VectorSet uses HNSW (Hierarchical Navigable Small World) algorithm internally
122+
- Approximate nearest neighbor search — extremely fast even with millions of vectors
123+
- Memory efficient compared to external vector databases
124+
- Vectors are stored directly in Redis — no external index to maintain
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright (c) Ugo Lattanzi. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
2+
3+
using System.Threading.Tasks;
4+
5+
namespace StackExchange.Redis.Extensions.Core.Abstractions;
6+
7+
/// <summary>
8+
/// The Redis Database VectorSet extensions for AI/ML similarity search.
9+
/// Requires Redis 8.0+.
10+
/// </summary>
11+
public partial interface IRedisDatabase
12+
{
13+
/// <summary>
14+
/// Adds a vector to the VectorSet stored at key.
15+
/// </summary>
16+
/// <param name="key">The key of the VectorSet.</param>
17+
/// <param name="request">The add request containing the member, vector, and optional attributes.</param>
18+
/// <param name="flag">Behaviour markers associated with a given command.</param>
19+
/// <returns>True if the member was added, false if it already existed and was updated.</returns>
20+
Task<bool> VectorSetAddAsync(string key, VectorSetAddRequest request, CommandFlags flag = CommandFlags.None);
21+
22+
/// <summary>
23+
/// Performs a similarity search against the VectorSet stored at key.
24+
/// </summary>
25+
/// <param name="key">The key of the VectorSet.</param>
26+
/// <param name="query">The search request containing the query vector and parameters.</param>
27+
/// <param name="flag">Behaviour markers associated with a given command.</param>
28+
/// <returns>The matching results with scores. The returned Lease must be disposed after use.</returns>
29+
Task<Lease<VectorSetSimilaritySearchResult>?> VectorSetSimilaritySearchAsync(string key, VectorSetSimilaritySearchRequest query, CommandFlags flag = CommandFlags.None);
30+
31+
/// <summary>
32+
/// Removes a member from the VectorSet stored at key.
33+
/// </summary>
34+
/// <param name="key">The key of the VectorSet.</param>
35+
/// <param name="member">The member to remove.</param>
36+
/// <param name="flag">Behaviour markers associated with a given command.</param>
37+
/// <returns>True if the member was removed, false if it did not exist.</returns>
38+
Task<bool> VectorSetRemoveAsync(string key, string member, CommandFlags flag = CommandFlags.None);
39+
40+
/// <summary>
41+
/// Checks if a member exists in the VectorSet stored at key.
42+
/// </summary>
43+
/// <param name="key">The key of the VectorSet.</param>
44+
/// <param name="member">The member to check.</param>
45+
/// <param name="flag">Behaviour markers associated with a given command.</param>
46+
/// <returns>True if the member exists.</returns>
47+
Task<bool> VectorSetContainsAsync(string key, string member, CommandFlags flag = CommandFlags.None);
48+
49+
/// <summary>
50+
/// Returns the number of members in the VectorSet stored at key.
51+
/// </summary>
52+
/// <param name="key">The key of the VectorSet.</param>
53+
/// <param name="flag">Behaviour markers associated with a given command.</param>
54+
/// <returns>The cardinality of the VectorSet, or 0 if the key does not exist.</returns>
55+
Task<long> VectorSetLengthAsync(string key, CommandFlags flag = CommandFlags.None);
56+
57+
/// <summary>
58+
/// Returns the number of dimensions of vectors in the VectorSet stored at key.
59+
/// </summary>
60+
/// <param name="key">The key of the VectorSet.</param>
61+
/// <param name="flag">Behaviour markers associated with a given command.</param>
62+
/// <returns>The number of dimensions.</returns>
63+
Task<long> VectorSetDimensionAsync(string key, CommandFlags flag = CommandFlags.None);
64+
65+
/// <summary>
66+
/// Gets the JSON attributes associated with a member in the VectorSet.
67+
/// </summary>
68+
/// <param name="key">The key of the VectorSet.</param>
69+
/// <param name="member">The member to retrieve attributes for.</param>
70+
/// <param name="flag">Behaviour markers associated with a given command.</param>
71+
/// <returns>The JSON attributes string, or null if the member has no attributes.</returns>
72+
Task<string?> VectorSetGetAttributesJsonAsync(string key, string member, CommandFlags flag = CommandFlags.None);
73+
74+
/// <summary>
75+
/// Sets JSON attributes on a member in the VectorSet.
76+
/// </summary>
77+
/// <param name="key">The key of the VectorSet.</param>
78+
/// <param name="member">The member to set attributes on.</param>
79+
/// <param name="attributesJson">The JSON attributes string.</param>
80+
/// <param name="flag">Behaviour markers associated with a given command.</param>
81+
/// <returns>True if the attributes were set.</returns>
82+
Task<bool> VectorSetSetAttributesJsonAsync(string key, string member, string attributesJson, CommandFlags flag = CommandFlags.None);
83+
84+
/// <summary>
85+
/// Returns information about the VectorSet stored at key.
86+
/// </summary>
87+
/// <param name="key">The key of the VectorSet.</param>
88+
/// <param name="flag">Behaviour markers associated with a given command.</param>
89+
/// <returns>Information about the VectorSet.</returns>
90+
Task<VectorSetInfo?> VectorSetInfoAsync(string key, CommandFlags flag = CommandFlags.None);
91+
92+
/// <summary>
93+
/// Returns a random member from the VectorSet stored at key.
94+
/// </summary>
95+
/// <param name="key">The key of the VectorSet.</param>
96+
/// <param name="flag">Behaviour markers associated with a given command.</param>
97+
/// <returns>A random member, or null if the VectorSet is empty.</returns>
98+
Task<RedisValue> VectorSetRandomMemberAsync(string key, CommandFlags flag = CommandFlags.None);
99+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Ugo Lattanzi. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
2+
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace StackExchange.Redis.Extensions.Core.Implementations;
7+
8+
/// <inheritdoc/>
9+
public partial class RedisDatabase
10+
{
11+
/// <inheritdoc/>
12+
public Task<bool> VectorSetAddAsync(string key, VectorSetAddRequest request, CommandFlags flag = CommandFlags.None) =>
13+
Database.VectorSetAddAsync(key, request, flag);
14+
15+
/// <inheritdoc/>
16+
public Task<Lease<VectorSetSimilaritySearchResult>?> VectorSetSimilaritySearchAsync(string key, VectorSetSimilaritySearchRequest query, CommandFlags flag = CommandFlags.None) =>
17+
Database.VectorSetSimilaritySearchAsync(key, query, flag);
18+
19+
/// <inheritdoc/>
20+
public Task<bool> VectorSetRemoveAsync(string key, string member, CommandFlags flag = CommandFlags.None) =>
21+
Database.VectorSetRemoveAsync(key, member, flag);
22+
23+
/// <inheritdoc/>
24+
public Task<bool> VectorSetContainsAsync(string key, string member, CommandFlags flag = CommandFlags.None) =>
25+
Database.VectorSetContainsAsync(key, member, flag);
26+
27+
/// <inheritdoc/>
28+
public Task<long> VectorSetLengthAsync(string key, CommandFlags flag = CommandFlags.None) =>
29+
Database.VectorSetLengthAsync(key, flag).ContinueWith(t => (long)t.Result, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
30+
31+
/// <inheritdoc/>
32+
public Task<long> VectorSetDimensionAsync(string key, CommandFlags flag = CommandFlags.None) =>
33+
Database.VectorSetDimensionAsync(key, flag).ContinueWith(t => (long)t.Result, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
34+
35+
/// <inheritdoc/>
36+
public Task<string?> VectorSetGetAttributesJsonAsync(string key, string member, CommandFlags flag = CommandFlags.None) =>
37+
Database.VectorSetGetAttributesJsonAsync(key, member, flag);
38+
39+
/// <inheritdoc/>
40+
public Task<bool> VectorSetSetAttributesJsonAsync(string key, string member, string attributesJson, CommandFlags flag = CommandFlags.None) =>
41+
Database.VectorSetSetAttributesJsonAsync(key, member, attributesJson, flag);
42+
43+
/// <inheritdoc/>
44+
public Task<VectorSetInfo?> VectorSetInfoAsync(string key, CommandFlags flag = CommandFlags.None) =>
45+
Database.VectorSetInfoAsync(key, flag);
46+
47+
/// <inheritdoc/>
48+
public Task<RedisValue> VectorSetRandomMemberAsync(string key, CommandFlags flag = CommandFlags.None) =>
49+
Database.VectorSetRandomMemberAsync(key, flag);
50+
}

0 commit comments

Comments
 (0)