mirror of
https://gitee.com/dromara/MilvusPlus.git
synced 2025-12-06 17:08:27 +08:00
first commit
This commit is contained in:
commit
38d5963108
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
README.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
110
README.cn.md
Normal file
110
README.cn.md
Normal file
@ -0,0 +1,110 @@
|
||||
# MilvusPlus:向量数据库增强操作库
|
||||
|
||||
MilvusPlus 是一个功能强大的 Java 库,旨在简化与 Milvus 向量数据库的交互,为开发者提供类似 MyBatis-Plus 注解和方法调用风格的直观 API。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [特性](#特性)
|
||||
2. [快速开始](#快速开始)
|
||||
3. [应用场景](#应用场景)
|
||||
4. [注解说明](#注解说明)
|
||||
5. [贡献](#贡献)
|
||||
6. [许可证](#许可证)
|
||||
|
||||
## 特性
|
||||
|
||||
- **注解式配置**:采用与 MyBatis-Plus 类似的注解方式来配置你的实体模型。
|
||||
- **直观的 API**:直接的 API 设计,让向量数据库操作变得自然而然。
|
||||
- **易于扩展**:以可扩展性为核心设计,方便新增功能。
|
||||
- **类型安全**:利用 Java 的类型安全特性,最大程度减少错误。
|
||||
|
||||
## 快速开始
|
||||
|
||||
将 MilvusPlus 添加到你的项目中:
|
||||
|
||||
**Maven:**
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.javpower</groupId>
|
||||
<artifactId>MilvusPlus</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## 应用场景
|
||||
|
||||
向量数据库在以下场景中特别有用:
|
||||
|
||||
- **相似性搜索**:快速检索与给定向量最相似的项。
|
||||
- **推荐系统**:根据用户行为和偏好推荐相关内容。
|
||||
- **图像检索**:在大规模图像库中找到与查询图像最相似的图像。
|
||||
- **自然语言处理**:将文本转换为向量并执行语义搜索。
|
||||
- **生物信息学**:分析和比较生物序列,如蛋白质和基因组数据。
|
||||
|
||||
## 注解说明
|
||||
|
||||
MilvusPlus 引入了几个注解,用于将你的 Java 实体映射到 Milvus 集合:
|
||||
|
||||
- `@MilvusCollection`:表示一个 Java 类是一个 Milvus 集合。
|
||||
- `@MilvusField`:将 Java 字段映射到 Milvus 字段,并提供数据类型、维度等选项。
|
||||
- `@MilvusIndex`:在 Milvus 字段上定义索引。
|
||||
|
||||
示例用法:
|
||||
|
||||
```java
|
||||
|
||||
@Data
|
||||
@MilvusCollection(name = "face_collection") // 指定Milvus集合的名称
|
||||
public class Face {
|
||||
@MilvusField(
|
||||
name = "person_id", // 字段名称
|
||||
dataType = DataType.Int64, // 数据类型为64位整数
|
||||
isPrimaryKey = true, // 标记为主键
|
||||
autoID = true // 假设这个ID是自动生成的
|
||||
)
|
||||
private Long personId; // 人员的唯一标识符
|
||||
|
||||
@MilvusField(
|
||||
name = "face_vector", // 字段名称
|
||||
dataType = DataType.FloatVector, // 数据类型为浮点型向量
|
||||
dimension = 128, // 向量维度,假设人脸特征向量的维度是128
|
||||
isPartitionKey = false // 假设这个字段不是分区键
|
||||
)
|
||||
@MilvusIndex(
|
||||
indexType = IndexParam.IndexType.IVF_FLAT, // 使用IVF_FLAT索引类型
|
||||
metricType = IndexParam.MetricType.L2, // 使用L2距离度量类型
|
||||
indexName = "face_index", // 索引名称
|
||||
extraParams = { // 指定额外的索引参数
|
||||
@ExtraParam(key = "nlist", value = "100") // 例如,IVF的nlist参数
|
||||
}
|
||||
)
|
||||
private List<Float> faceVector; // 存储人脸特征的向量
|
||||
}
|
||||
```
|
||||
```
|
||||
public static void main(String[] args) {
|
||||
MilvusWrapper<Face> wrapper=new MilvusWrapper();
|
||||
List<Float> vector = Lists.newArrayList(0.1f,0.2f,0.3f);
|
||||
MilvusResp<Face> resp = wrapper.lambda()
|
||||
.eq(Face::getPersonId,1l)
|
||||
.addVector(vector)
|
||||
.query();
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 贡献
|
||||
|
||||
欢迎贡献!
|
||||
|
||||
- 报告问题或建议功能,[创建一个 issue](https://github.com/yourusername/MilvusPlus/issues/new)。
|
||||
- 提交更改,[创建一个 pull request](https://github.com/yourusername/MilvusPlus/compare)。
|
||||
|
||||
## 许可证
|
||||
|
||||
MilvusPlus 是开源的,遵循 [MIT 许可证](https://github.com/yourusername/MilvusPlus/blob/master/LICENSE)。
|
||||
|
||||
## 联系
|
||||
|
||||
如有问题或需要支持,请联系 [javpower@163.com](mailto:javpower@163.com) 。
|
||||
77
milvus-demo/pom.xml
Normal file
77
milvus-demo/pom.xml
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.javpower</groupId>
|
||||
<artifactId>milvus-demo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>milvus-demo</name>
|
||||
<description>milvus-demo</description>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<spring-boot.version>2.6.13</spring-boot.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.javpower</groupId>
|
||||
<artifactId>milvus-plus-boot-starter</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<configuration>
|
||||
<mainClass>io.github.javpower.milvus.demo.MilvusDemoApplication</mainClass>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>repackage</id>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@ -0,0 +1,13 @@
|
||||
package io.github.javpower.milvus.demo;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class MilvusDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(MilvusDemoApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package io.github.javpower.milvus.demo.model;
|
||||
|
||||
import io.github.javpower.milvus.plus.annotation.ExtraParam;
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusCollection;
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusField;
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusIndex;
|
||||
import io.milvus.v2.common.DataType;
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@MilvusCollection(name = "face_collection") // 指定Milvus集合的名称
|
||||
public class Face {
|
||||
@MilvusField(
|
||||
name = "person_id", // 字段名称
|
||||
dataType = DataType.Int64, // 数据类型为64位整数
|
||||
isPrimaryKey = true, // 标记为主键
|
||||
autoID = true // 假设这个ID是自动生成的
|
||||
)
|
||||
private Long personId; // 人员的唯一标识符
|
||||
|
||||
@MilvusField(
|
||||
name = "face_vector", // 字段名称
|
||||
dataType = DataType.FloatVector, // 数据类型为浮点型向量
|
||||
dimension = 128, // 向量维度,假设人脸特征向量的维度是128
|
||||
isPartitionKey = false // 假设这个字段不是分区键
|
||||
)
|
||||
@MilvusIndex(
|
||||
indexType = IndexParam.IndexType.IVF_FLAT, // 使用IVF_FLAT索引类型
|
||||
metricType = IndexParam.MetricType.L2, // 使用L2距离度量类型
|
||||
indexName = "face_index", // 索引名称
|
||||
extraParams = { // 指定额外的索引参数
|
||||
@ExtraParam(key = "nlist", value = "100") // 例如,IVF的nlist参数
|
||||
}
|
||||
)
|
||||
private List<Float> faceVector; // 存储人脸特征的向量
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
//package io.github.javpower.milvus.demo.test;
|
||||
//
|
||||
//import com.google.common.collect.Lists;
|
||||
//import io.github.javpower.milvus.plus.core.conditions.MilvusWrapper;
|
||||
//import io.github.javpower.milvus.plus.model.MilvusResp;
|
||||
//
|
||||
//import java.util.List;
|
||||
//
|
||||
//public class TestWrapper {
|
||||
// public static void main(String[] args) {
|
||||
// MilvusWrapper<Face> wrapper=new MilvusWrapper();
|
||||
// List<Float> vector = Lists.newArrayList(0.1f,0.2f,0.3f);
|
||||
// MilvusResp<Face> resp = wrapper.lambda()
|
||||
// .eq(Face::getPersonId,1l)
|
||||
// .addVector(vector)
|
||||
// .query();
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
//}
|
||||
5
milvus-demo/src/main/resources/application.yml
Normal file
5
milvus-demo/src/main/resources/application.yml
Normal file
@ -0,0 +1,5 @@
|
||||
server:
|
||||
port: 8131
|
||||
milvus:
|
||||
uri: localhost:8999
|
||||
token: sss
|
||||
201
milvus-plus-boot-starter/pom.xml
Normal file
201
milvus-plus-boot-starter/pom.xml
Normal file
@ -0,0 +1,201 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.javpower</groupId>
|
||||
<artifactId>milvus-plus-boot-starter</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<name>milvus-plus-boot-starter</name>
|
||||
<description>a tool about milvus-plus</description>
|
||||
<url>https://github.com/javpower/milvus-plus</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name> The Apache Software License, Version 2.0 </name>
|
||||
<url> http://www.apache.org/licenses/LICENSE-2.0.txt </url>
|
||||
<distribution> repo </distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>https://github.com/javpower/milvus-plus-boot-starter</url>
|
||||
<connection>scm:git@github.com/javpower/milvus-plus.git</connection>
|
||||
<developerConnection>scm:git@github.com/javpower/milvus-plus.git</developerConnection>
|
||||
</scm>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>gc.x</name>
|
||||
<email>javpower@163.com</email>
|
||||
<organization> https://github.com/javpower</organization>
|
||||
<timezone>+8</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<spring-boot.version>2.7.13</spring-boot.version>
|
||||
<mica-auto.vaersion>2.3.2</mica-auto.vaersion>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.24.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.milvus</groupId>
|
||||
<artifactId>milvus-sdk-java</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-client</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-csv</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-storage-blob</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-identity</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.dreamlu</groupId>
|
||||
<artifactId>mica-auto</artifactId>
|
||||
<version>${mica-auto.vaersion}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<url> https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId> org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
<nohelp>true</nohelp>
|
||||
<charset>UTF-8</charset>
|
||||
<encoding>UTF-8</encoding>
|
||||
<docencoding>UTF-8</docencoding>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package io.github.javpower.milvus.plus.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExtraParam {
|
||||
String key();
|
||||
String value();
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package io.github.javpower.milvus.plus.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MilvusCollection {
|
||||
String name(); // 集合的名称
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package io.github.javpower.milvus.plus.annotation;
|
||||
|
||||
import io.milvus.v2.common.DataType;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MilvusField {
|
||||
|
||||
String name() default ""; // 字段名称,默认使用 Java 字段名
|
||||
DataType dataType() default DataType.FloatVector; // 数据类型,默认为 FLOAT_VECTOR
|
||||
int dimension() default -1; // 向量维度,仅对向量类型有效
|
||||
boolean isPrimaryKey() default false; // 是否为主键
|
||||
boolean autoID() default false; // 是否自动生成
|
||||
String description() default ""; // 字段描述
|
||||
DataType elementType() default DataType.None; // 数组或集合中元素的类型,默认为 INVALID
|
||||
int maxLength() default -1; // 数组或字符串类型的最大长度,默认为 -1(不指定)
|
||||
int maxCapacity() default -1; // 集合类型的最大容量,默认为 -1(不指定)
|
||||
boolean isPartitionKey() default false; // 是否为分区键
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package io.github.javpower.milvus.plus.annotation;
|
||||
|
||||
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MilvusIndex {
|
||||
IndexParam.IndexType indexType() default IndexParam.IndexType.FLAT; // 索引类型
|
||||
IndexParam.MetricType metricType() default IndexParam.MetricType.L2; // 度量类型
|
||||
String indexName() default ""; // 索引名称
|
||||
ExtraParam[] extraParams() default {}; // 指定额外的参数
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package io.github.javpower.milvus.plus.builder;
|
||||
|
||||
import io.github.javpower.milvus.plus.service.MilvusClient;
|
||||
import io.milvus.exception.MilvusException;
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
import io.milvus.v2.service.collection.request.AddFieldReq;
|
||||
import io.milvus.v2.service.collection.request.CreateCollectionReq;
|
||||
import io.milvus.v2.service.index.request.CreateIndexReq;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class CollectionSchemaBuilder {
|
||||
|
||||
private final String collectionName;
|
||||
private final MilvusClient wrapper;
|
||||
private final CreateCollectionReq.CollectionSchema schema;
|
||||
|
||||
public CollectionSchemaBuilder(String collectionName, MilvusClient wrapper) {
|
||||
this.collectionName = collectionName;
|
||||
this.wrapper = wrapper;
|
||||
this.schema = wrapper.client.createSchema();
|
||||
}
|
||||
|
||||
public CollectionSchemaBuilder addField(AddFieldReq field) {
|
||||
schema.addField(field);
|
||||
return this;
|
||||
}
|
||||
public CollectionSchemaBuilder addField(AddFieldReq ... fields) {
|
||||
for (AddFieldReq field : fields) {
|
||||
schema.addField(field);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void createSchema() throws MilvusException {
|
||||
CreateCollectionReq req=CreateCollectionReq.builder().collectionName(this.collectionName).collectionSchema(this.schema).build();
|
||||
wrapper.client.createCollection(req);
|
||||
}
|
||||
public void createIndex(java.util.List<IndexParam> indexParams) throws MilvusException {
|
||||
CreateIndexReq req = CreateIndexReq.builder()
|
||||
.collectionName(collectionName)
|
||||
.indexParams(indexParams)
|
||||
.build();
|
||||
wrapper.client.createIndex(req);
|
||||
}
|
||||
}
|
||||
15
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/ConversionCache.java
vendored
Normal file
15
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/ConversionCache.java
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package io.github.javpower.milvus.plus.cache;
|
||||
|
||||
import io.github.javpower.milvus.plus.model.MilvusEntity;
|
||||
import lombok.Data;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
public class ConversionCache<T,R> {
|
||||
private String collectionName;
|
||||
private FieldFunctionCache<T,R> fieldFunctionCache;
|
||||
private PropertyCache propertyCache;
|
||||
private MilvusEntity milvusEntity;
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package io.github.javpower.milvus.plus.cache;
|
||||
|
||||
import io.github.javpower.milvus.plus.core.FieldFunction;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class FieldFunctionCache<T,R> {
|
||||
|
||||
public Map<String, FieldFunction<T, R>> propertyToFunctionMap = new HashMap<>();//属性名称获取对应的函数
|
||||
|
||||
public String getMethodName(Field field,String fieldName){
|
||||
String capitalizedFieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
|
||||
String getMethodName = "get" + capitalizedFieldName; // 构建get方法名称
|
||||
|
||||
// 检查字段类型是否为boolean,如果是,get方法名称应以is开头
|
||||
if (field.getType() == boolean.class) {
|
||||
getMethodName = "is" + capitalizedFieldName;
|
||||
}
|
||||
return getMethodName;
|
||||
}
|
||||
public String getFieldName(FieldFunction fieldFunction) {
|
||||
for (Map.Entry<String, FieldFunction<T, R>> entry : propertyToFunctionMap.entrySet()) {
|
||||
if (entry.getValue().equals(fieldFunction)) {
|
||||
return entry.getKey(); // 返回匹配的属性名称
|
||||
}
|
||||
}
|
||||
return null; // 如果没有找到匹配项,返回null
|
||||
}
|
||||
public FieldFunction<Object, Object> createFunction(Method method) {
|
||||
return (instance) -> {
|
||||
try {
|
||||
return method.invoke(instance);
|
||||
} catch (Exception e) {
|
||||
// 异常处理
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
14
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/MilvusCache.java
vendored
Normal file
14
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/MilvusCache.java
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package io.github.javpower.milvus.plus.cache;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
public class MilvusCache {
|
||||
public static final Map<Class<?>,ConversionCache<?,?>> milvusCache=new ConcurrentHashMap<>();
|
||||
|
||||
}
|
||||
12
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/PropertyCache.java
vendored
Normal file
12
milvus-plus-boot-starter/src/main/java/io/github/javpower/milvus/plus/cache/PropertyCache.java
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package io.github.javpower.milvus.plus.cache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class PropertyCache {
|
||||
|
||||
public Map<String, String> functionToPropertyMap = new HashMap<>(); //属性名称->表名称
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package io.github.javpower.milvus.plus.config;
|
||||
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusCollection;
|
||||
import io.github.javpower.milvus.plus.service.MilvusCollectionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Component
|
||||
public class MilvusCollectionConfig implements ApplicationRunner {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
private final MilvusCollectionService milvusCollectionService;
|
||||
|
||||
@Autowired
|
||||
public MilvusCollectionConfig(ApplicationContext applicationContext, MilvusCollectionService milvusCollectionService) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.milvusCollectionService = milvusCollectionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
// 获取所有带有@MilvusCollection注解的类
|
||||
String[] beanNames = applicationContext.getBeanNamesForAnnotation(MilvusCollection.class);
|
||||
Class<?>[] annotatedClasses = Arrays.stream(beanNames)
|
||||
.map(applicationContext::getType)
|
||||
.toArray(Class<?>[]::new);
|
||||
// 调用业务处理服务
|
||||
milvusCollectionService.performBusinessLogic(Arrays.asList(annotatedClasses));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package io.github.javpower.milvus.plus.config;
|
||||
|
||||
import io.milvus.v2.client.ConnectConfig;
|
||||
import io.milvus.v2.client.MilvusClientV2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Configuration
|
||||
public class MilvusConfig {
|
||||
@Autowired
|
||||
private MilvusProperties properties;
|
||||
@Bean
|
||||
public MilvusClientV2 milvusClientV2() {
|
||||
ConnectConfig connectConfig = ConnectConfig.builder()
|
||||
.uri(properties.getUri())
|
||||
.token(properties.getToken())
|
||||
.build();
|
||||
return new MilvusClientV2(connectConfig);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package io.github.javpower.milvus.plus.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "milvus")
|
||||
@Component
|
||||
public class MilvusProperties {
|
||||
private String uri;
|
||||
private String token;
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package io.github.javpower.milvus.plus.converter;
|
||||
|
||||
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusCollection;
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusField;
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusIndex;
|
||||
import io.github.javpower.milvus.plus.cache.ConversionCache;
|
||||
import io.github.javpower.milvus.plus.cache.FieldFunctionCache;
|
||||
import io.github.javpower.milvus.plus.cache.MilvusCache;
|
||||
import io.github.javpower.milvus.plus.cache.PropertyCache;
|
||||
import io.github.javpower.milvus.plus.core.FieldFunction;
|
||||
import io.github.javpower.milvus.plus.model.MilvusEntity;
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
import io.milvus.v2.service.collection.request.AddFieldReq;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class MilvusEntityConverter {
|
||||
|
||||
public static MilvusEntity convert(Class<?> entityClass) {
|
||||
MilvusEntity milvus=new MilvusEntity();
|
||||
MilvusCollection collectionAnnotation = entityClass.getAnnotation(MilvusCollection.class);
|
||||
if (collectionAnnotation == null) {
|
||||
throw new IllegalArgumentException("Entity must be annotated with @MilvusCollection");
|
||||
}
|
||||
String collectionName = collectionAnnotation.name();
|
||||
milvus.setCollectionName(collectionName);
|
||||
List<AddFieldReq> milvusFields = new ArrayList<>();
|
||||
List<IndexParam> indexParams=new ArrayList<>();
|
||||
PropertyCache propertyCache=new PropertyCache();
|
||||
FieldFunctionCache fieldFunctionCache=new FieldFunctionCache();
|
||||
for (Field field : entityClass.getDeclaredFields()) {
|
||||
MilvusField fieldAnnotation = field.getAnnotation(MilvusField.class);
|
||||
if (fieldAnnotation != null) {
|
||||
String fieldName = fieldAnnotation.name().isEmpty() ? field.getName() : fieldAnnotation.name();
|
||||
propertyCache.functionToPropertyMap.put(field.getName(),fieldName);
|
||||
String methodName = fieldFunctionCache.getMethodName(field, field.getName());
|
||||
try {
|
||||
Method method = entityClass.getMethod(methodName);
|
||||
FieldFunction function = fieldFunctionCache.createFunction(method);
|
||||
fieldFunctionCache.propertyToFunctionMap.put(field.getName(),function);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
AddFieldReq milvusField = AddFieldReq.builder()
|
||||
.fieldName(fieldName)
|
||||
.dataType(fieldAnnotation.dataType())
|
||||
.isPrimaryKey(fieldAnnotation.isPrimaryKey())
|
||||
.autoID(fieldAnnotation.autoID())
|
||||
.description(StringUtils.isNotEmpty(fieldAnnotation.description()) ? fieldAnnotation.description() : null)
|
||||
.dimension(fieldAnnotation.dimension())
|
||||
.maxLength(fieldAnnotation.maxLength() > 0 ? fieldAnnotation.maxLength() : null)
|
||||
.isPartitionKey(fieldAnnotation.isPartitionKey())
|
||||
.elementType(fieldAnnotation.elementType())
|
||||
.maxCapacity(fieldAnnotation.maxCapacity() > 0 ? fieldAnnotation.maxCapacity() : null)
|
||||
.build();
|
||||
milvusFields.add(milvusField);
|
||||
// 构建IndexParam对象
|
||||
IndexParam indexParam = createIndexParam(field,fieldName);
|
||||
if(indexParam!=null){
|
||||
indexParams.add(indexParam);
|
||||
}
|
||||
}
|
||||
}
|
||||
milvus.setMilvusFields(milvusFields);
|
||||
milvus.setIndexParams(indexParams);
|
||||
//缓存
|
||||
ConversionCache conversionCache=new ConversionCache<>();
|
||||
conversionCache.setMilvusEntity(milvus);
|
||||
conversionCache.setCollectionName(collectionName);
|
||||
conversionCache.setPropertyCache(propertyCache);
|
||||
conversionCache.setFieldFunctionCache(fieldFunctionCache);
|
||||
MilvusCache.milvusCache.put(entityClass,conversionCache);
|
||||
return milvus;
|
||||
}
|
||||
|
||||
|
||||
private static IndexParam createIndexParam(Field field,String fieldName) {
|
||||
MilvusIndex fieldAnnotation = field.getAnnotation(MilvusIndex.class);
|
||||
if (fieldAnnotation == null) {
|
||||
return null;
|
||||
}
|
||||
return IndexParam.builder()
|
||||
.fieldName(fieldAnnotation.indexName().isEmpty() ? fieldName : fieldAnnotation.indexName())
|
||||
.indexType(fieldAnnotation.indexType())
|
||||
.metricType(fieldAnnotation.metricType()) // 默认使用L2距离,根据需要调整
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package io.github.javpower.milvus.plus.converter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.github.javpower.milvus.plus.model.MilvusResp;
|
||||
import io.github.javpower.milvus.plus.model.MilvusResult;
|
||||
import io.milvus.v2.service.vector.response.SearchResp;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class SearchRespConverter {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static <T> MilvusResp<T> convertSearchRespToMilvusResp(SearchResp searchResp, Class<T> entityType){
|
||||
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
|
||||
|
||||
// 反序列化JSON字符串到具体的MilvusResult对象列表
|
||||
TypeReference<List<List<MilvusResult<T>>>> typeRef = new TypeReference<List<List<MilvusResult<T>>>>() {};
|
||||
List<List<MilvusResult<T>>> milvusResults = null;
|
||||
try {
|
||||
// 将searchResults转换为JSON字符串
|
||||
String jsonResults = objectMapper.writeValueAsString(searchResults);
|
||||
milvusResults = objectMapper.readValue(jsonResults, typeRef);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// 创建MilvusResp对象并设置结果
|
||||
MilvusResp<T> milvusResp = new MilvusResp<>();
|
||||
milvusResp.setRes(milvusResults);
|
||||
return milvusResp;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package io.github.javpower.milvus.plus.core;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@FunctionalInterface
|
||||
public interface FieldFunction<T, R> {
|
||||
R apply(T entity);
|
||||
}
|
||||
@ -0,0 +1,380 @@
|
||||
package io.github.javpower.milvus.plus.core.conditions;
|
||||
|
||||
import io.github.javpower.milvus.plus.annotation.MilvusCollection;
|
||||
import io.github.javpower.milvus.plus.cache.ConversionCache;
|
||||
import io.github.javpower.milvus.plus.cache.FieldFunctionCache;
|
||||
import io.github.javpower.milvus.plus.cache.MilvusCache;
|
||||
import io.github.javpower.milvus.plus.cache.PropertyCache;
|
||||
import io.github.javpower.milvus.plus.converter.SearchRespConverter;
|
||||
import io.github.javpower.milvus.plus.core.FieldFunction;
|
||||
import io.github.javpower.milvus.plus.model.MilvusResp;
|
||||
import io.github.javpower.milvus.plus.service.MilvusClient;
|
||||
import io.github.javpower.milvus.plus.util.SpringUtils;
|
||||
import io.milvus.exception.MilvusException;
|
||||
import io.milvus.v2.common.ConsistencyLevel;
|
||||
import io.milvus.v2.service.vector.request.SearchReq;
|
||||
import io.milvus.v2.service.vector.response.SearchResp;
|
||||
import lombok.Data;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
public class MilvusWrapper<T> {
|
||||
|
||||
|
||||
/**
|
||||
* 创建搜索构建器实例
|
||||
* @param collectionName 集合名称
|
||||
* @return 返回搜索构建器
|
||||
*/
|
||||
public LambdaSearchWrapper<T> lambda() {
|
||||
// 获取实例化的类的类型参数T
|
||||
Type type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
Class<T> entityType = (Class<T>) type;
|
||||
// 从实体类上获取@MilvusCollection注解
|
||||
MilvusCollection collectionAnnotation = entityType.getAnnotation(MilvusCollection.class);
|
||||
if (collectionAnnotation == null) {
|
||||
throw new IllegalStateException("Entity type " + entityType.getName() + " is not annotated with @MilvusCollection.");
|
||||
}
|
||||
ConversionCache<?, ?> conversionCache = MilvusCache.milvusCache.get(entityType);
|
||||
String collectionName = conversionCache.getCollectionName();
|
||||
// 使用SpringUtil获取MilvusClient实例
|
||||
MilvusClient client = SpringUtils.getBean(MilvusClient.class);
|
||||
// 使用注解中的集合名称创建LambdaSearchWrapper实例
|
||||
return new LambdaSearchWrapper<>(collectionName, client,conversionCache,entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索构建器内部类,用于构建搜索请求
|
||||
*/
|
||||
@Data
|
||||
public static class LambdaSearchWrapper<T> {
|
||||
private ConversionCache<?, ?> conversionCache;
|
||||
private Class<T> entityType;
|
||||
private String collectionName;
|
||||
private String annsField;
|
||||
private int topK;
|
||||
private List<String> filters = new ArrayList<>();
|
||||
private List<List<Float>> vectors = new ArrayList<>();
|
||||
private long offset;
|
||||
private long limit;
|
||||
private int roundDecimal;
|
||||
private String searchParams;
|
||||
private long guaranteeTimestamp;
|
||||
private ConsistencyLevel consistencyLevel;
|
||||
private boolean ignoreGrowing;
|
||||
private MilvusClient client;
|
||||
|
||||
public LambdaSearchWrapper(String collectionName, MilvusClient client,ConversionCache<?, ?> conversionCache,Class<T> entityType) {
|
||||
this.collectionName = collectionName;
|
||||
this.client = client;
|
||||
this.conversionCache=conversionCache;
|
||||
this.entityType=entityType;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper() {
|
||||
|
||||
}
|
||||
|
||||
// addVector
|
||||
|
||||
public LambdaSearchWrapper<T> addVector(List<Float> vector) {
|
||||
vectors.add(vector);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Common comparison operations
|
||||
public LambdaSearchWrapper<T> eq(String fieldName, Object value) {
|
||||
return addFilter(fieldName, "==", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> ne(String fieldName, Object value) {
|
||||
return addFilter(fieldName, "!=", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> gt(String fieldName, Object value) {
|
||||
return addFilter(fieldName, ">", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> ge(String fieldName, Object value) {
|
||||
return addFilter(fieldName, ">=", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> lt(String fieldName, Object value) {
|
||||
return addFilter(fieldName, "<", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> le(String fieldName, Object value) {
|
||||
return addFilter(fieldName, "<=", value);
|
||||
}
|
||||
|
||||
// Range operation
|
||||
public LambdaSearchWrapper<T> between(String fieldName, Object start, Object end) {
|
||||
String filter = String.format("%s >= %s && %s <= %s", fieldName, convertValue(start), fieldName, convertValue(end));
|
||||
filters.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Null check
|
||||
public LambdaSearchWrapper<T> isNull(String fieldName) {
|
||||
filters.add(fieldName + " == null");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> isNotNull(String fieldName) {
|
||||
filters.add(fieldName + " != null");
|
||||
return this;
|
||||
}
|
||||
|
||||
// In operator
|
||||
public LambdaSearchWrapper<T> in(String fieldName, List<?> values) {
|
||||
String valueList = values.stream()
|
||||
.map(this::convertValue)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
filters.add(fieldName + " in " + valueList);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Like operator
|
||||
public LambdaSearchWrapper<T> like(String fieldName, String value) {
|
||||
filters.add(fieldName + " like '%" + value + "%'");
|
||||
return this;
|
||||
}
|
||||
|
||||
// JSON array operations
|
||||
public LambdaSearchWrapper<T> jsonContains(String fieldName, Object value) {
|
||||
filters.add("JSON_CONTAINS(" + fieldName + ", " + convertValue(value) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> jsonContainsAll(String fieldName, List<?> values) {
|
||||
String valueList = convertValues(values);
|
||||
filters.add("JSON_CONTAINS_ALL(" + fieldName + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> jsonContainsAny(String fieldName, List<?> values) {
|
||||
String valueList = convertValues(values);
|
||||
filters.add("JSON_CONTAINS_ANY(" + fieldName + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
// Array operations
|
||||
public LambdaSearchWrapper<T> arrayContains(String fieldName, Object value) {
|
||||
filters.add("ARRAY_CONTAINS(" + fieldName + ", " + convertValue(value) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> arrayContainsAll(String fieldName, List<?> values) {
|
||||
String valueList = convertValues(values);
|
||||
filters.add("ARRAY_CONTAINS_ALL(" + fieldName + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> arrayContainsAny(String fieldName, List<?> values) {
|
||||
String valueList = convertValues(values);
|
||||
filters.add("ARRAY_CONTAINS_ANY(" + fieldName + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> arrayLength(String fieldName, int length) {
|
||||
filters.add(fieldName + ".length() == " + length);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public LambdaSearchWrapper<T> eq(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, "==", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> ne(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, "!=", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> gt(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, ">", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> ge(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, ">=", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> lt(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, "<", value);
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> le(FieldFunction<T,?> fieldName, Object value) {
|
||||
return addFilter(fieldName, "<=", value);
|
||||
}
|
||||
|
||||
// Range operation
|
||||
public LambdaSearchWrapper<T> between(FieldFunction<T,?> fieldName, Object start, Object end) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String filter = String.format("%s >= %s && %s <= %s", fn, convertValue(start), fn, convertValue(end));
|
||||
filters.add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Null check
|
||||
public LambdaSearchWrapper<T> isNull(FieldFunction<T,?> fieldName) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add(fn + " == null");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> isNotNull(FieldFunction<T,?> fieldName) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add(fn + " != null");
|
||||
return this;
|
||||
}
|
||||
|
||||
// In operator
|
||||
public LambdaSearchWrapper<T> in(FieldFunction<T,?> fieldName, List<?> values) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String valueList = values.stream()
|
||||
.map(this::convertValue)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
filters.add(fn + " in " + valueList);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Like operator
|
||||
public LambdaSearchWrapper<T> like(FieldFunction<T,?> fieldName, String value) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add(fn + " like '%" + value + "%'");
|
||||
return this;
|
||||
}
|
||||
|
||||
// JSON array operations
|
||||
public LambdaSearchWrapper<T> jsonContains(FieldFunction<T,?> fieldName, Object value) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add("JSON_CONTAINS(" + fn + ", " + convertValue(value) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> jsonContainsAll(FieldFunction<T,?> fieldName, List<?> values) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String valueList = convertValues(values);
|
||||
filters.add("JSON_CONTAINS_ALL(" + fn + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> jsonContainsAny(FieldFunction<T,?> fieldName, List<?> values) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String valueList = convertValues(values);
|
||||
filters.add("JSON_CONTAINS_ANY(" + fn + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
// Array operations
|
||||
public LambdaSearchWrapper<T> arrayContains(FieldFunction<T,?> fieldName, Object value) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add("ARRAY_CONTAINS(" + fn + ", " + convertValue(value) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> arrayContainsAll(FieldFunction<T,?> fieldName, List<?> values) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String valueList = convertValues(values);
|
||||
filters.add("ARRAY_CONTAINS_ALL(" + fn + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
public LambdaSearchWrapper<T> arrayContainsAny(FieldFunction<T,?> fieldName, List<?> values) {
|
||||
String fn = getFieldName(fieldName);
|
||||
String valueList = convertValues(values);
|
||||
filters.add("ARRAY_CONTAINS_ANY(" + fn + ", " + valueList + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> arrayLength(FieldFunction<T,?> fieldName, int length) {
|
||||
String fn = getFieldName(fieldName);
|
||||
filters.add(fn + ".length() == " + length);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Logic operations
|
||||
public LambdaSearchWrapper<T> and(LambdaSearchWrapper<T> other) {
|
||||
filters.add("(" + String.join(" && ", other.filters) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> or(LambdaSearchWrapper<T> other) {
|
||||
filters.add("(" + String.join(" || ", other.filters) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaSearchWrapper<T> not() {
|
||||
filters.add("not (" + String.join(" && ", filters) + ")");
|
||||
return this;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
private String convertValue(Object value) {
|
||||
if (value instanceof String) {
|
||||
return "'" + value.toString().replace("'", "\\'") + "'";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
private String convertValues(List<?> values) {
|
||||
return values.stream()
|
||||
.map(this::convertValue)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
private LambdaSearchWrapper<T> addFilter(String fieldName, String op, Object value) {
|
||||
filters.add(fieldName + " " + op + " " + convertValue(value));
|
||||
return this;
|
||||
}
|
||||
private LambdaSearchWrapper<T> addFilter(FieldFunction<T, ?> fieldFunction, String op, Object value) {
|
||||
String fieldName = getFieldName(fieldFunction);
|
||||
filters.add(fieldName + " " + op + " " + convertValue(value));
|
||||
return this;
|
||||
}
|
||||
private String getFieldName(FieldFunction<T, ?> fieldFunction) {
|
||||
FieldFunctionCache<?, ?> fieldFunctionCache = conversionCache.getFieldFunctionCache();
|
||||
String fn = fieldFunctionCache.getFieldName(fieldFunction);
|
||||
PropertyCache propertyCache = conversionCache.getPropertyCache();
|
||||
String fieldName = propertyCache.functionToPropertyMap.get(fn);
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的搜索请求
|
||||
* @return 搜索请求对象
|
||||
*/
|
||||
private SearchReq build() {
|
||||
SearchReq.SearchReqBuilder<?, ?> builder = SearchReq.builder()
|
||||
.collectionName(collectionName)
|
||||
.annsField(annsField)
|
||||
.topK(topK);
|
||||
if (!vectors.isEmpty()) {
|
||||
builder.data(vectors);
|
||||
}
|
||||
String filterStr = filters.stream().collect(Collectors.joining(" && "));
|
||||
if (filterStr != null && !filterStr.isEmpty()) {
|
||||
builder.filter(filterStr);
|
||||
}
|
||||
// Set other parameters as needed
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行搜索
|
||||
* @return 搜索响应对象
|
||||
* @throws MilvusException 如果搜索执行失败
|
||||
*/
|
||||
public MilvusResp<T> query() throws MilvusException {
|
||||
SearchReq searchReq = build();
|
||||
SearchResp search = client.client.search(searchReq);
|
||||
MilvusResp<T> tMilvusResp = SearchRespConverter.convertSearchRespToMilvusResp(search, entityType);
|
||||
return tMilvusResp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package io.github.javpower.milvus.plus.model;
|
||||
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
import io.milvus.v2.service.collection.request.AddFieldReq;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
public class MilvusEntity {
|
||||
private String collectionName;
|
||||
private List<IndexParam> indexParams;
|
||||
private List<AddFieldReq> milvusFields;
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package io.github.javpower.milvus.plus.model;
|
||||
import io.milvus.v2.common.DataType;
|
||||
import io.milvus.v2.service.collection.request.AddFieldReq;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MilvusField {
|
||||
private String fieldName;
|
||||
private DataType dataType;
|
||||
private Boolean isPrimaryKey;
|
||||
private Boolean autoId;
|
||||
private String description;
|
||||
private Integer dimension;
|
||||
private Integer maxLength;
|
||||
private Boolean isPartitionKey;
|
||||
private DataType elementType;
|
||||
private Integer maxCapacity;
|
||||
|
||||
public AddFieldReq to() {
|
||||
return AddFieldReq.builder()
|
||||
.fieldName(fieldName)
|
||||
.dataType(dataType)
|
||||
.isPrimaryKey(isPrimaryKey)
|
||||
.autoID(autoId)
|
||||
.description(StringUtils.isNotEmpty(description) ? description : null)
|
||||
.dimension(dimension)
|
||||
.maxLength(maxLength != null && maxLength > 0 ? maxLength : null)
|
||||
.isPartitionKey(isPartitionKey)
|
||||
.elementType(elementType)
|
||||
.maxCapacity(maxCapacity != null && maxCapacity > 0 ? maxCapacity : null)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package io.github.javpower.milvus.plus.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
public class MilvusResp<T> {
|
||||
private List<List<MilvusResult<T>>> res ;
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package io.github.javpower.milvus.plus.model;
|
||||
|
||||
import lombok.Data;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Data
|
||||
public class MilvusResult<T> {
|
||||
private T entity;
|
||||
private Float distance;
|
||||
private Object id;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package io.github.javpower.milvus.plus.service;
|
||||
|
||||
|
||||
import io.milvus.v2.client.MilvusClientV2;
|
||||
import org.springframework.stereotype.Service;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Service
|
||||
public class MilvusClient implements AutoCloseable {
|
||||
|
||||
public final MilvusClientV2 client;
|
||||
|
||||
public MilvusClient(MilvusClientV2 client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws InterruptedException {
|
||||
client.close(10);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
package io.github.javpower.milvus.plus.service;
|
||||
|
||||
|
||||
import io.github.javpower.milvus.plus.builder.CollectionSchemaBuilder;
|
||||
import io.github.javpower.milvus.plus.converter.MilvusEntityConverter;
|
||||
import io.github.javpower.milvus.plus.model.MilvusEntity;
|
||||
import io.milvus.exception.MilvusException;
|
||||
import io.milvus.v2.common.IndexParam;
|
||||
import io.milvus.v2.service.collection.request.AddFieldReq;
|
||||
import io.milvus.v2.service.collection.request.DescribeCollectionReq;
|
||||
import io.milvus.v2.service.collection.request.DropCollectionReq;
|
||||
import io.milvus.v2.service.collection.request.HasCollectionReq;
|
||||
import io.milvus.v2.service.collection.response.DescribeCollectionResp;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
/**
|
||||
* @author xgc
|
||||
**/
|
||||
@Service
|
||||
public class MilvusCollectionService {
|
||||
private final MilvusClient milvusClient;
|
||||
|
||||
public MilvusCollectionService(MilvusClient milvusClient) {
|
||||
this.milvusClient = milvusClient;
|
||||
}
|
||||
|
||||
public void performBusinessLogic(List<Class<?>> annotatedClasses) {
|
||||
for (Class<?> milvusClass : annotatedClasses) {
|
||||
MilvusEntity milvusEntity = MilvusEntityConverter.convert(milvusClass);
|
||||
try {
|
||||
String collectionName = milvusEntity.getCollectionName();
|
||||
// 检查集合是否存在
|
||||
boolean collectionExists = milvusClient.client.hasCollection(
|
||||
HasCollectionReq.builder().collectionName(collectionName).build()
|
||||
);
|
||||
if (collectionExists) {
|
||||
// 获取集合的详细信息
|
||||
DescribeCollectionResp collectionInfo = milvusClient.client.describeCollection(
|
||||
DescribeCollectionReq.builder().collectionName(collectionName).build()
|
||||
);
|
||||
// 检查字段是否一致,这里需要实现字段比较逻辑
|
||||
List<String> existingFieldNames = collectionInfo.getFieldNames();
|
||||
List<AddFieldReq> requiredFields = milvusEntity.getMilvusFields();
|
||||
List<String> requiredFieldNames = requiredFields.stream().map(AddFieldReq::getFieldName).collect(Collectors.toList());
|
||||
if (!new HashSet<>(existingFieldNames).containsAll(requiredFieldNames) || !new HashSet<>(requiredFieldNames).containsAll(existingFieldNames)) {
|
||||
// 字段不一致,删除并重新创建集合
|
||||
milvusClient.client.dropCollection(
|
||||
DropCollectionReq.builder().collectionName(collectionName).build()
|
||||
);
|
||||
// 创建新集合
|
||||
create(milvusEntity);
|
||||
}
|
||||
} else {
|
||||
// 创建新集合
|
||||
create(milvusEntity);
|
||||
}
|
||||
} catch (MilvusException e) {
|
||||
throw new RuntimeException("Error handling Milvus collection", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void create(MilvusEntity milvusEntity){
|
||||
// 创建新集合
|
||||
CollectionSchemaBuilder schemaBuilder = new CollectionSchemaBuilder(
|
||||
milvusEntity.getCollectionName(), milvusClient
|
||||
);
|
||||
schemaBuilder.addField(milvusEntity.getMilvusFields().toArray(new AddFieldReq[0]));
|
||||
List<IndexParam> indexParams = milvusEntity.getIndexParams();
|
||||
schemaBuilder.createSchema();
|
||||
if (indexParams != null && !indexParams.isEmpty()) {
|
||||
schemaBuilder.createIndex(indexParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package io.github.javpower.milvus.plus.util;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* SpringUtils 提供从Spring应用上下文中获取Bean的静态方法。
|
||||
* @author xgc
|
||||
*/
|
||||
|
||||
@Component
|
||||
public class SpringUtils implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
SpringUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用上下文中的Bean实例。
|
||||
* @param <T> Bean类型
|
||||
* @param clazz Bean的Class
|
||||
* @return Bean实例,如果没有找到返回null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return applicationContext.getBean(clazz);
|
||||
}
|
||||
|
||||
// 如果项目中有多个相同类型的Bean,你可能需要一个更具体的获取方法,例如:
|
||||
/**
|
||||
* 获取应用上下文中指定名称的Bean实例。
|
||||
* @param <T> Bean类型
|
||||
* @param clazz Bean的Class
|
||||
* @param beanName Bean的名称
|
||||
* @return Bean实例,如果没有找到返回null
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(Class<T> clazz, String beanName) {
|
||||
return (T) applicationContext.getBean(beanName, clazz);
|
||||
}
|
||||
}
|
||||
17
pom.xml
Normal file
17
pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.github.javpower</groupId>
|
||||
<artifactId>MilvusPlus</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>milvus-plus</name>
|
||||
<description>milvus-plus</description>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>milvus-demo</module>
|
||||
<module>milvus-plus-boot-starter</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
Loading…
x
Reference in New Issue
Block a user