first commit

This commit is contained in:
xgc 2024-05-09 12:24:05 +08:00
commit 38d5963108
32 changed files with 1549 additions and 0 deletions

33
.gitignore vendored Normal file
View 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
View 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
View 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>

View File

@ -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);
}
}

View File

@ -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; // 存储人脸特征的向量
}

View File

@ -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();
// }
//
//
//
//}

View File

@ -0,0 +1,5 @@
server:
port: 8131
milvus:
uri: localhost:8999
token: sss

View 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>

View File

@ -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();
}

View File

@ -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(); // 集合的名称
}

View File

@ -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; // 是否为分区键
}

View File

@ -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 {}; // 指定额外的参数
}

View File

@ -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);
}
}

View 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;
}

View File

@ -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;
}
};
}
}

View 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<>();
}

View 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<>(); //属性名称->表名称
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
package io.github.javpower.milvus.plus.core;
/**
* @author xgc
**/
@FunctionalInterface
public interface FieldFunction<T, R> {
R apply(T entity);
}

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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 ;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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
View 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>