Redis 向量集合
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/vector-sets/
代码示例说明
以下代码示例展示了如何使用不同的编程语言和客户端库执行相同的操作。为简洁起见,本译文仅保留 Java 相关的示例(同步 Jedis、异步 Lettuce、响应式 Lettuce)。
每个代码示例均以不同语言展示相同的基本操作。具体语法和模式会因语言和客户端库而异,但底层的 Redis 命令和行为保持一致。
向量集合命令摘要
本组共 13 条命令:
| 命令 | 摘要 | 复杂度 | 引入版本 |
|---|---|---|---|
| VADD | 向向量集合添加一个或多个元素,若元素已存在则更新其向量 | 不适用 | 8.0.0 |
| VCARD | 返回向量集合中的元素数量 | 不适用 | 8.0.0 |
| VDIM | 返回向量集合中向量的维度 | 不适用 | 8.0.0 |
| VEMB | 返回与元素关联的向量 | 不适用 | 8.0.0 |
| VGETATTR | 检索元素的 JSON 属性 | 不适用 | 8.0.0 |
| VINFO | 返回向量集合的信息 | 不适用 | 8.0.0 |
| VISMEMBER | 检查元素是否存在于向量集合中 | 不适用 | 8.2.0 |
| VLINKS | 返回 HNSW 图中元素在各层的邻居 | 不适用 | 8.0.0 |
| VRANDMEMBER | 从向量集合中返回一个或多个随机成员 | 不适用 | 8.0.0 |
| VRANGE | 按字典范围返回向量集合元素 | 不适用 | 8.4.0 |
向量集合命令摘要(第2部分)
| 命令 | 摘要 | 复杂度 | 引入版本 |
|---|---|---|---|
| VREM | 从向量集合中移除元素 | 不适用 | 8.0.0 |
| VSETATTR | 关联或移除元素的 JSON 属性 | 不适用 | 8.0.0 |
| VSIM | 按向量相似度返回元素 | 不适用 | 8.0.0 |
向量集合是一种类似于有序集合的数据类型,但向量集合的元素具有向量的字符串表示形式(而非分数)。 向量集合允许您向集合中添加项,然后:
- 检索与指定向量最相似的一个子集项,或
- 检索与向量集合中已有元素的向量最相似的一个子集项。
向量集合还支持可选的过滤搜索。您可以为向量集合中的全部或部分元素关联属性,然后在 VSIM 命令中使用 FILTER 选项,在检索与给定向量相似的项的同时对这些属性应用简单的数学过滤器。以下是一个过滤器示例:".year > 1950"。
FP32 格式的字节序注意事项
当在 VADD 和 VSIM 等向量集合命令中使用 FP32 二进制大对象格式时,二进制数据必须以小端字节序编码。这对于跨平台兼容性很重要,因为某些 ARM 变体和其他架构可能使用不同的字节序。
如果您的平台使用大端或混合端编码,您有两种选择:
- 在将二进制大对象传递给 Redis 之前,手动将字节序转换为小端。
- 改用
VALUES语法,它接受字符串形式的浮点数,且与平台无关。
示例
以下示例概述了如何使用向量集合。为清晰起见,我们将使用一组二维向量来表示笛卡尔坐标平面中的点。但在实际用例中,向量通常表示 文本嵌入 并具有数百个维度。有关使用文本嵌入的更多信息,请参阅 Redis for AI。
我们将使用的点为 A:(1.0, 1.0)、B:(-1.0, -1.0)、C:(-1.0, 1.0)、D:(1.0, -1.0) 和 E:(1.0, 0),如下图所示。
基本操作
首先使用 VADD 将点向量添加到名为 points 的集合中。此操作同时会创建向量集合对象。该对象的 TYPE 返回类型为 vectorset。
命令: VADD, TYPE
复杂度:
- TYPE: O(1)
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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); // >>> vectorsetJava(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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();使用 VCARD 获取集合中的元素数量(也称为集合的 基数),使用 VDIM 获取向量的维度:
基于: vadd
命令: VCARD, VDIM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
long res7 = jedis.vcard("points");
System.out.println(res7); // >>> 5
long res8 = jedis.vdim("points");
System.out.println(res8); // >>> 2Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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
});使用 VEMB 从元素中获取坐标值。请注意,由于应用了量化以提高性能,这些值通常不会与您添加向量时提供的精确值相同。
基于: vadd
命令: VEMB
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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]Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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]
});使用 VREM 移除不需要的元素。
基于: vadd
命令: VADD, VCARD, VREM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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); // >>> 5Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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
});使用 VSETATTR 和 VGETATTR 设置和检索元素的 JSON 属性数据。您也可以向 VSETATTR 传递空字符串来删除属性数据:
基于: vadd
命令: VSETATTR, VGETATTR
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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); // >>> nullJava(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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
});向量相似性搜索
使用 VSIM 按向量距离对点进行排序:
难度: 中级
基于: vadd
命令: VSIM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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"]Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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]
});查找距离点 A 最近的四个元素,并显示其距离“分数”:
难度: 中级
基于: vadd
命令: VSIM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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}Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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}
});添加一些 JSON 属性并使用过滤器表达式将其包含在搜索中:
难度: 高级
基于: vadd
命令: VSETATTR, VSIM
可用客户端: Redis CLI, C#, Go, Java(异步 - Lettuce), Java(响应式 - Lettuce), Java(同步 - Jedis), JavaScript(Node.js), PHP, Python, Rust(异步), Rust(同步)
Java(同步 - Jedis)
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"]Java(异步 - Lettuce)
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();Java(响应式 - Lettuce)
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();更多信息
请参阅本节中的其他页面,了解向量集合的功能和性能参数的更多信息。