diff --git a/pom.xml b/pom.xml index 9b5d91ec631428899476c58254f5a16c49e9329a..0e9142ba08ee9cd29daf6b09d5be7b00604092da 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ org.dromara milvus-plus-boot-starter - 2.1.0 + 2.1.4 org.apache.commons @@ -210,6 +210,19 @@ + + + + + + + + redis.clients + jedis + 5.2.0-beta5 + + + diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/DistanceMetric.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/DistanceMetric.java new file mode 100644 index 0000000000000000000000000000000000000000..2552528b4b7390d8fa5b50f4c45b3c22ae87a950 --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/DistanceMetric.java @@ -0,0 +1,7 @@ +package com.github.javpower.javavision.util.redis.vector; + +public enum DistanceMetric { + COSINE, + L2, + IP +} \ No newline at end of file diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldSchema.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldSchema.java new file mode 100644 index 0000000000000000000000000000000000000000..dee22199bf2d9bda089787229a2632f3df991abc --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldSchema.java @@ -0,0 +1,21 @@ +package com.github.javpower.javavision.util.redis.vector; + +import lombok.Data; + +@Data +public class FieldSchema { + private String name; + private FieldType type; + private VectorDataType dataType; + private Integer dimension; + private DistanceMetric metric; + + public FieldSchema(String name, FieldType type, VectorDataType dataType, Integer dimension, DistanceMetric metric) { + this.name = name; + this.type = type; + this.dataType = dataType; + this.dimension = dimension; + this.metric = metric; + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldType.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldType.java new file mode 100644 index 0000000000000000000000000000000000000000..44936b488cbdbbcf2e8f4bbcac37d705ffe2cdbc --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/FieldType.java @@ -0,0 +1,7 @@ +package com.github.javpower.javavision.util.redis.vector; + +public enum FieldType { + TEXT, + VECTOR, + NUMBER +} \ No newline at end of file diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorTest.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c50f195b32b184b6609eb4e071e9dc11cc9487c --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorTest.java @@ -0,0 +1,39 @@ +package com.github.javpower.javavision.util.redis.vector; + +import cn.hutool.json.JSONUtil; +import org.springframework.web.bind.annotation.RestController; +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.search.Document; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +@RestController +public class RedisVectorTest { + + + public static void main(String[] args) { + UnifiedJedis unifiedjedis=new UnifiedJedis(new HostAndPort("xxxxx",6379));; + RedisVectorUtil redisVectorTool=new RedisVectorUtil(unifiedjedis); + // 定义字段结构来创建索引 + List fields = Arrays.asList( + new FieldSchema("text", FieldType.TEXT, null, null, null), + new FieldSchema("vector", FieldType.VECTOR, VectorDataType.FLOAT32, 4, DistanceMetric.COSINE) + ); + // 创建一个名为 "myIndex" 的向量索引 + redisVectorTool.createVectorIndex("testIndex3", fields); + // 添加一个文档到索引,包含文本和向量 + java.util.Map document = new HashMap<>(); + document.put("text", "This is a sample text"); + document.put("vector", new float[]{0.1f, 0.2f, 0.3f, 0.4f});// 示例向量数据 + redisVectorTool.addDocumentToIndex("testIndex3", "1", document); // 假设文档ID为 "1" + // 执行向量搜索,假设我们搜索与上面添加的向量相似的文档 + float[] queryVector = new float[]{0.1f, 0.2f, 0.3f, 0.4f}; + List searchResults = redisVectorTool.searchVector("testIndex3", queryVector, 10); // 限制返回结果为10个 + // 打印搜索结果 + searchResults.forEach(v-> System.out.println(JSONUtil.toJsonStr(v))); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorUtil.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..c0cdc02e6fbfdadd1030651414dd0ab25e57cefc --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/RedisVectorUtil.java @@ -0,0 +1,64 @@ +package com.github.javpower.javavision.util.redis.vector; + +import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.search.*; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RedisVectorUtil { + + private UnifiedJedis unifiedjedis; + + public RedisVectorUtil(UnifiedJedis unifiedjedis){ + this.unifiedjedis=unifiedjedis; + } + public void createVectorIndex(String indexName, List fields) { + String pre= "doc:"+indexName+":"; + IndexDefinition definition = new IndexDefinition().setPrefixes(new String[]{pre}); + Schema schema = new Schema(); + for (FieldSchema field : fields) { + switch (field.getType()){ + case VECTOR: + Map attr = new HashMap<>(); + attr.put("TYPE", field.getDataType().name()); + attr.put("DIM", field.getDimension()); + attr.put("DISTANCE_METRIC", field.getMetric().name()); + schema.addHNSWVectorField(field.getName(),attr); + break; + case TEXT: + schema.addTextField(field.getName(),1); + break; + case NUMBER: + schema.addNumericField(field.getName()); + break; + } + } + unifiedjedis.ftCreate(indexName, IndexOptions.defaultOptions().setDefinition(definition), schema); + } + + public List searchVector(String indexName, float[] queryVector, int limit) { + // 创建查询向量 + byte[] vectorBytes = new byte[queryVector.length * 4]; + ByteBuffer byteBuffer = ByteBuffer.wrap(vectorBytes); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); + for (float v : queryVector) { + byteBuffer.putFloat(v); + } + // 构建查询 + Query query = new Query("*=>[KNN " + limit + " @vector $vector AS score]").addParam("vector", vectorBytes).limit(0,limit).dialect(2); + // 执行搜索 + SearchResult results = unifiedjedis.ftSearch(indexName, query); + // 提取文档 + return results.getDocuments(); + } + + // 向索引中添加文档 + public void addDocumentToIndex(String indexName, String docId, Map fields) { + String key= "doc:"+indexName+":"+docId; + unifiedjedis.hsetObject(key,fields); + } +} diff --git a/src/main/java/com/github/javpower/javavision/util/redis/vector/VectorDataType.java b/src/main/java/com/github/javpower/javavision/util/redis/vector/VectorDataType.java new file mode 100644 index 0000000000000000000000000000000000000000..f2e5b6111866b18e2548dd02d757c516ecb6b547 --- /dev/null +++ b/src/main/java/com/github/javpower/javavision/util/redis/vector/VectorDataType.java @@ -0,0 +1,7 @@ +package com.github.javpower.javavision.util.redis.vector; + +// 定义向量数据类型的枚举 +public enum VectorDataType { + FLOAT32, + FLOAT64 +} \ No newline at end of file