├── .github ├── ISSUE_TEMPLATE │ ├── 🐛-bug-report.md │ └── 🚀-feature-request.md ├── dependabot.yml └── workflows │ ├── java_sdk_ci.yaml │ └── maven-publish.yml ├── .gitignore ├── LICENSE ├── README-CN.md ├── README.md ├── pom.xml └── src ├── main ├── java │ └── plus │ │ └── jdk │ │ └── milvus │ │ ├── annotation │ │ ├── EmbeddingHandler.java │ │ ├── EnableMilvusPlus.java │ │ ├── VectorCollectionColumn.java │ │ ├── VectorCollectionName.java │ │ ├── VectorRepository.java │ │ └── package-info.java │ │ ├── autoconfigure │ │ ├── IdentifierGeneratorAutoConfiguration.java │ │ ├── MilvusPlusAutoConfiguration.java │ │ ├── MilvusPlusProperties.java │ │ ├── MilvusPlusVersion.java │ │ └── package-info.java │ │ ├── common │ │ ├── MilvusException.java │ │ ├── PropertyNamer.java │ │ └── package-info.java │ │ ├── conditions │ │ ├── AbstractLambdaWrapper.java │ │ ├── AbstractWrapper.java │ │ ├── IExprSegment.java │ │ ├── SharedString.java │ │ ├── Wrapper.java │ │ ├── interfaces │ │ │ ├── Compare.java │ │ │ ├── Func.java │ │ │ ├── Join.java │ │ │ ├── Nested.java │ │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── query │ │ │ ├── Query.java │ │ │ ├── QueryWrapper.java │ │ │ └── package-info.java │ │ ├── search │ │ │ ├── Search.java │ │ │ ├── SearchWrapper.java │ │ │ └── package-info.java │ │ └── segments │ │ │ ├── AbstractISegmentList.java │ │ │ ├── ColumnSegment.java │ │ │ ├── MatchSegment.java │ │ │ ├── MergeSegments.java │ │ │ ├── NormalSegmentList.java │ │ │ └── package-info.java │ │ ├── config │ │ ├── GlobalConfig.java │ │ └── package-info.java │ │ ├── enums │ │ ├── ExprKeyword.java │ │ ├── ExprLike.java │ │ ├── IdType.java │ │ ├── WrapperKeyword.java │ │ └── package-info.java │ │ ├── factory │ │ └── MilvusPlusFactoryBean.java │ │ ├── global │ │ ├── MilvusClientService.java │ │ ├── SimpleTypeRegistry.java │ │ ├── VectorTypeHandler.java │ │ ├── handler │ │ │ ├── AnnotationHandler.java │ │ │ ├── PostInitCollectionInfoHandler.java │ │ │ ├── UnknownTypeHandler.java │ │ │ └── package-info.java │ │ └── package-info.java │ │ ├── incrementer │ │ ├── DefaultIdentifierGenerator.java │ │ ├── IdentifierGenerator.java │ │ └── package-info.java │ │ ├── metadata │ │ ├── CollectionDefinition.java │ │ ├── CollectionHelper.java │ │ ├── ColumnDefinition.java │ │ └── package-info.java │ │ ├── model │ │ ├── ANNOYIndexExtra.java │ │ ├── FLATIndexExtra.java │ │ ├── HNSWIIndexExtra.java │ │ ├── IIndexExtra.java │ │ ├── IVF_FLATIndexExtra.java │ │ ├── IVF_PQIndexExtra.java │ │ ├── IVF_SQ8IndexExtra.java │ │ ├── Page.java │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── record │ │ ├── VectorModel.java │ │ ├── VectorModelRepository.java │ │ ├── VectorModelRepositoryImpl.java │ │ └── package-info.java │ │ ├── selector │ │ ├── MilvusSelector.java │ │ └── package-info.java │ │ ├── toolkit │ │ ├── Assert.java │ │ ├── ClassUtils.java │ │ ├── CollectionUtils.java │ │ ├── Constants.java │ │ ├── ExceptionUtils.java │ │ ├── GenericTypeUtils.java │ │ ├── GlobalConfigUtils.java │ │ ├── IdWorker.java │ │ ├── LambdaUtils.java │ │ ├── ReflectionKit.java │ │ ├── SetAccessibleAction.java │ │ ├── Snowflake.java │ │ ├── StringPool.java │ │ ├── StringUtils.java │ │ ├── SystemClock.java │ │ ├── expr │ │ │ ├── ExprUtils.java │ │ │ ├── StringEscape.java │ │ │ └── package-info.java │ │ ├── package-info.java │ │ ├── reflect │ │ │ ├── IGenericTypeResolver.java │ │ │ ├── SpringReflectionHelper.java │ │ │ └── package-info.java │ │ └── support │ │ │ ├── BiIntFunction.java │ │ │ ├── ColumnCache.java │ │ │ ├── IdeaProxyLambdaMeta.java │ │ │ ├── LambdaMeta.java │ │ │ ├── ReflectLambdaMeta.java │ │ │ ├── SFunction.java │ │ │ ├── SerializedLambda.java │ │ │ ├── ShadowLambdaMeta.java │ │ │ └── package-info.java │ │ ├── utils │ │ ├── Converter.java │ │ └── package-info.java │ │ └── wrapper │ │ ├── LambdaQueryWrapper.java │ │ ├── LambdaSearchWrapper.java │ │ └── package-info.java └── resources │ └── META-INF │ └── spring.factories └── test ├── java └── plus │ └── jdk │ └── milvus │ ├── MilvusTestApplication.java │ ├── UserBlogVectorServiceTest.java │ ├── collection │ └── UserBlogVector.java │ ├── common │ ├── chat │ │ ├── ChatClient.java │ │ └── DefaultChatClient.java │ └── config │ │ └── HttpClientConfigurer.java │ ├── dao │ └── UserBlogVectorDao.java │ └── wrapper │ └── LambdaQueryWrapperTest.java └── resources └── application.yml /.github/ISSUE_TEMPLATE/🐛-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a bug report to help us improve spring-boot-starter-milvus 4 | title: '' 5 | labels: bug 6 | assignees: p-moon, 1402564807 7 | 8 | --- 9 | 10 | Thanks for taking the time to fill out this bug report! Please fill the form in English! 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/🚀-feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: As a user, I want to request a feature for this SDK 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | --- 11 | name: "\U0001F680 Feature request" 12 | about: As a user, I want to request a feature for Milvus 13 | title: '' 14 | labels: kind/feature 15 | assignees: '' 16 | 17 | --- 18 | 19 | 20 | #### Is your feature request related to a problem? Please describe: 21 | 22 | 23 | #### Describe the solution you'd like: 24 | 25 | 26 | #### Describe alternatives you've considered: 27 | 28 | 29 | #### Additional context: 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/java_sdk_ci.yaml: -------------------------------------------------------------------------------- 1 | name: Java sdk CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | name: build 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Build jar 19 | timeout-minutes: 30 20 | shell: bash 21 | run: | 22 | echo "build jar" 23 | git submodule update --init 24 | mvn clean versions:set -DnewVersion=2.2.12 25 | mvn clean install -Dmaven.test.skip=true -Dgpg.skip 26 | 27 | - name: Upload logs 28 | if: ${{ always() }} 29 | uses: actions/upload-artifact@v2 30 | with: 31 | name: logs-java-sdk-ci-test 32 | path: | 33 | tests/test/target/surefire-reports 34 | tests/test/target/allure-results 35 | -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish package to the Maven Central Repository 2 | on: 3 | push: 4 | tags: [ "*" ] 5 | jobs: 6 | publish: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Set up Maven Central Repository 11 | uses: actions/setup-java@v2 12 | with: 13 | java-version: '8' 14 | distribution: 'adopt' 15 | server-id: ossrh 16 | server-username: MAVEN_USERNAME 17 | server-password: MAVEN_PASSWORD 18 | - id: install-secret-key 19 | name: Install gpg secret key 20 | run: | 21 | cat <(echo -e "${{ secrets.MAVEN_GPG_PRIVATE_KEY }}") | gpg --batch --import 22 | gpg --list-secret-keys --keyid-format LONG 23 | - name: Publish package 24 | env: 25 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 26 | MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN }} 27 | run: mvn --batch-mode -Dgpg.passphrase=${{ secrets.MAVEN_GPG_PASSPHRASE }} clean deploy 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | /.idea/ 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### Eclipse ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | build/ 28 | !**/src/main/**/build/ 29 | !**/src/test/**/build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### Mac OS ### 35 | .DS_Store -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | Milvus 2.0 是一款云原生向量数据库,采用存储与计算分离的架构设计。该重构版本的所有组件均为无状态组件,极大地增强了系统弹性和灵活性。更多系统架构细节,参考 [Milvus 系统架构](https://milvus.io/cn/docs/architecture_overview.md)。 2 | 3 | Milvus 基于 [Apache 2.0 License](https://github.com/milvus-io/milvus/blob/master/LICENSE) 协议发布,于 2019 年 10 月正式开源,是 [LF AI & Data 基金会](https://lfaidata.foundation/) 的毕业项目。 4 | 5 | 6 |

一个java风格的Milvus操作库

7 |

8 | 9 | 10 | 11 | 12 |

13 | 该组件是一个仿照mybatis-plus风格编写的Milvus组件, 可以让你像使用mysql那样使用java来操作Milvus,执行精确的query查询或者使用向量执行相似性查询。 14 | 15 | - [English](README.md) 16 | 17 | 18 | ### 一、如何引入 19 | 20 | ```xml 21 | 22 | plus.jdk 23 | spring-boot-starter-milvus 24 | ${last.version} 25 | 26 | ``` 27 | 28 | ### 二、milvus的引用配置 29 | 30 | ```bash 31 | plus.jdk.milvus.enabled=true 32 | plus.jdk.milvus.host=192.168.1.101 33 | plus.jdk.milvus.port=19530 34 | plus.jdk.milvus.user-name=root 35 | plus.jdk.milvus.password=123456 36 | ``` 37 | 38 | ### 三、定义ORM对象 39 | 40 | 41 | ```java 42 | import io.milvus.grpc.DataType; 43 | import lombok.Data; 44 | import lombok.EqualsAndHashCode; 45 | import plus.jdk.milvus.annotation.VectorCollectionColumn; 46 | import plus.jdk.milvus.annotation.VectorCollectionName; 47 | import plus.jdk.milvus.record.VectorModel; 48 | 49 | import java.util.List; 50 | 51 | @Data 52 | @EqualsAndHashCode(callSuper = true) 53 | @VectorCollectionName(name = "user_blog", description = "用户博文向量表") 54 | public class UserBlogVector extends VectorModel { 55 | 56 | /** 57 | * 主键 58 | */ 59 | @VectorCollectionColumn(name = "id", dataType = DataType.Int64, primary = true) 60 | private Long id; 61 | 62 | /** 63 | * uid 64 | */ 65 | @VectorCollectionColumn(name = "uid", dataType = DataType.Int64) 66 | private Long uid; 67 | 68 | /** 69 | * 博文文本 70 | */ 71 | @VectorCollectionColumn(name = "blog_text", dataType = DataType.VarChar, maxLength = 1024) 72 | private String blogText; 73 | 74 | /** 75 | * 博文类型 76 | */ 77 | @VectorCollectionColumn(name = "blog_type", dataType = DataType.JSON) 78 | private JSONObject blogType; 79 | 80 | /** 81 | * 博文文本向量, 此处的博文文本向量使用m3e embedding, 所以是768 82 | */ 83 | @VectorCollectionColumn(name = "v_blog_text", dataType = DataType.FloatVector, vectorDimension = 768) 84 | private List blogTextVector; 85 | } 86 | ``` 87 | 88 | ### 四、定义申明Dao数据层 89 | 90 | 我们在 `VectorModelRepositoryImpl` 封装了很多对 `milvus`进行基本操作的api 91 | 92 | ```java 93 | import com.weibo.biz.omniscience.dolly.milvus.entity.UserBlogVector; 94 | import plus.jdk.milvus.annotation.VectorRepository; 95 | import plus.jdk.milvus.record.VectorModelRepositoryImpl; 96 | 97 | @VectorRepository 98 | public class UserBlogVectorDao extends VectorModelRepositoryImpl { 99 | } 100 | ``` 101 | 102 | **一些常用的api示例如下:** 103 | 104 | ```java 105 | import com.alibaba.fastjson.JSONObject; 106 | import lombok.extern.slf4j.Slf4j; 107 | import org.junit.jupiter.api.Test; 108 | import org.springframework.beans.factory.annotation.Autowired; 109 | import org.springframework.boot.test.context.SpringBootTest; 110 | import plus.jdk.milvus.collection.UserBlogVector; 111 | import plus.jdk.milvus.common.MilvusException; 112 | import plus.jdk.milvus.common.chat.ChatClient; 113 | import plus.jdk.milvus.dao.UserBlogVectorDao; 114 | import plus.jdk.milvus.model.HNSWIIndexExtra; 115 | 116 | import java.util.Arrays; 117 | import java.util.Collections; 118 | import java.util.List; 119 | 120 | 121 | @Slf4j 122 | @SpringBootTest 123 | public class UserBlogVectorServiceTest { 124 | 125 | @Autowired 126 | private UserBlogVectorDao userBlogVectorDao; 127 | 128 | @Autowired 129 | private ChatClient chatClient; 130 | 131 | 132 | /** 133 | * 创建集合和索引 134 | */ 135 | @Test 136 | public void createCollection() throws MilvusException { 137 | boolean ret = userBlogVectorDao.createCollection(); 138 | HNSWIIndexExtra extra = new HNSWIIndexExtra(); 139 | extra.setM(16); 140 | extra.setEfConstruction(8); 141 | userBlogVectorDao.createIndex("idx_blog_vector", 142 | UserBlogVector::getBlogTextVector, extra); 143 | userBlogVectorDao.loadCollection(); 144 | } 145 | 146 | 147 | /** 148 | * 向集合插入记录 149 | */ 150 | @Test 151 | public void insertVector() throws MilvusException { 152 | String text = "宝贝们!!没睡吧啊啊啊 刚出炉的九图 投票!喜欢图几"; 153 | Long uid = 2656274875L; 154 | // long timestamp = System.currentTimeMillis(); 155 | // Date startTime = new Date(timestamp - 3600 * 24 * 10 * 1000L); //最近3天的发博数据 156 | // Date endTime = new Date(timestamp); 157 | UserBlogVector userBlogVector = new UserBlogVector(); 158 | userBlogVector.setBlogText(text); 159 | userBlogVector.setUid(uid); 160 | userBlogVector.setBlogType(new JSONObject() {{ 161 | put("type", Arrays.asList("1", "2")); 162 | }}); 163 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 164 | userBlogVector.setBlogTextVector(embedding.get(0)); 165 | boolean ret = userBlogVectorDao.insert(userBlogVector); 166 | log.info("{}", ret); 167 | } 168 | 169 | /** 170 | * 使用其他字段查找相关内容 171 | */ 172 | @Test 173 | public void query() throws MilvusException { 174 | LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); 175 | wrapper.eq(UserBlogVector::getUid, 2656274875L) 176 | .or() 177 | .ne(UserBlogVector::getUid, 1234567890L) 178 | .or(jsonWrapper -> 179 | jsonWrapper 180 | .jsonContains(UserBlogVector::getBlogType, 1, "type") 181 | .jsonContainsAll(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type") 182 | .or() 183 | .jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("112", "312"), "tasd") 184 | ); 185 | List queryResults = userBlogVectorDao.query(wrapper); 186 | log.info("{}", queryResults); 187 | } 188 | 189 | /** 190 | * 使用向量查找相似度最高的内容。可以结合其他字段做条件查询过滤 191 | */ 192 | @Test 193 | public void search() throws MilvusException { 194 | String text = "Hi guys!! Just out of the oven nine pictures. Vote! Like figure few"; 195 | LambdaSearchWrapper wrapper = new LambdaSearchWrapper<>(); 196 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 197 | wrapper.vector(UserBlogVector::getBlogTextVector, embedding.get(0)); 198 | wrapper.setTopK(10); 199 | wrapper.eq(UserBlogVector::getUid, 2656274875L); 200 | wrapper.jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type"); 201 | List searchResults = userBlogVectorDao.search(wrapper); 202 | log.info("{}", searchResults); 203 | } 204 | 205 | 206 | /** 207 | * 使用主键删除记录 208 | * = 209 | */ 210 | @Test 211 | public void deleteRecord() throws MilvusException { 212 | boolean ret = userBlogVectorDao.remove(12345556); 213 | log.info("{}", ret); 214 | } 215 | 216 | } 217 | ``` 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Milvus 2.0 is a cloud-native vector database, featuring a design architecture that separates storage from computation. All components of this revamped version are stateless, greatly enhancing system resilience and flexibility. For more details about the system architecture, refer to [Milvus System Architecture](https://milvus.io/cn/docs/architecture_overview.md). 2 | 3 | Milvus is released under the [Apache 2.0 License](https://github.com/milvus-io/milvus/blob/master/LICENSE), it was officially open-sourced in October 2019 and now is a graduate project of [LF AI & Data Foundation](https://lfaidata.foundation/). 4 | 5 | 6 |

A Java-style Milvus Operation Library

7 |

8 | 9 | 10 | 11 | 12 |

13 | This component is a Milvus component written in the style of mybatis-plus. It allows you to operate Milvus in java just like using mysql, executing precise query operations, or using vectors to execute similarity queries. 14 | 15 | - [中文文档](README-CN.md) 16 | 17 | ### I. How to Import 18 | 19 | ```xml 20 | 21 | plus.jdk 22 | spring-boot-starter-milvus 23 | ${last.version} 24 | 25 | ``` 26 | 27 | ### II. Milvus Configuration 28 | 29 | ```bash 30 | plus.jdk.milvus.enabled=true 31 | plus.jdk.milvus.host=* 32 | plus.jdk.milvus.port=19530 33 | plus.jdk.milvus.user-name=root 34 | plus.jdk.milvus.*=123456 35 | ``` 36 | 37 | ### III. Define ORM Objects 38 | 39 | 40 | ```java 41 | import io.milvus.grpc.DataType; 42 | import lombok.Data; 43 | import lombok.EqualsAndHashCode; 44 | import plus.jdk.milvus.annotation.VectorCollectionColumn; 45 | import plus.jdk.milvus.annotation.VectorCollectionName; 46 | import plus.jdk.milvus.record.VectorModel; 47 | 48 | import java.util.List; 49 | 50 | @Data 51 | @EqualsAndHashCode(callSuper = true) 52 | @VectorCollectionName(name = "user_blog", description = "User blog vector table") 53 | public class UserBlogVector extends VectorModel { 54 | 55 | /** 56 | * Primary Key 57 | */ 58 | @VectorCollectionColumn(name = "id", dataType = DataType.Int64, primary = true) 59 | private Long id; 60 | 61 | /** 62 | * uid 63 | */ 64 | @VectorCollectionColumn(name = "uid", dataType = DataType.Int64) 65 | private Long uid; 66 | 67 | /** 68 | * Blog text 69 | */ 70 | @VectorCollectionColumn(name = "blog_text", dataType = DataType.VarChar, maxLength = 1024) 71 | private String blogText; 72 | 73 | /** 74 | * Blog type 75 | */ 76 | @VectorCollectionColumn(name = "blog_type", dataType = DataType.JSON) 77 | private JSONObject blogType; 78 | 79 | /** 80 | * Blog text vector, the blog text vector used here is m3e embedding, so it is 768 81 | */ 82 | @VectorCollectionColumn(name = "v_blog_text", dataType = DataType.FloatVector, vectorDimension = 768) 83 | private List blogTextVector; 84 | } 85 | ``` 86 | 87 | ### IV. Define DAO Layer 88 | 89 | We have encapsulated many basic operation APIs for `milvus` in `VectorModelRepositoryImpl`. 90 | 91 | ```java 92 | import com.weibo.biz.omniscience.dolly.milvus.entity.UserBlogVector; 93 | import plus.jdk.milvus.annotation.VectorRepository; 94 | import plus.jdk.milvus.record.VectorModelRepositoryImpl; 95 | 96 | @VectorRepository 97 | public class UserBlogVectorDao extends VectorModelRepositoryImpl { 98 | } 99 | ``` 100 | 101 | **Some commonly used API examples are as follows:** 102 | 103 | ```java 104 | import com.alibaba.fastjson.JSONObject; 105 | import lombok.extern.slf4j.Slf4j; 106 | import org.junit.jupiter.api.Test; 107 | import org.springframework.beans.factory.annotation.Autowired; 108 | import org.springframework.boot.test.context.SpringBootTest; 109 | import plus.jdk.milvus.common.MilvusException; 110 | import plus.jdk.milvus.common.chat.ChatClient; 111 | import plus.jdk.milvus.dao.UserBlogVectorDao; 112 | import plus.jdk.milvus.model.HNSWIIndexExtra; 113 | 114 | import java.util.Arrays; 115 | import java.util.Collections; 116 | import java.util.List; 117 | 118 | 119 | @Slf4j 120 | @SpringBootTest 121 | public class UserBlogVectorServiceTest { 122 | 123 | @Autowired 124 | private UserBlogVectorDao userBlogVectorDao; 125 | 126 | @Autowired 127 | private ChatClient chatClient; 128 | 129 | /** 130 | * Create collection and index 131 | */ 132 | @Test 133 | public void createCollection() throws MilvusException { 134 | boolean ret = userBlogVectorDao.createCollection(); 135 | HNSWIIndexExtra extra = new HNSWIIndexExtra(); 136 | extra.setM(16); 137 | extra.setEfConstruction(8); 138 | userBlogVectorDao.createIndex("idx_blog_vector", 139 | UserBlogVector::getBlogTextVector, extra); 140 | userBlogVectorDao.loadCollection(); 141 | } 142 | 143 | /** 144 | * Insert record into collection 145 | */ 146 | @Test 147 | public void insertVector() throws MilvusException { 148 | String text = "Hi guys!! Just out of the oven nine pictures. Vote! Like figure few"; 149 | Long uid = 2656274875L; 150 | UserBlogVector userBlogVector = new UserBlogVector(); 151 | userBlogVector.setBlogText(text); 152 | userBlogVector.setUid(uid); 153 | userBlogVector.setBlogType(new JSONObject() {{ 154 | put("type", Arrays.asList("1", "2")); 155 | }}); 156 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 157 | userBlogVector.setBlogTextVector(embedding.get(0)); 158 | boolean ret = userBlogVectorDao.insert(userBlogVector); 159 | log.info("{}", ret); 160 | } 161 | 162 | /** 163 | * Use other fields to lookup related content 164 | */ 165 | @Test 166 | public void query() throws MilvusException { 167 | LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); 168 | wrapper.eq(UserBlogVector::getUid, 2656274875L) 169 | .or() 170 | .ne(UserBlogVector::getUid, 1234567890L) 171 | .or(jsonWrapper -> 172 | jsonWrapper 173 | .jsonContains(UserBlogVector::getBlogType, 1, "type") 174 | .jsonContainsAll(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type") 175 | .or() 176 | .jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("112", "312"), "tasd") 177 | ); 178 | List queryResults = userBlogVectorDao.query(wrapper); 179 | log.info("{}", queryResults); 180 | } 181 | 182 | /** 183 | * Use a vector to find the most similar content. You can also combine it with other fields for query filtering 184 | */ 185 | @Test 186 | public void search() throws MilvusException { 187 | String text = "Hi guys!! Just out of the oven nine pictures. Vote! Like figure few"; 188 | LambdaSearchWrapper wrapper = new LambdaSearchWrapper<>(); 189 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 190 | wrapper.vector(UserBlogVector::getBlogTextVector, embedding.get(0)); 191 | wrapper.setTopK(10); 192 | wrapper.eq(UserBlogVector::getUid, 2656274875L); 193 | wrapper.jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type"); 194 | List searchResults = userBlogVectorDao.search(wrapper); 195 | log.info("{}", searchResults); 196 | } 197 | 198 | /** 199 | * Use primary key to delete record 200 | */ 201 | @Test 202 | public void deleteRecord() throws MilvusException { 203 | boolean ret = userBlogVectorDao.remove(12345556); 204 | log.info("{}", ret); 205 | } 206 | } 207 | ``` 208 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | plus.jdk 5 | spring-boot-starter-milvus 6 | 1.1.3 7 | spring-boot-starter-milvus 8 | 9 | spring-boot-starter-milvus 10 | https://github.com/JDK-Plus/spring-boot-starter-milvus 11 | 12 | 13 | 8 14 | 8 15 | UTF-8 16 | 17 | 2.10.1 18 | 1.11.0 19 | 2.0 20 | 4.12.0 21 | 2.3.4 22 | 23 | 24 | 25 | 26 | The MIT License (MIT) 27 | https://github.com/JDK-Plus/spring-boot-starter-milvus/blob/main/LICENSE 28 | 29 | 30 | 31 | 32 | 33 | Moon 34 | moon@jdk.plus 35 | 36 | 37 | 38 | 39 | scm:git:https://github.com/JDK-Plus/spring-boot-starter-milvus.git 40 | scm:git:https://github.com/JDK-Plus/spring-boot-starter-milvus.git 41 | https://github.com/JDK-Plus/spring-boot-starter-milvus 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-parent 46 | 2.7.18 47 | 48 | 49 | 50 | ossrh 51 | https://oss.sonatype.org/service/local/staging/deploy/maven2 52 | 53 | 54 | ossrh 55 | https://s01.oss.sonatype.org/content/repositories/snapshots 56 | 57 | 58 | 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | compile 64 | 65 | 66 | io.milvus 67 | milvus-sdk-java 68 | ${milvus-sdk.version} 69 | 70 | 71 | org.springframework.boot 72 | spring-boot-configuration-processor 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-starter-web 77 | 78 | 79 | org.springframework.boot 80 | spring-boot-starter-tomcat 81 | 82 | 83 | org.springframework.boot 84 | spring-boot-starter-logging 85 | 86 | 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-starter-test 91 | test 92 | 93 | 94 | 95 | org.junit.vintage 96 | junit-vintage-engine 97 | 98 | 99 | 100 | 101 | org.springframework.boot 102 | spring-boot-starter-tomcat 103 | test 104 | 105 | 106 | junit 107 | junit 108 | test 109 | 110 | 111 | com.azure 112 | azure-core 113 | 1.48.0 114 | test 115 | 116 | 117 | com.azure 118 | azure-core-http-netty 119 | 1.14.2 120 | test 121 | 122 | 123 | 124 | 125 | 126 | kr.motd.maven 127 | os-maven-plugin 128 | 1.7.1 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-javadoc-plugin 135 | 3.6.3 136 | 137 | 138 | attach-javadocs 139 | 140 | jar 141 | 142 | 143 | 144 | 145 | 146 | org.sonatype.plugins 147 | nexus-staging-maven-plugin 148 | 1.6.13 149 | true 150 | 151 | ossrh 152 | https://s01.oss.sonatype.org/ 153 | true 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-source-plugin 160 | 3.3.1 161 | 162 | 163 | package 164 | 165 | jar-no-fork 166 | 167 | 168 | 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-gpg-plugin 174 | 3.2.3 175 | 176 | 177 | sign-artifacts 178 | verify 179 | 180 | sign 181 | 182 | 183 | 184 | 185 | 186 | --pinentry-mode 187 | loopback 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/EmbeddingHandler.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.lang.annotation.*; 6 | 7 | @Component 8 | @Documented 9 | @Target(ElementType.TYPE) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | public @interface EmbeddingHandler { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/EnableMilvusPlus.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; 2 | 3 | import org.springframework.context.annotation.Import; 4 | import plus.jdk.milvus.selector.MilvusSelector; 5 | 6 | import java.lang.annotation.*; 7 | 8 | @Documented 9 | @Target(ElementType.TYPE) 10 | @Import(MilvusSelector.class) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | public @interface EnableMilvusPlus { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/VectorCollectionColumn.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; 2 | 3 | import io.milvus.grpc.DataType; 4 | import io.milvus.param.IndexType; 5 | import io.milvus.param.MetricType; 6 | import plus.jdk.milvus.global.VectorTypeHandler; 7 | import plus.jdk.milvus.global.handler.UnknownTypeHandler; 8 | 9 | import java.lang.annotation.*; 10 | 11 | @Documented 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface VectorCollectionColumn { 15 | 16 | /** 17 | * @return 字段名 18 | */ 19 | String name(); 20 | 21 | /** 22 | * @return 是否是主键 23 | */ 24 | boolean primary() default false; 25 | 26 | /** 27 | * @return 数据类型 28 | */ 29 | DataType dataType(); 30 | 31 | /** 32 | * 当数据类型【dataType】为 {DataType.Array} 时,需要指定数组元素的类型【elementType】 33 | * 34 | * @return 数组类型字段的元素类型 35 | */ 36 | DataType elementType() default DataType.None; 37 | 38 | /** 39 | * @return 数组类型字段的最大容量 40 | */ 41 | int maxCapacity() default 32; 42 | 43 | /** 44 | * @return 数据向量化处理的handler 45 | */ 46 | Class> embeddingTypeHandler() default UnknownTypeHandler.class; 47 | 48 | /** 49 | * @return 字段描述 50 | */ 51 | String desc() default ""; 52 | 53 | String property() default ""; 54 | 55 | /** 56 | * @return 指定向量维度, 其他类型无需指定 57 | */ 58 | int vectorDimension() default 1024; 59 | 60 | /** 61 | * @return varchar类型最大长度, 其他类型无需指定 62 | */ 63 | int maxLength() default 512; 64 | 65 | /** 66 | * @return 将字段设置为分区键。分区键字段的值经过哈希处理并分发到不同的逻辑分区。只有 int64 和 varchar 类型字段可以是分区键。主键字段不能是分区键。 67 | */ 68 | boolean partitionKey() default false; 69 | 70 | /** 71 | * @return 是否基于该字段创建索引 72 | */ 73 | boolean index() default false; 74 | 75 | /** 76 | * @return 索引类型 77 | * ... 78 | */ 79 | IndexType indexType() default IndexType.HNSW; 80 | 81 | /** 82 | * @return 度量类型 83 | * ... 84 | */ 85 | MetricType metricType() default MetricType.L2; 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/VectorCollectionName.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Documented 6 | @Target(ElementType.TYPE) 7 | @Retention(RetentionPolicy.RUNTIME) 8 | public @interface VectorCollectionName { 9 | 10 | /** 11 | * @return 表名称 12 | */ 13 | String name(); 14 | 15 | /** 16 | * @return 表描述 17 | */ 18 | String description(); 19 | 20 | /** 21 | * @return 指定数据库,若未指定,则使用默认的 22 | */ 23 | String database() default ""; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/VectorRepository.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; 2 | 3 | import org.springframework.stereotype.Repository; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Repository 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface VectorRepository { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/annotation/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.annotation; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/autoconfigure/IdentifierGeneratorAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.autoconfigure; 2 | 3 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import plus.jdk.milvus.incrementer.DefaultIdentifierGenerator; 7 | import plus.jdk.milvus.incrementer.IdentifierGenerator; 8 | 9 | @Configuration(proxyBeanMethods = false) 10 | public class IdentifierGeneratorAutoConfiguration { 11 | 12 | @Bean 13 | @ConditionalOnMissingBean 14 | public IdentifierGenerator identifierGenerator() { 15 | return DefaultIdentifierGenerator.getInstance(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/autoconfigure/MilvusPlusAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.autoconfigure; 2 | 3 | import io.milvus.client.MilvusServiceClient; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import plus.jdk.milvus.annotation.EnableMilvusPlus; 13 | import plus.jdk.milvus.config.GlobalConfig; 14 | import plus.jdk.milvus.factory.MilvusPlusFactoryBean; 15 | import plus.jdk.milvus.global.MilvusClientService; 16 | import plus.jdk.milvus.global.handler.AnnotationHandler; 17 | import plus.jdk.milvus.incrementer.IdentifierGenerator; 18 | 19 | import java.util.function.Consumer; 20 | 21 | @Slf4j 22 | @EnableMilvusPlus 23 | @Configuration(proxyBeanMethods = false) 24 | @ConditionalOnProperty(prefix = "plus.jdk.milvus", name = "enabled", havingValue = "true") 25 | @EnableConfigurationProperties(MilvusPlusProperties.class) 26 | public class MilvusPlusAutoConfiguration implements InitializingBean { 27 | 28 | private final MilvusPlusProperties properties; 29 | private final ApplicationContext applicationContext; 30 | 31 | 32 | public MilvusPlusAutoConfiguration( 33 | MilvusPlusProperties properties, 34 | ApplicationContext applicationContext 35 | ) { 36 | this.properties = properties; 37 | this.applicationContext = applicationContext; 38 | log.debug("{}", properties); 39 | } 40 | 41 | @Override 42 | public void afterPropertiesSet() { 43 | 44 | } 45 | 46 | @Bean 47 | @ConditionalOnMissingBean 48 | public MilvusClientService milvusClientService() { 49 | MilvusPlusFactoryBean factoryBean = new MilvusPlusFactoryBean(); 50 | GlobalConfig globalConfig = this.properties.getGlobalConfig(); 51 | this.getBeanThen(AnnotationHandler.class, globalConfig::setAnnotationHandler); 52 | this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator); 53 | factoryBean.setGlobalConfig(globalConfig); 54 | factoryBean.setProperties(properties); 55 | MilvusServiceClient client = factoryBean.getObject(); 56 | return new MilvusClientService(client); 57 | } 58 | 59 | /** 60 | * 检查spring容器里是否有对应的bean,有则进行消费 61 | * 62 | * @param clazz class 63 | * @param consumer 消费 64 | * @param 泛型 65 | */ 66 | private void getBeanThen(Class clazz, Consumer consumer) { 67 | if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) { 68 | consumer.accept(this.applicationContext.getBean(clazz)); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/autoconfigure/MilvusPlusProperties.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.autoconfigure; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.boot.context.properties.NestedConfigurationProperty; 6 | import plus.jdk.milvus.config.GlobalConfig; 7 | import plus.jdk.milvus.toolkit.GlobalConfigUtils; 8 | 9 | 10 | /** 11 | * ... 12 | */ 13 | @Data 14 | @ConfigurationProperties(prefix = "plus.jdk.milvus") 15 | public class MilvusPlusProperties { 16 | 17 | /** 18 | * 是否启动 19 | */ 20 | private Boolean enabled = false; 21 | 22 | /** 23 | * 用户名 24 | */ 25 | private String userName; 26 | 27 | /** 28 | * 密码 29 | */ 30 | private String password; 31 | 32 | /** 33 | * 主机名 34 | */ 35 | private String host; 36 | 37 | /** 38 | * 端口 39 | */ 40 | private Integer port; 41 | 42 | /** 43 | * 链接的uri 44 | */ 45 | private String connectUri; 46 | 47 | /** 48 | * 链接超时时间 49 | */ 50 | private Long connectTimeout; 51 | 52 | /** 53 | * Enables the secure for client channel. 54 | * enable – true keep-alive 55 | */ 56 | private Boolean secure = false; 57 | 58 | /** 59 | * Sets the idle timeout value of client channel. The timeout value must be larger than zero. 60 | */ 61 | private Long idleTimeout; 62 | 63 | /** 64 | * Sets the database name. 65 | */ 66 | private String database; 67 | 68 | /** 69 | * Sets the token 70 | */ 71 | private String token; 72 | 73 | /** 74 | * Set a deadline for how long you are willing to wait for a reply from the server. 75 | * With a deadline setting, the client will wait when encounter fast RPC fail caused by network fluctuations. 76 | * The deadline value must be larger than or equal to zero. Default value is 0, deadline is disabled. 77 | */ 78 | private Long rpcDeadline; 79 | 80 | /** 81 | * Sets the keep-alive time value of client channel. 82 | * The keep-alive value must be greater than zero. 83 | */ 84 | private Long keepAliveTime; 85 | 86 | /** 87 | * 全局配置 88 | */ 89 | @NestedConfigurationProperty 90 | private GlobalConfig globalConfig = GlobalConfigUtils.defaults(); 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/autoconfigure/MilvusPlusVersion.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.autoconfigure; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.JarURLConnection; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import java.security.CodeSource; 9 | import java.util.jar.Attributes; 10 | import java.util.jar.JarFile; 11 | 12 | /** 13 | * 获取Milvus-Plus版本 14 | */ 15 | public class MilvusPlusVersion { 16 | 17 | private MilvusPlusVersion() { 18 | } 19 | 20 | public static String getVersion() { 21 | return determineSpringBootVersion(); 22 | } 23 | 24 | private static String determineSpringBootVersion() { 25 | final Package pkg = MilvusPlusVersion.class.getPackage(); 26 | if (pkg != null && pkg.getImplementationVersion() != null) { 27 | return pkg.getImplementationVersion(); 28 | } 29 | CodeSource codeSource = MilvusPlusVersion.class.getProtectionDomain().getCodeSource(); 30 | if (codeSource == null) { 31 | return null; 32 | } 33 | URL codeSourceLocation = codeSource.getLocation(); 34 | try { 35 | URLConnection connection = codeSourceLocation.openConnection(); 36 | if (connection instanceof JarURLConnection) { 37 | return getImplementationVersion(((JarURLConnection) connection).getJarFile()); 38 | } 39 | try (JarFile jarFile = new JarFile(new File(codeSourceLocation.toURI()))) { 40 | return getImplementationVersion(jarFile); 41 | } 42 | } catch (Exception ex) { 43 | return null; 44 | } 45 | } 46 | 47 | private static String getImplementationVersion(JarFile jarFile) throws IOException { 48 | return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/autoconfigure/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.autoconfigure; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/common/MilvusException.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common; 2 | 3 | public class MilvusException extends RuntimeException { 4 | 5 | public MilvusException(String message) { 6 | super(message); 7 | } 8 | 9 | public MilvusException(String message, Throwable t) { 10 | super(message, t); 11 | } 12 | 13 | public MilvusException(Throwable t) { 14 | super(t); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/common/PropertyNamer.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common; 2 | 3 | import java.util.Locale; 4 | 5 | 6 | /** 7 | * @author Clinton Begin 8 | */ 9 | public final class PropertyNamer { 10 | 11 | public static String methodToProperty(String name) { 12 | if (name.startsWith("is")) { 13 | name = name.substring(2); 14 | } else if (name.startsWith("get") || name.startsWith("set")) { 15 | name = name.substring(3); 16 | } else { 17 | throw new MilvusException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'."); 18 | } 19 | 20 | if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) { 21 | name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1); 22 | } 23 | 24 | return name; 25 | } 26 | 27 | public static boolean isProperty(String name) { 28 | return isGetter(name) || isSetter(name); 29 | } 30 | 31 | public static boolean isGetter(String name) { 32 | return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2); 33 | } 34 | 35 | public static boolean isSetter(String name) { 36 | return name.startsWith("set") && name.length() > 3; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/common/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/AbstractLambdaWrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions; 2 | 3 | import plus.jdk.milvus.common.MilvusException; 4 | import plus.jdk.milvus.common.PropertyNamer; 5 | import plus.jdk.milvus.metadata.CollectionHelper; 6 | import plus.jdk.milvus.record.VectorModel; 7 | import plus.jdk.milvus.toolkit.Assert; 8 | import plus.jdk.milvus.toolkit.LambdaUtils; 9 | import plus.jdk.milvus.toolkit.support.ColumnCache; 10 | import plus.jdk.milvus.toolkit.support.LambdaMeta; 11 | import plus.jdk.milvus.toolkit.support.SFunction; 12 | 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | public abstract class AbstractLambdaWrapper>, C extends AbstractLambdaWrapper> 17 | extends AbstractWrapper, C> { 18 | 19 | private final Map columnMap = new ConcurrentHashMap<>(); 20 | 21 | 22 | @Override 23 | protected String columnToString(SFunction column) { 24 | ColumnCache cache = getColumnCache(column); 25 | return cache.getColumn(); 26 | } 27 | 28 | /** 29 | * 获取 SerializedLambda 对应的列信息,从 lambda 表达式中推测实体类 30 | *

31 | * 如果获取不到列信息,那么本次条件组装将会失败 32 | * 33 | * @param column 列 34 | * @return 列 35 | * @throws MilvusException 获取不到列信息时抛出异常 36 | */ 37 | protected ColumnCache getColumnCache(SFunction column) { 38 | LambdaMeta meta = LambdaUtils.extract(column); 39 | String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName()); 40 | Class instantiatedClass = meta.getInstantiatedClass(); 41 | tryInitCache(instantiatedClass); 42 | return getColumnCache(fieldName, instantiatedClass); 43 | } 44 | 45 | private void tryInitCache(Class lambdaClass) { 46 | final Class entityClass = getEntityClass(); 47 | if (entityClass != null) { 48 | lambdaClass = entityClass; 49 | } 50 | Map cacheMap = LambdaUtils.getColumnMap(lambdaClass); 51 | if (cacheMap == null) { 52 | CollectionHelper.initCollectionInfo(lambdaClass); 53 | cacheMap = LambdaUtils.getColumnMap(lambdaClass); 54 | } 55 | columnMap.putAll(cacheMap); 56 | } 57 | 58 | private ColumnCache getColumnCache(String fieldName, Class lambdaClass) { 59 | ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName)); 60 | Assert.notNull(columnCache, "can not find lambda cache for this property [%s] of entity [%s]", 61 | fieldName, lambdaClass.getName()); 62 | return columnCache; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/IExprSegment.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Expr 片段接口 7 | */ 8 | @FunctionalInterface 9 | public interface IExprSegment extends Serializable { 10 | 11 | /** 12 | * Expr 片段 13 | * 14 | * @return Expr片段 15 | */ 16 | String getExprSegment(); 17 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/SharedString.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | import lombok.experimental.Accessors; 7 | import plus.jdk.milvus.toolkit.StringPool; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * 共享查询字段 13 | */ 14 | @Data 15 | @Accessors(chain = true) 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | public class SharedString implements Serializable { 19 | private static final long serialVersionUID = -1536422416594422874L; 20 | 21 | /** 22 | * 共享的 string 值 23 | */ 24 | private String stringValue; 25 | 26 | /** 27 | * SharedString 里是 "" 28 | * 29 | * @return SharedString 里是 "" 30 | */ 31 | public static SharedString emptyString() { 32 | return new SharedString(StringPool.EMPTY); 33 | } 34 | 35 | /** 36 | * 置 empty 37 | * 38 | * @since 3.3.1 39 | */ 40 | public void toEmpty() { 41 | stringValue = StringPool.EMPTY; 42 | } 43 | 44 | /** 45 | * 置 null 46 | * 47 | * @since 3.3.1 48 | */ 49 | public void toNull() { 50 | stringValue = null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/Wrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions; 2 | 3 | import org.apache.commons.collections4.CollectionUtils; 4 | import plus.jdk.milvus.conditions.segments.MergeSegments; 5 | import plus.jdk.milvus.record.VectorModel; 6 | 7 | /** 8 | * 条件构造抽象类 9 | */ 10 | public abstract class Wrapper>> implements IExprSegment { 11 | 12 | /** 13 | * 实体对象(子类实现) 14 | * 15 | * @return 泛型 T 16 | */ 17 | public abstract T getEntity(); 18 | 19 | /** 20 | * 获取 MergeSegments 21 | * 22 | * @return 合并片段 23 | */ 24 | public abstract MergeSegments getExpression(); 25 | 26 | /** 27 | * 查询条件为空 28 | * 29 | * @return 是否为空 30 | */ 31 | public boolean isEmptyOfWhere() { 32 | return isEmptyOfNormal(); 33 | } 34 | 35 | /** 36 | * 查询条件不为空(包含entity) 37 | * 38 | * @return 是否不为空 39 | */ 40 | public boolean isNonEmptyOfWhere() { 41 | return !isEmptyOfWhere(); 42 | } 43 | 44 | /** 45 | * 查询条件为空(不包含entity) 46 | * 47 | * @return 是否为空 48 | */ 49 | public boolean isEmptyOfNormal() { 50 | return CollectionUtils.isEmpty(getExpression().getNormal()); 51 | } 52 | 53 | /** 54 | * 查询条件为空(不包含entity) 55 | * 56 | * @return 是否不为空 57 | */ 58 | public boolean isNonEmptyOfNormal() { 59 | return !isEmptyOfNormal(); 60 | } 61 | 62 | /** 63 | * 获取格式化后的执行Expr 64 | * 65 | * @return Expr 66 | * @since 3.3.1 67 | */ 68 | public String getTargetExpr() { 69 | return getExprSegment().replaceAll("#\\{.+?}", "?"); 70 | } 71 | 72 | /** 73 | * 条件清空 74 | * 75 | * @since 3.3.1 76 | */ 77 | abstract public void clear(); 78 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/interfaces/Compare.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2023, baomidou (jobob@qq.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package plus.jdk.milvus.conditions.interfaces; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * 查询条件封装 22 | *

比较值

23 | */ 24 | public interface Compare extends Serializable { 25 | 26 | /** 27 | * 等于 = 28 | * 29 | * @param column 字段 30 | * @param val 值 31 | * @return children 32 | */ 33 | default Children eq(R column, Object val) { 34 | return eq(true, column, val); 35 | } 36 | 37 | /** 38 | * 等于 = 39 | * 40 | * @param condition 执行条件 41 | * @param column 字段 42 | * @param val 值 43 | * @return children 44 | */ 45 | Children eq(boolean condition, R column, Object val); 46 | 47 | /** 48 | * 不等于 != 49 | * 50 | * @param column 字段 51 | * @param val 值 52 | * @return children 53 | */ 54 | default Children ne(R column, Object val) { 55 | return ne(true, column, val); 56 | } 57 | 58 | /** 59 | * 不等于 != 60 | * 61 | * @param condition 执行条件 62 | * @param column 字段 63 | * @param val 值 64 | * @return children 65 | */ 66 | Children ne(boolean condition, R column, Object val); 67 | 68 | /** 69 | * 大于 > 70 | * 71 | * @param column 字段 72 | * @param val 值 73 | * @return children 74 | */ 75 | default Children gt(R column, Object val) { 76 | return gt(true, column, val); 77 | } 78 | 79 | /** 80 | * 大于 > 81 | * 82 | * @param condition 执行条件 83 | * @param column 字段 84 | * @param val 值 85 | * @return children 86 | */ 87 | Children gt(boolean condition, R column, Object val); 88 | 89 | /** 90 | * 大于等于 >= 91 | * 92 | * @param column 字段 93 | * @param val 值 94 | * @return children 95 | */ 96 | default Children ge(R column, Object val) { 97 | return ge(true, column, val); 98 | } 99 | 100 | /** 101 | * 大于等于 >= 102 | * 103 | * @param condition 执行条件 104 | * @param column 字段 105 | * @param val 值 106 | * @return children 107 | */ 108 | Children ge(boolean condition, R column, Object val); 109 | 110 | /** 111 | * 小于 < 112 | * 113 | * @param column 字段 114 | * @param val 值 115 | * @return children 116 | */ 117 | default Children lt(R column, Object val) { 118 | return lt(true, column, val); 119 | } 120 | 121 | /** 122 | * 小于 < 123 | * 124 | * @param condition 执行条件 125 | * @param column 字段 126 | * @param val 值 127 | * @return children 128 | */ 129 | Children lt(boolean condition, R column, Object val); 130 | 131 | /** 132 | * 小于等于 <= 133 | * 134 | * @param column 字段 135 | * @param val 值 136 | * @return children 137 | */ 138 | default Children le(R column, Object val) { 139 | return le(true, column, val); 140 | } 141 | 142 | /** 143 | * 小于等于 <= 144 | * 145 | * @param condition 执行条件 146 | * @param column 字段 147 | * @param val 值 148 | * @return children 149 | */ 150 | Children le(boolean condition, R column, Object val); 151 | 152 | /** 153 | * NOT LIKE '值%' 154 | * 155 | * @param column 字段 156 | * @param val 值 157 | * @return children 158 | */ 159 | default Children notLikeRight(R column, Object val) { 160 | return notLikeRight(true, column, val); 161 | } 162 | 163 | /** 164 | * NOT LIKE '值%' 165 | * 166 | * @param condition 执行条件 167 | * @param column 字段 168 | * @param val 值 169 | * @return children 170 | */ 171 | Children notLikeRight(boolean condition, R column, Object val); 172 | 173 | /** 174 | * LIKE '值%' 175 | * 176 | * @param column 字段 177 | * @param val 值 178 | * @return children 179 | */ 180 | default Children likeRight(R column, Object val) { 181 | return likeRight(true, column, val); 182 | } 183 | 184 | /** 185 | * LIKE '值%' 186 | * 187 | * @param condition 执行条件 188 | * @param column 字段 189 | * @param val 值 190 | * @return children 191 | */ 192 | Children likeRight(boolean condition, R column, Object val); 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/interfaces/Join.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2023, baomidou (jobob@qq.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package plus.jdk.milvus.conditions.interfaces; 17 | 18 | import java.io.Serializable; 19 | 20 | /** 21 | * 查询条件封装 22 | *

拼接

23 | */ 24 | public interface Join extends Serializable { 25 | 26 | /** 27 | * 拼接 OR 28 | * 29 | * @return children 30 | */ 31 | default Children or() { 32 | return or(true); 33 | } 34 | 35 | /** 36 | * 拼接 OR 37 | * 38 | * @param condition 执行条件 39 | * @return children 40 | */ 41 | Children or(boolean condition); 42 | 43 | /** 44 | * 拼接 Expr 45 | *

例1: apply("id = 1")

46 | * 47 | * @param applyExpr 自定义表达式 48 | * @param values 数据数组 49 | * @return children 50 | */ 51 | default Children apply(String applyExpr, Object... values) { 52 | return apply(true, applyExpr, values); 53 | } 54 | 55 | /** 56 | * 拼接 Expr 57 | *

例1: apply("id = 1")

58 | * 59 | * @param condition 执行条件 60 | * @param applyExpr 自定义表达式 61 | * @param values 数据数组 62 | * @return children 63 | */ 64 | Children apply(boolean condition, String applyExpr, Object... values); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/interfaces/Nested.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2023, baomidou (jobob@qq.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package plus.jdk.milvus.conditions.interfaces; 17 | 18 | import java.io.Serializable; 19 | import java.util.function.Consumer; 20 | 21 | /** 22 | * 查询条件封装 23 | *

嵌套

24 | *
    25 | *
  • 泛型 Param 是具体需要运行函数的类(也是 wrapper 的子类)
  • 26 | *
27 | */ 28 | public interface Nested extends Serializable { 29 | 30 | /** 31 | * AND 嵌套 32 | *

33 | * 例: and(i -> i.eq("name", "李白").ne("status", "活着")) 34 | *

35 | * 36 | * @param consumer 消费函数 37 | * @return children 38 | */ 39 | default Children and(Consumer consumer) { 40 | return and(true, consumer); 41 | } 42 | 43 | /** 44 | * AND 嵌套 45 | *

46 | * 例: and(i -> i.eq("name", "李白").ne("status", "活着")) 47 | *

48 | * 49 | * @param condition 执行条件 50 | * @param consumer 消费函数 51 | * @return children 52 | */ 53 | Children and(boolean condition, Consumer consumer); 54 | 55 | /** 56 | * OR 嵌套 57 | *

58 | * 例: or(i -> i.eq("name", "李白").ne("status", "活着")) 59 | *

60 | * 61 | * @param consumer 消费函数 62 | * @return children 63 | */ 64 | default Children or(Consumer consumer) { 65 | return or(true, consumer); 66 | } 67 | 68 | /** 69 | * OR 嵌套 70 | *

71 | * 例: or(i -> i.eq("name", "李白").ne("status", "活着")) 72 | *

73 | * 74 | * @param condition 执行条件 75 | * @param consumer 消费函数 76 | * @return children 77 | */ 78 | Children or(boolean condition, Consumer consumer); 79 | 80 | /** 81 | * not嵌套 82 | *

83 | * 例: not(i -> i.eq("name", "李白").ne("status", "活着")) 84 | *

85 | * 86 | * @param consumer 消费函数 87 | * @return children 88 | */ 89 | default Children not(Consumer consumer) { 90 | return not(true, consumer); 91 | } 92 | 93 | /** 94 | * not嵌套 95 | *

96 | * 例: not(i -> i.eq("name", "李白").ne("status", "活着")) 97 | *

98 | * 99 | * @param condition 执行条件 100 | * @param consumer 消费函数 101 | * @return children 102 | */ 103 | Children not(boolean condition, Consumer consumer); 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/interfaces/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Wrapper 接口 3 | */ 4 | package plus.jdk.milvus.conditions.interfaces; 5 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/query/Query.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.query; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | public interface Query extends Serializable { 7 | String getExprSelect(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/query/QueryWrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.query; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.experimental.Accessors; 6 | import plus.jdk.milvus.conditions.AbstractWrapper; 7 | import plus.jdk.milvus.conditions.SharedString; 8 | import plus.jdk.milvus.conditions.segments.MergeSegments; 9 | import plus.jdk.milvus.record.VectorModel; 10 | import plus.jdk.milvus.wrapper.LambdaQueryWrapper; 11 | 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | /** 15 | * Entity 对象封装操作类 16 | */ 17 | public class QueryWrapper>> extends AbstractWrapper> 18 | implements Query, T, String> { 19 | 20 | /** 21 | * 查询字段 22 | */ 23 | protected final SharedString exprSelect = new SharedString(); 24 | @Getter 25 | @Setter 26 | @Accessors(chain = true) 27 | private Long offset; 28 | @Getter 29 | @Setter 30 | @Accessors(chain = true) 31 | private Long limit; 32 | 33 | public QueryWrapper() { 34 | this((T) null); 35 | } 36 | 37 | public QueryWrapper(T entity) { 38 | super.setEntity(entity); 39 | super.initNeed(); 40 | } 41 | 42 | public QueryWrapper(Class entityClass) { 43 | super.setEntityClass(entityClass); 44 | super.initNeed(); 45 | } 46 | 47 | /** 48 | * 非对外公开的构造方法,只用于生产嵌套 Expr 49 | * 50 | * @param entityClass 本不应该需要的 51 | */ 52 | private QueryWrapper(T entity, Class entityClass, AtomicInteger paramNameSeq, MergeSegments mergeSegments) { 53 | super.setEntity(entity); 54 | super.setEntityClass(entityClass); 55 | this.paramNameSeq = paramNameSeq; 56 | this.expression = mergeSegments; 57 | } 58 | 59 | @Override 60 | public String getExprSelect() { 61 | return exprSelect.getStringValue(); 62 | } 63 | 64 | /** 65 | * 返回一个支持 lambda 函数写法的 wrapper 66 | * 67 | * @return LambdaQueryWrapper 68 | */ 69 | public LambdaQueryWrapper lambda() { 70 | return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), exprSelect, paramNameSeq, expression, offset, limit); 71 | } 72 | 73 | /** 74 | * 用于生成嵌套 Expr 75 | *

76 | * 故 ExprSelect 不向下传递 77 | *

78 | */ 79 | @Override 80 | protected QueryWrapper instance() { 81 | return new QueryWrapper<>(getEntity(), getEntityClass(), paramNameSeq, new MergeSegments()); 82 | } 83 | 84 | @Override 85 | public void clear() { 86 | super.clear(); 87 | exprSelect.toNull(); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/query/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 查询 Wrapper 3 | */ 4 | package plus.jdk.milvus.conditions.query; 5 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/search/Search.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.search; 2 | 3 | 4 | import java.io.Serializable; 5 | 6 | public interface Search extends Serializable { 7 | String getExprSelect(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/search/SearchWrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.search; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.experimental.Accessors; 6 | import plus.jdk.milvus.conditions.AbstractWrapper; 7 | import plus.jdk.milvus.conditions.SharedString; 8 | import plus.jdk.milvus.conditions.segments.MergeSegments; 9 | import plus.jdk.milvus.model.IIndexExtra; 10 | import plus.jdk.milvus.record.VectorModel; 11 | import plus.jdk.milvus.toolkit.support.SFunction; 12 | import plus.jdk.milvus.wrapper.LambdaSearchWrapper; 13 | 14 | import java.util.List; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | public class SearchWrapper>> extends AbstractWrapper> 18 | implements Search, T, String> { 19 | private static final long serialVersionUID = -1L; 20 | /** 21 | * 查询字段 22 | */ 23 | protected final SharedString exprSelect = new SharedString(); 24 | /** 25 | * 额外的索引查询参数 26 | * Search parameter(s) specific to the index. 27 | * See Vector Index for more information. 28 | */ 29 | @Getter 30 | @Setter 31 | @Accessors(chain = true) 32 | private IIndexExtra extra; 33 | /** 34 | * 查询最相似的多少条数据 35 | * Number of the most similar results to return. 36 | */ 37 | @Getter 38 | @Setter 39 | @Accessors(chain = true) 40 | private Integer topK = 10; 41 | /** 42 | * 指定要检索的向量列 43 | */ 44 | @Getter 45 | @Setter 46 | @Accessors(chain = true) 47 | private SFunction vectorColumn; 48 | /** 49 | * 指定输入向量 50 | */ 51 | @Getter 52 | @Setter 53 | @Accessors(chain = true) 54 | private List vectorValue; 55 | 56 | public SearchWrapper() { 57 | this((T) null); 58 | } 59 | 60 | public SearchWrapper(T entity) { 61 | super.setEntity(entity); 62 | super.initNeed(); 63 | } 64 | 65 | public SearchWrapper(Class entityClass) { 66 | super.setEntityClass(entityClass); 67 | super.initNeed(); 68 | } 69 | 70 | /** 71 | * 非对外公开的构造方法,只用于生产嵌套 Expr 72 | * 73 | * @param entityClass 本不应该需要的 74 | */ 75 | private SearchWrapper(T entity, Class entityClass, AtomicInteger paramNameSeq, 76 | MergeSegments mergeSegments) { 77 | super.setEntity(entity); 78 | super.setEntityClass(entityClass); 79 | this.paramNameSeq = paramNameSeq; 80 | this.expression = mergeSegments; 81 | } 82 | 83 | @Override 84 | public String getExprSelect() { 85 | return exprSelect.getStringValue(); 86 | } 87 | 88 | /** 89 | * 返回一个支持 lambda 函数写法的 wrapper 90 | * 91 | * @return LambdaSearchWrapper 92 | */ 93 | public LambdaSearchWrapper lambda() { 94 | return new LambdaSearchWrapper<>(getEntity(), getEntityClass(), exprSelect, paramNameSeq, 95 | expression, extra, topK, vectorColumn, vectorValue); 96 | } 97 | 98 | /** 99 | * 用于生成嵌套 Expr 100 | *

101 | * 故 ExprSelect 不向下传递 102 | *

103 | */ 104 | @Override 105 | protected SearchWrapper instance() { 106 | return new SearchWrapper<>(getEntity(), getEntityClass(), paramNameSeq, new MergeSegments()); 107 | } 108 | 109 | @Override 110 | public void clear() { 111 | super.clear(); 112 | exprSelect.toNull(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/search/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 查询 Wrapper 3 | */ 4 | package plus.jdk.milvus.conditions.search; 5 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/AbstractISegmentList.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import plus.jdk.milvus.conditions.IExprSegment; 5 | import plus.jdk.milvus.toolkit.StringPool; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | /** 12 | * Expr 片段集合 处理的抽象类 13 | */ 14 | @EqualsAndHashCode(callSuper = true) 15 | public abstract class AbstractISegmentList extends ArrayList implements IExprSegment, StringPool { 16 | 17 | /** 18 | * 最后一个值 19 | */ 20 | IExprSegment lastValue = null; 21 | /** 22 | * 刷新 lastValue 23 | */ 24 | boolean flushLastValue = false; 25 | /** 26 | * 结果集缓存 27 | */ 28 | private String exprSegment = EMPTY; 29 | /** 30 | * 是否缓存过结果集 31 | */ 32 | private boolean cacheExprSegment = true; 33 | 34 | /** 35 | * 重写方法,做个性化适配 36 | * 37 | * @param c 元素集合 38 | * @return 是否添加成功 39 | */ 40 | @Override 41 | public boolean addAll(Collection c) { 42 | List list = new ArrayList<>(c); 43 | boolean goon = transformList(list, list.get(0), list.get(list.size() - 1)); 44 | if (goon) { 45 | cacheExprSegment = false; 46 | if (flushLastValue) { 47 | this.flushLastValue(list); 48 | } 49 | return super.addAll(list); 50 | } 51 | return false; 52 | } 53 | 54 | /** 55 | * 在其中对值进行判断以及更改 list 的内部元素 56 | * 57 | * @param list 传入进来的 IExprSegment 集合 58 | * @param firstSegment IEbnfSegment 集合里第一个值 59 | * @param lastSegment IEbnfSegment 集合里最后一个值 60 | * @return true 是否继续向下执行; false 不再向下执行 61 | */ 62 | protected abstract boolean transformList(List list, IExprSegment firstSegment, IExprSegment lastSegment); 63 | 64 | /** 65 | * 刷新属性 lastValue 66 | */ 67 | private void flushLastValue(List list) { 68 | lastValue = list.get(list.size() - 1); 69 | } 70 | 71 | /** 72 | * 删除元素里最后一个值
73 | * 并刷新属性 lastValue 74 | */ 75 | void removeAndFlushLast() { 76 | remove(size() - 1); 77 | flushLastValue(this); 78 | } 79 | 80 | @Override 81 | public String getExprSegment() { 82 | if (cacheExprSegment) { 83 | return exprSegment; 84 | } 85 | cacheExprSegment = true; 86 | exprSegment = childrenExprSegment(); 87 | return exprSegment; 88 | } 89 | 90 | /** 91 | * 只有该类进行过 addAll 操作,才会触发这个方法 92 | *

93 | * 方法内可以放心进行操作 94 | * 95 | * @return ebnfSegment 96 | */ 97 | protected abstract String childrenExprSegment(); 98 | 99 | @Override 100 | public void clear() { 101 | super.clear(); 102 | lastValue = null; 103 | exprSegment = EMPTY; 104 | cacheExprSegment = true; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/ColumnSegment.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; 2 | 3 | import plus.jdk.milvus.conditions.IExprSegment; 4 | 5 | @FunctionalInterface 6 | public interface ColumnSegment extends IExprSegment { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/MatchSegment.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import plus.jdk.milvus.conditions.IExprSegment; 7 | import plus.jdk.milvus.enums.ExprKeyword; 8 | import plus.jdk.milvus.enums.WrapperKeyword; 9 | 10 | import java.util.function.Predicate; 11 | 12 | /** 13 | * 匹配片段 14 | */ 15 | @Getter 16 | @AllArgsConstructor(access = AccessLevel.PACKAGE) 17 | public enum MatchSegment { 18 | NOT(i -> i == ExprKeyword.NOT), 19 | AND(i -> i == ExprKeyword.AND), 20 | OR(i -> i == ExprKeyword.OR), 21 | AND_OR(i -> i == ExprKeyword.AND || i == ExprKeyword.OR), 22 | APPLY(i -> i == WrapperKeyword.APPLY), 23 | JSON(i -> i == ExprKeyword.JSON || i == ExprKeyword.JSON_ALL || i == ExprKeyword.JSON_ANY), 24 | ARRAY(i -> i == ExprKeyword.ARRAY || i == ExprKeyword.ARRAY_ALL || i == ExprKeyword.ARRAY_ANY || i == ExprKeyword.ARRAY_LENGTH), 25 | ; 26 | 27 | private final Predicate predicate; 28 | 29 | public boolean match(IExprSegment segment) { 30 | return getPredicate().test(segment); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/MergeSegments.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Getter; 5 | import plus.jdk.milvus.conditions.IExprSegment; 6 | import plus.jdk.milvus.toolkit.StringPool; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * 合并 EBNF 片段 13 | */ 14 | @Getter 15 | public class MergeSegments implements IExprSegment { 16 | 17 | private final NormalSegmentList normal = new NormalSegmentList(); 18 | 19 | @Getter(AccessLevel.NONE) 20 | private String exprSegment = StringPool.EMPTY; 21 | @Getter(AccessLevel.NONE) 22 | private boolean cacheExprSegment = true; 23 | 24 | public void add(IExprSegment... iExprSegments) { 25 | List list = Arrays.asList(iExprSegments); 26 | normal.addAll(list); 27 | cacheExprSegment = false; 28 | } 29 | 30 | @Override 31 | public String getExprSegment() { 32 | if (cacheExprSegment) { 33 | return exprSegment; 34 | } 35 | cacheExprSegment = true; 36 | exprSegment = normal.getExprSegment(); 37 | return exprSegment; 38 | } 39 | 40 | /** 41 | * 清理 42 | * 43 | * @since 3.3.1 44 | */ 45 | public void clear() { 46 | exprSegment = StringPool.EMPTY; 47 | cacheExprSegment = true; 48 | normal.clear(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/NormalSegmentList.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; 2 | 3 | import plus.jdk.milvus.conditions.IExprSegment; 4 | import plus.jdk.milvus.enums.ExprKeyword; 5 | 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * 普通片段 11 | */ 12 | public class NormalSegmentList extends AbstractISegmentList { 13 | 14 | /** 15 | * 是否处理了的上个 not 16 | */ 17 | private boolean executeNot = true; 18 | 19 | NormalSegmentList() { 20 | this.flushLastValue = true; 21 | } 22 | 23 | @Override 24 | protected boolean transformList(List list, IExprSegment firstSegment, IExprSegment lastSegment) { 25 | if (list.size() == 1) { 26 | /* 只有 and() 以及 or() 以及 not() 会进入 */ 27 | if (!MatchSegment.NOT.match(firstSegment)) { 28 | //不是 not 29 | if (isEmpty()) { 30 | //EbnfSegment是 and 或者 or 并且在第一位,不继续执行 31 | return false; 32 | } 33 | boolean matchLastAnd = MatchSegment.AND.match(lastValue); 34 | boolean matchLastOr = MatchSegment.OR.match(lastValue); 35 | if (matchLastAnd || matchLastOr) { 36 | //上次最后一个值是 and 或者 or 37 | if (matchLastAnd && MatchSegment.AND.match(firstSegment)) { 38 | return false; 39 | } else if (matchLastOr && MatchSegment.OR.match(firstSegment)) { 40 | return false; 41 | } else { 42 | //和上次的不一样 43 | removeAndFlushLast(); 44 | } 45 | } 46 | } else { 47 | executeNot = false; 48 | return false; 49 | } 50 | } else { 51 | if (MatchSegment.APPLY.match(firstSegment)) { 52 | list.remove(0); 53 | } 54 | if (!MatchSegment.AND_OR.match(lastValue) && !isEmpty()) { 55 | add(ExprKeyword.AND); 56 | } 57 | if (!executeNot) { 58 | list.add(0, ExprKeyword.NOT); 59 | executeNot = true; 60 | } 61 | } 62 | return true; 63 | } 64 | 65 | @Override 66 | protected String childrenExprSegment() { 67 | if (MatchSegment.AND_OR.match(lastValue)) { 68 | removeAndFlushLast(); 69 | } 70 | final String str = this.stream().map(IExprSegment::getExprSegment).collect(Collectors.joining(SPACE)); 71 | return (LEFT_BRACKET + str + RIGHT_BRACKET); 72 | } 73 | 74 | @Override 75 | public void clear() { 76 | super.clear(); 77 | flushLastValue = true; 78 | executeNot = true; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/conditions/segments/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.conditions.segments; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/config/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.config; 2 | 3 | import lombok.Data; 4 | import lombok.experimental.Accessors; 5 | import plus.jdk.milvus.enums.IdType; 6 | import plus.jdk.milvus.global.handler.AnnotationHandler; 7 | import plus.jdk.milvus.global.handler.PostInitCollectionInfoHandler; 8 | import plus.jdk.milvus.incrementer.IdentifierGenerator; 9 | import plus.jdk.milvus.record.VectorModelRepository; 10 | 11 | import java.io.Serializable; 12 | 13 | /** 14 | * Milvus 全局缓存 15 | */ 16 | @Data 17 | @Accessors(chain = true) 18 | public class GlobalConfig implements Serializable { 19 | /** 20 | * 是否开启 LOGO 21 | */ 22 | private boolean banner = true; 23 | /** 24 | * 数据库相关配置 25 | */ 26 | private MilvusConfig milvusConfig; 27 | /** 28 | * Mapper父类 29 | */ 30 | private Class superMapperClass = VectorModelRepository.class; 31 | /** 32 | * 注解控制器 33 | */ 34 | private AnnotationHandler annotationHandler = new AnnotationHandler() { 35 | }; 36 | /** 37 | * 参与 CollectionInfo 的初始化 38 | */ 39 | private PostInitCollectionInfoHandler postInitCollectionInfoHandler = new PostInitCollectionInfoHandler() { 40 | }; 41 | /** 42 | * 主键生成器 43 | */ 44 | private IdentifierGenerator identifierGenerator; 45 | 46 | @Data 47 | public static class MilvusConfig { 48 | /** 49 | * 主键类型 50 | */ 51 | private IdType idType = IdType.ASSIGN_ID; 52 | /** 53 | * 表名前缀 54 | */ 55 | private String collectionPrefix; 56 | /** 57 | * db字段 format 58 | *

59 | * 例: `%s` 60 | *

61 | * 对主键无效 62 | */ 63 | private String columnFormat; 64 | /** 65 | * db 表 format 66 | *

67 | * 例: `%s` 68 | *

69 | */ 70 | private String collectionFormat; 71 | /** 72 | * entity 的字段(property)的 format,只有在 column as property 这种情况下生效 73 | *

74 | * 例: `%s` 75 | *

76 | * 对主键无效 77 | */ 78 | private String propertyFormat; 79 | /** 80 | * 表名是否使用驼峰转下划线命名,只对表名生效 81 | */ 82 | private boolean tableUnderline = true; 83 | /** 84 | * 大写命名,对表名和字段名均生效 85 | */ 86 | private boolean capitalMode = false; 87 | /** 88 | * 逻辑删除全局属性名 89 | */ 90 | private String logicDeleteField; 91 | /** 92 | * 逻辑删除全局值(默认 1、表示已删除) 93 | */ 94 | private String logicDeleteValue = "1"; 95 | /** 96 | * 逻辑未删除全局值(默认 0、表示未删除) 97 | */ 98 | private String logicNotDeleteValue = "0"; 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/config/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.config; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/enums/ExprKeyword.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.enums; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import plus.jdk.milvus.conditions.IExprSegment; 6 | import plus.jdk.milvus.toolkit.StringPool; 7 | 8 | /** 9 | * Expr 保留关键字枚举 10 | */ 11 | @AllArgsConstructor 12 | public enum ExprKeyword implements IExprSegment { 13 | AND("and"), 14 | OR("or"), 15 | NOT("not"), 16 | IN("in"), 17 | NOT_IN("not in"), 18 | LIKE("like"), 19 | NOT_LIKE("not like"), 20 | EQ("=="), 21 | NE("!="), 22 | GT(StringPool.RIGHT_CHEV), 23 | GE(">="), 24 | LT(StringPool.LEFT_CHEV), 25 | LE("<="), 26 | JSON("json_contains"), 27 | JSON_ALL("json_contains_all"), 28 | JSON_ANY("json_contains_any"), 29 | ARRAY("array_contains"), 30 | ARRAY_ALL("array_contains_all"), 31 | ARRAY_ANY("array_contains_any"), 32 | ARRAY_LENGTH("array_length"), 33 | ; 34 | 35 | private final String keyword; 36 | 37 | @Override 38 | public String getExprSegment() { 39 | return this.keyword; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/enums/ExprLike.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.enums; 2 | 3 | /** 4 | * Expr like 枚举 5 | */ 6 | public enum ExprLike { 7 | /** 8 | * %值 9 | */ 10 | LEFT, 11 | /** 12 | * 值% 13 | */ 14 | RIGHT, 15 | /** 16 | * %值% 17 | */ 18 | DEFAULT 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/enums/IdType.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 生成ID类型枚举类 8 | */ 9 | @Getter 10 | @AllArgsConstructor 11 | public enum IdType { 12 | /** 13 | * 数据库ID自增 14 | *

该类型请确保数据库设置了 ID自增 否则无效

15 | */ 16 | AUTO(0), 17 | /** 18 | * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) 19 | */ 20 | NONE(1), 21 | /** 22 | * 用户输入ID 23 | *

该类型可以通过自己注册自动填充插件进行填充

24 | */ 25 | INPUT(2), 26 | 27 | /* 以下2种类型、只有当插入对象ID 为空,才自动填充。 */ 28 | /** 29 | * 分配ID (主键类型为number或string), 30 | * 默认实现类 {@link plus.jdk.milvus.incrementer.DefaultIdentifierGenerator}(雪花算法) 31 | */ 32 | ASSIGN_ID(3), 33 | /** 34 | * 分配UUID (主键类型为 string) 35 | * 默认实现类 {@link plus.jdk.milvus.incrementer.DefaultIdentifierGenerator}(UUID.replace("-","")) 36 | */ 37 | ASSIGN_UUID(4); 38 | 39 | private final int key; 40 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/enums/WrapperKeyword.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.enums; 2 | 3 | import lombok.AllArgsConstructor; 4 | import plus.jdk.milvus.conditions.IExprSegment; 5 | 6 | /** 7 | * wrapper 内部使用枚举 8 | */ 9 | @AllArgsConstructor 10 | public enum WrapperKeyword implements IExprSegment { 11 | /** 12 | * 只用作于辨识,不用于其他 13 | */ 14 | APPLY(null); 15 | 16 | private final String keyword; 17 | 18 | @Override 19 | public String getExprSegment() { 20 | return keyword; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/enums/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.enums; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/factory/MilvusPlusFactoryBean.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.factory; 2 | 3 | import io.milvus.client.MilvusServiceClient; 4 | import io.milvus.param.ConnectParam; 5 | import lombok.Setter; 6 | import org.springframework.beans.factory.FactoryBean; 7 | import org.springframework.beans.factory.InitializingBean; 8 | import org.springframework.boot.Banner; 9 | import org.springframework.boot.ansi.AnsiColor; 10 | import org.springframework.boot.ansi.AnsiOutput; 11 | import org.springframework.boot.ansi.AnsiStyle; 12 | import org.springframework.core.env.Environment; 13 | import plus.jdk.milvus.autoconfigure.MilvusPlusProperties; 14 | import plus.jdk.milvus.autoconfigure.MilvusPlusVersion; 15 | import plus.jdk.milvus.config.GlobalConfig; 16 | 17 | import java.io.PrintStream; 18 | import java.util.concurrent.TimeUnit; 19 | 20 | @Setter 21 | public class MilvusPlusFactoryBean implements FactoryBean, InitializingBean { 22 | 23 | private GlobalConfig globalConfig; 24 | private MilvusServiceClient milvusServiceClient; 25 | private MilvusPlusProperties properties; 26 | 27 | 28 | @Override 29 | public MilvusServiceClient getObject() { 30 | if (this.milvusServiceClient == null) { 31 | afterPropertiesSet(); 32 | } 33 | return this.milvusServiceClient; 34 | } 35 | 36 | @Override 37 | public Class getObjectType() { 38 | return null; 39 | } 40 | 41 | @Override 42 | public void afterPropertiesSet() { 43 | this.milvusServiceClient = buildMilvusServiceClient(); 44 | } 45 | 46 | private MilvusServiceClient buildMilvusServiceClient() { 47 | if (properties == null) { 48 | return null; 49 | } 50 | ConnectParam.Builder builder = ConnectParam.newBuilder(); 51 | if (properties.getHost() != null) { 52 | builder.withHost(properties.getHost()); 53 | } 54 | if (properties.getPort() != null) { 55 | builder.withPort(properties.getPort()); 56 | } 57 | if (properties.getUserName() != null) { 58 | builder.withAuthorization(properties.getUserName(), properties.getPassword()); 59 | } 60 | if (properties.getConnectUri() != null) { 61 | builder.withUri(properties.getConnectUri()); 62 | } 63 | if (properties.getConnectTimeout() != null) { 64 | builder.withConnectTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS); 65 | } 66 | if (properties.getRpcDeadline() != null) { 67 | builder.withRpcDeadline(properties.getRpcDeadline(), TimeUnit.MILLISECONDS); 68 | } 69 | if (properties.getDatabase() != null) { 70 | builder.withDatabaseName(properties.getDatabase()); 71 | } 72 | if (properties.getSecure() != null) { 73 | builder.withSecure(properties.getSecure()); 74 | } 75 | if (properties.getKeepAliveTime() != null) { 76 | builder.withKeepAliveTime(properties.getKeepAliveTime(), TimeUnit.MILLISECONDS); 77 | } 78 | if (properties.getIdleTimeout() != null) { 79 | builder.withIdleTimeout(properties.getIdleTimeout(), TimeUnit.MILLISECONDS); 80 | } 81 | if (properties.getToken() != null) { 82 | builder.withToken(properties.getToken()); 83 | } 84 | 85 | if (globalConfig.isBanner()) { 86 | new MilvusPlusBanner().printBanner(null, null, System.out); 87 | } 88 | 89 | return new MilvusServiceClient(builder.build()); 90 | } 91 | 92 | static class MilvusPlusBanner implements Banner { 93 | 94 | private static final int STRAP_LINE_SIZE = 66; 95 | private final String[] bannerLines = { 96 | " __ ___ _ __ ____ __ ", 97 | " / |/ /(_)/ /_ __ __ __ _____ / __ \\ / /__ __ _____", 98 | " / /|_/ // // /| | / // / / // ___/______ / /_/ // // / / // ___/", 99 | " / / / // // / | |/ // /_/ /(__ )/_____// ____// // /_/ /(__ ) ", 100 | "/_/ /_//_//_/ |___/ \\__,_//____/ /_/ /_/ \\__,_//____/" 101 | }; 102 | private final String MILVUS_PLUS = " :: Milvis-Plus :: "; 103 | 104 | @Override 105 | public void printBanner(Environment environment, Class sourceClass, PrintStream out) { 106 | for (String line : bannerLines) { 107 | out.println(line); 108 | } 109 | String version = MilvusPlusVersion.getVersion(); 110 | version = (version != null) ? " (v" + version + ")" : ""; 111 | StringBuilder padding = new StringBuilder(); 112 | while (padding.length() < STRAP_LINE_SIZE - (version.length() + MILVUS_PLUS.length())) { 113 | padding.append(" "); 114 | } 115 | out.println(AnsiOutput.toString(AnsiColor.BLUE, MILVUS_PLUS, AnsiColor.DEFAULT, padding.toString(), 116 | AnsiStyle.FAINT, version)); 117 | out.println(); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/SimpleTypeRegistry.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.util.Date; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class SimpleTypeRegistry { 10 | private static final Set> SIMPLE_TYPE_SET = new HashSet<>(); 11 | 12 | static { 13 | SIMPLE_TYPE_SET.add(String.class); 14 | SIMPLE_TYPE_SET.add(Byte.class); 15 | SIMPLE_TYPE_SET.add(Short.class); 16 | SIMPLE_TYPE_SET.add(Character.class); 17 | SIMPLE_TYPE_SET.add(Integer.class); 18 | SIMPLE_TYPE_SET.add(Long.class); 19 | SIMPLE_TYPE_SET.add(Float.class); 20 | SIMPLE_TYPE_SET.add(Double.class); 21 | SIMPLE_TYPE_SET.add(Boolean.class); 22 | SIMPLE_TYPE_SET.add(Date.class); 23 | SIMPLE_TYPE_SET.add(Class.class); 24 | SIMPLE_TYPE_SET.add(BigInteger.class); 25 | SIMPLE_TYPE_SET.add(BigDecimal.class); 26 | } 27 | 28 | private SimpleTypeRegistry() { 29 | } 30 | 31 | public static boolean isSimpleType(Class clazz) { 32 | return SIMPLE_TYPE_SET.contains(clazz); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/VectorTypeHandler.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | 6 | public interface VectorTypeHandler { 7 | 8 | /** 9 | * @param data 传入的数据 10 | * @return 因为milvus要求数据输入必须全是list 11 | */ 12 | List serialize(T data); 13 | 14 | 15 | /** 16 | * @param data 传入的数据 17 | * @return 目前没用到,暂时不支持,需要后续新增功能 18 | */ 19 | T deserialize(Collection data); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/handler/AnnotationHandler.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global.handler; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | 7 | 8 | public interface AnnotationHandler { 9 | 10 | /** 11 | * 从类上获取注解 12 | * 13 | * @param beanClass 类的class 14 | * @param annotationClass 要获取的注解class 15 | * @param 具体注解 16 | * @return 注解 17 | */ 18 | default T getAnnotation(Class beanClass, Class annotationClass) { 19 | return beanClass.getAnnotation(annotationClass); 20 | } 21 | 22 | /** 23 | * 判断类上是否存在注解 24 | * 25 | * @param beanClass 类的class 26 | * @param annotationClass 要获取的注解class 27 | * @param 具体注解 28 | * @return 是否包含该注解 29 | */ 30 | default boolean isAnnotationPresent(Class beanClass, Class annotationClass) { 31 | return beanClass.isAnnotationPresent(annotationClass); 32 | } 33 | 34 | /** 35 | * 从字段上获取注解 36 | * 37 | * @param field 字段 38 | * @param annotationClass 要获取的注解class 39 | * @param 具体注解 40 | * @return 注解 41 | */ 42 | default T getAnnotation(Field field, Class annotationClass) { 43 | return field.getAnnotation(annotationClass); 44 | } 45 | 46 | /** 47 | * 判断字段上是否存在注解 48 | * 49 | * @param field 字段 50 | * @param annotationClass 要获取的注解class 51 | * @param 具体注解 52 | * @return 是否包含该注解 53 | */ 54 | default boolean isAnnotationPresent(Field field, Class annotationClass) { 55 | return field.isAnnotationPresent(annotationClass); 56 | } 57 | 58 | /** 59 | * 从方法上获取注解 60 | * 61 | * @param method 方法 62 | * @param annotationClass 要获取的注解class 63 | * @param 具体注解 64 | * @return 注解 65 | */ 66 | default T getAnnotation(Method method, Class annotationClass) { 67 | return method.getAnnotation(annotationClass); 68 | } 69 | 70 | /** 71 | * 判断方法上是否存在注解 72 | * 73 | * @param method 方法 74 | * @param annotationClass 要获取的注解class 75 | * @param 具体注解 76 | * @return 是否包含该注解 77 | */ 78 | default boolean isAnnotationPresent(Method method, Class annotationClass) { 79 | return method.isAnnotationPresent(annotationClass); 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/handler/PostInitCollectionInfoHandler.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global.handler; 2 | 3 | import plus.jdk.milvus.metadata.CollectionDefinition; 4 | import plus.jdk.milvus.metadata.ColumnDefinition; 5 | 6 | /** 7 | * 初始化 CollectionInfo 同时进行一些操作 8 | * 9 | * @author miemie 10 | * @since 2022-09-20 11 | */ 12 | public interface PostInitCollectionInfoHandler { 13 | 14 | /** 15 | * 提供对 CollectionInfo 增强的能力 16 | * 17 | * @param entityType 实体类型 18 | * @return {@link CollectionDefinition} 19 | */ 20 | default CollectionDefinition creteCollectionInfo(Class entityType) { 21 | return new CollectionDefinition(entityType); 22 | } 23 | 24 | /** 25 | * 参与 CollectionInfo 初始化 26 | * 27 | * @param tableInfo TableInfo 28 | */ 29 | default void postCollectionInfo(CollectionDefinition tableInfo) { 30 | // ignore 31 | } 32 | 33 | /** 34 | * 参与 CollectionFieldInfo 初始化 35 | * 36 | * @param fieldInfo TableFieldInfo 37 | */ 38 | default void postFieldInfo(ColumnDefinition fieldInfo) { 39 | // ignore 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/handler/UnknownTypeHandler.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global.handler; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import plus.jdk.milvus.global.VectorTypeHandler; 5 | 6 | import java.util.Collection; 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | @Slf4j 11 | public class UnknownTypeHandler implements VectorTypeHandler { 12 | 13 | /** 14 | * 入库时序列化 15 | */ 16 | @Override 17 | public List serialize(Object data) { 18 | return Collections.singletonList(data); 19 | } 20 | 21 | @Override 22 | public Object deserialize(Collection data) { 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/handler/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global.handler; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/global/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.global; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/incrementer/DefaultIdentifierGenerator.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.incrementer; 2 | 3 | import plus.jdk.milvus.toolkit.Snowflake; 4 | import plus.jdk.milvus.toolkit.SystemClock; 5 | 6 | /** 7 | * 默认生成器 8 | */ 9 | public class DefaultIdentifierGenerator implements IdentifierGenerator { 10 | 11 | private final Snowflake snowflake; 12 | 13 | public DefaultIdentifierGenerator(long workerId) { 14 | this.snowflake = new Snowflake(workerId % 1024); 15 | } 16 | 17 | public DefaultIdentifierGenerator(long workerId, long dataCenterId) { 18 | this.snowflake = new Snowflake((workerId / 2 + dataCenterId / 2) % 1024); 19 | } 20 | 21 | public DefaultIdentifierGenerator(Snowflake snowflake) { 22 | this.snowflake = snowflake; 23 | } 24 | 25 | public static DefaultIdentifierGenerator getInstance() { 26 | return DefaultInstance.INSTANCE; 27 | } 28 | 29 | @Override 30 | public Long nextId(Object entity) { 31 | return snowflake.nextId(); 32 | } 33 | 34 | private static class DefaultInstance { 35 | 36 | public static final DefaultIdentifierGenerator INSTANCE = new DefaultIdentifierGenerator(SystemClock.now()); 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/incrementer/IdentifierGenerator.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.incrementer; 2 | 3 | 4 | import plus.jdk.milvus.toolkit.IdWorker; 5 | import plus.jdk.milvus.toolkit.StringUtils; 6 | 7 | /** 8 | * Id生成器接口 9 | */ 10 | public interface IdentifierGenerator { 11 | 12 | /** 13 | * 判断是否分配 ID 14 | * 15 | * @param idValue 主键值 16 | * @return true 分配 false 无需分配 17 | */ 18 | default boolean assignId(Object idValue) { 19 | return StringUtils.checkValNull(idValue); 20 | } 21 | 22 | /** 23 | * 生成Id 24 | * 25 | * @param entity 实体 26 | * @return id 27 | */ 28 | Number nextId(Object entity); 29 | 30 | /** 31 | * 生成uuid 32 | * 33 | * @param entity 实体 34 | * @return uuid 35 | */ 36 | default String nextUUID(Object entity) { 37 | return IdWorker.get32UUID(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/incrementer/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2023, baomidou (jobob@qq.com). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /** 17 | * key 生成器 18 | */ 19 | package plus.jdk.milvus.incrementer; 20 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/metadata/CollectionDefinition.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.metadata; 2 | 3 | import lombok.Data; 4 | import plus.jdk.milvus.toolkit.StringUtils; 5 | 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | @Data 10 | public class CollectionDefinition { 11 | 12 | /** 13 | * 实体类型 14 | */ 15 | private Class entityType; 16 | /** 17 | * 表名称 18 | */ 19 | private String name; 20 | 21 | /** 22 | * 表描述 23 | */ 24 | private String description; 25 | 26 | /** 27 | * 表主键ID 字段名 28 | */ 29 | private String keyColumn; 30 | 31 | /** 32 | * 表主键ID 属性名 33 | */ 34 | private String keyProperty; 35 | 36 | /** 37 | * 是否开启下划线转驼峰 38 | *

39 | * 未注解指定字段名的情况下,用于自动从 property 推算 column 的命名 40 | */ 41 | private boolean underCamel = true; 42 | 43 | /** 44 | * 表字段信息列表 45 | */ 46 | private List columns; 47 | 48 | /** 49 | * 指定数据库,若未指定,则使用默认的 50 | */ 51 | private String database; 52 | 53 | /** 54 | * 类型 55 | */ 56 | private Class clazz; 57 | 58 | /** 59 | * @param entityType 实体类型 60 | */ 61 | public CollectionDefinition(Class entityType) { 62 | this.entityType = entityType; 63 | } 64 | 65 | public ColumnDefinition getPrimaryColumn() { 66 | for (ColumnDefinition columnDefinition : columns) { 67 | if (Boolean.TRUE.equals(columnDefinition.getPrimary())) { 68 | return columnDefinition; 69 | } 70 | } 71 | return null; 72 | } 73 | 74 | /** 75 | * 是否有主键 76 | * 77 | * @return 是否有 78 | */ 79 | public boolean havePK() { 80 | return StringUtils.isNotBlank(keyColumn); 81 | } 82 | 83 | public List getFieldList() { 84 | return Collections.unmodifiableList(columns); 85 | } 86 | 87 | public ColumnDefinition getColumnByColumnName(String columnName) { 88 | for (ColumnDefinition columnDefinition : columns) { 89 | if (columnDefinition.getName().equals(columnName)) { 90 | return columnDefinition; 91 | } 92 | } 93 | return null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/metadata/CollectionHelper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.metadata; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import plus.jdk.milvus.annotation.VectorCollectionColumn; 5 | import plus.jdk.milvus.annotation.VectorCollectionName; 6 | import plus.jdk.milvus.config.GlobalConfig; 7 | import plus.jdk.milvus.global.SimpleTypeRegistry; 8 | import plus.jdk.milvus.global.VectorTypeHandler; 9 | import plus.jdk.milvus.global.handler.AnnotationHandler; 10 | import plus.jdk.milvus.global.handler.PostInitCollectionInfoHandler; 11 | import plus.jdk.milvus.selector.MilvusSelector; 12 | import plus.jdk.milvus.toolkit.*; 13 | 14 | import java.lang.reflect.Field; 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | 20 | import static java.util.stream.Collectors.toList; 21 | 22 | /** 23 | *

24 | * 实体类反射表辅助类 25 | *

26 | */ 27 | @Slf4j 28 | public class CollectionHelper { 29 | 30 | /** 31 | * 储存反射类表信息 32 | */ 33 | private static final Map, CollectionDefinition> COLLECTION_INFO_CACHE = new ConcurrentHashMap<>(); 34 | 35 | /** 36 | * 储存表名对应的反射类表信息 37 | */ 38 | private static final Map COLLECTION_NAME_INFO_CACHE = new ConcurrentHashMap<>(); 39 | 40 | 41 | /** 42 | * 默认表主键名称 43 | */ 44 | private static final String DEFAULT_ID_NAME = "id"; 45 | 46 | /** 47 | *

48 | * 获取实体映射表信息 49 | *

50 | * 51 | * @param clazz 反射实体类 52 | * @return 数据库表反射信息 53 | */ 54 | public static CollectionDefinition getCollectionInfo(Class clazz) { 55 | if (clazz == null || clazz.isPrimitive() || SimpleTypeRegistry.isSimpleType(clazz) || clazz.isInterface()) { 56 | return null; 57 | } 58 | Class targetClass = ClassUtils.getUserClass(clazz); 59 | CollectionDefinition definition = COLLECTION_INFO_CACHE.get(targetClass); 60 | if (null != definition) { 61 | return definition; 62 | } 63 | definition = initCollectionInfo(targetClass); 64 | 65 | COLLECTION_INFO_CACHE.put(targetClass, definition); 66 | return definition; 67 | } 68 | 69 | /** 70 | *

71 | * 根据表名获取实体映射表信息 72 | *

73 | * 74 | * @param collectionName 表名 75 | * @return 数据库表反射信息 76 | */ 77 | public static CollectionDefinition getCollectionInfo(String collectionName) { 78 | if (StringUtils.isBlank(collectionName)) { 79 | return null; 80 | } 81 | return COLLECTION_NAME_INFO_CACHE.get(collectionName); 82 | } 83 | 84 | 85 | /** 86 | *

87 | * 实体类反射获取表信息【初始化】 88 | *

89 | * 90 | * @param clazz 反射实体类 91 | * @return 数据库表反射信息 92 | */ 93 | public static synchronized CollectionDefinition initCollectionInfo(Class clazz) { 94 | CollectionDefinition targetCollectionInfo = COLLECTION_INFO_CACHE.get(clazz); 95 | if (targetCollectionInfo != null && (clazz.equals(targetCollectionInfo.getClazz()))) { 96 | return targetCollectionInfo; 97 | } 98 | // 不是同一个 Configuration,进行重新初始化 99 | GlobalConfig globalConfig = GlobalConfigUtils.getGlobalConfig(clazz); 100 | PostInitCollectionInfoHandler postInitCollectionInfoHandler = globalConfig.getPostInitCollectionInfoHandler(); 101 | /* 没有获取到缓存信息,则初始化 */ 102 | CollectionDefinition definition = postInitCollectionInfoHandler.creteCollectionInfo(clazz); 103 | 104 | /* 初始化表名相关 */ 105 | initCollectionName(clazz, globalConfig, definition); 106 | /* 初始化字段相关 */ 107 | initCollectionFields(clazz, globalConfig, definition); 108 | 109 | /* 自动构建 resultMap */ 110 | postInitCollectionInfoHandler.postCollectionInfo(definition); 111 | COLLECTION_INFO_CACHE.put(clazz, definition); 112 | COLLECTION_NAME_INFO_CACHE.put(definition.getName(), definition); 113 | 114 | /* 缓存 lambda */ 115 | LambdaUtils.installCache(definition); 116 | return definition; 117 | } 118 | 119 | 120 | /** 121 | *

122 | * 初始化 表数据库类型,表名,resultMap 123 | *

124 | * 125 | * @param clazz 实体类 126 | * @param globalConfig 全局配置 127 | * @param collectionInfo 数据库表反射信息 128 | */ 129 | private static void initCollectionName(Class clazz, GlobalConfig globalConfig, CollectionDefinition collectionInfo) { 130 | /* 数据库全局配置 */ 131 | GlobalConfig.MilvusConfig dbConfig = globalConfig.getMilvusConfig(); 132 | AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler(); 133 | VectorCollectionName vectorCollectionName = annotationHandler.getAnnotation(clazz, VectorCollectionName.class); 134 | if (vectorCollectionName == null) { 135 | throw ExceptionUtils.mpe("Missing @VectorCollectionName Annotation In Class: \"%s\".", clazz.getName()); 136 | } 137 | 138 | String collectionName = clazz.getSimpleName(); 139 | String collectionPrefix = dbConfig.getCollectionPrefix(); 140 | 141 | if (StringUtils.isNotBlank(vectorCollectionName.name())) { 142 | collectionName = vectorCollectionName.name(); 143 | } else { 144 | collectionName = initCollectionNameWithDbConfig(collectionName, dbConfig); 145 | } 146 | 147 | // 表追加前缀 148 | String targetCollectionName = collectionName; 149 | if (StringUtils.isNotBlank(collectionPrefix)) { 150 | targetCollectionName = collectionPrefix + targetCollectionName; 151 | } 152 | 153 | // 表格式化 154 | String collectionFormat = dbConfig.getCollectionFormat(); 155 | if (StringUtils.isNotBlank(collectionFormat)) { 156 | targetCollectionName = String.format(collectionFormat, targetCollectionName); 157 | } 158 | 159 | 160 | collectionInfo.setEntityType(clazz); 161 | collectionInfo.setDescription(vectorCollectionName.description()); 162 | collectionInfo.setDatabase(vectorCollectionName.database()); 163 | collectionInfo.setName(targetCollectionName); 164 | } 165 | 166 | 167 | /** 168 | *

169 | * 初始化 表主键,表字段 170 | *

171 | * 172 | * @param clazz 实体类 173 | * @param globalConfig 全局配置 174 | * @param collectionDefinition 数据库表反射信息 175 | */ 176 | private static void initCollectionFields(Class clazz, GlobalConfig globalConfig, CollectionDefinition collectionDefinition) { 177 | AnnotationHandler annotationHandler = globalConfig.getAnnotationHandler(); 178 | PostInitCollectionInfoHandler postInitCollectionInfoHandler = globalConfig.getPostInitCollectionInfoHandler(); 179 | List list = getAllFields(clazz, annotationHandler); 180 | // 标记是否读取到主键 181 | boolean isReadPK = false; 182 | // 是否存在 @TableId 注解 183 | // boolean existTableId = isExistTableId(list, annotationHandler); 184 | // 是否存在 @TableLogic 注解 185 | // boolean existTableLogic = isExistTableLogic(list, annotationHandler); 186 | boolean existTableLogic = false; 187 | 188 | List columnsList = new ArrayList<>(list.size()); 189 | for (Field field : list) { 190 | // if (excludeProperty.contains(field.getName())) { 191 | // continue; 192 | // } 193 | 194 | // boolean isPK = false; 195 | /* 主键ID 初始化 */ 196 | // if (existTableId) { 197 | // TableId tableId = annotationHandler.getAnnotation(field, TableId.class); 198 | // if (tableId != null) { 199 | // if (isReadPK) { 200 | // throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName()); 201 | // } 202 | // 203 | // initTableIdWithAnnotation(globalConfig, collectionDefinition, field, tableId); 204 | // isPK = isReadPK = true; 205 | // } 206 | // } else if (!isReadPK) { 207 | // isPK = isReadPK = initTableIdWithoutAnnotation(globalConfig, collectionDefinition, field); 208 | // } 209 | 210 | // if (isPK) { 211 | // continue; 212 | // } 213 | final VectorCollectionColumn collectionColumn = annotationHandler.getAnnotation(field, VectorCollectionColumn.class); 214 | 215 | /* 有 @TableField 注解的字段初始化 */ 216 | if (collectionColumn != null) { 217 | VectorTypeHandler typeHandler = MilvusSelector.applicationContext.getBean(collectionColumn.embeddingTypeHandler()); 218 | ColumnDefinition columnDefinition = new ColumnDefinition(globalConfig, collectionDefinition, field, collectionColumn, typeHandler, existTableLogic); 219 | columnsList.add(columnDefinition); 220 | postInitCollectionInfoHandler.postFieldInfo(columnDefinition); 221 | } 222 | } 223 | 224 | /* 字段列表 */ 225 | collectionDefinition.setColumns(columnsList); 226 | 227 | /* 未发现主键注解,提示警告信息 */ 228 | // if (!isReadPK) { 229 | // log.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName())); 230 | // } 231 | } 232 | 233 | /** 234 | * 根据 DbConfig 初始化 表名 235 | * 236 | * @param className 类名 237 | * @param dbConfig DbConfig 238 | * @return 表名 239 | */ 240 | private static String initCollectionNameWithDbConfig(String className, GlobalConfig.MilvusConfig dbConfig) { 241 | String collectionName = className; 242 | // 开启表名下划线申明 243 | if (dbConfig.isTableUnderline()) { 244 | collectionName = StringUtils.camelToUnderline(collectionName); 245 | } 246 | // 大写命名判断 247 | if (dbConfig.isCapitalMode()) { 248 | collectionName = collectionName.toUpperCase(); 249 | } else { 250 | // 首字母小写 251 | collectionName = StringUtils.firstToLowerCase(collectionName); 252 | } 253 | return collectionName; 254 | } 255 | 256 | /** 257 | *

258 | * 获取该类的所有属性列表 259 | *

260 | * 261 | * @param clazz 反射类 262 | * @param annotationHandler 注解处理类 263 | * @return 属性集合 264 | */ 265 | public static List getAllFields(Class clazz, AnnotationHandler annotationHandler) { 266 | List fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz)); 267 | return fieldList.stream() 268 | /* 过滤没有注解的非表字段属性 */ 269 | .filter(field -> annotationHandler.getAnnotation(field, VectorCollectionColumn.class) != null) 270 | .collect(toList()); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/metadata/ColumnDefinition.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.metadata; 2 | 3 | import io.milvus.grpc.DataType; 4 | import io.milvus.param.IndexType; 5 | import io.milvus.param.MetricType; 6 | import lombok.Data; 7 | import plus.jdk.milvus.annotation.VectorCollectionColumn; 8 | import plus.jdk.milvus.config.GlobalConfig; 9 | import plus.jdk.milvus.global.VectorTypeHandler; 10 | import plus.jdk.milvus.toolkit.StringUtils; 11 | import plus.jdk.milvus.toolkit.support.SFunction; 12 | 13 | import java.lang.reflect.Field; 14 | 15 | @Data 16 | public class ColumnDefinition { 17 | 18 | /** 19 | * 属性名 20 | */ 21 | private final String property; 22 | /** 23 | * 是否是主键 24 | */ 25 | private Boolean primary; 26 | /** 27 | * 字段名 28 | */ 29 | private String name; 30 | /** 31 | * 数据类型 32 | */ 33 | private DataType dataType; 34 | /** 35 | * 数组类型字段的元素类型 36 | */ 37 | private DataType elementType; 38 | 39 | /** 40 | * 数组类型字段的最大容量 41 | */ 42 | private int maxCapacity; 43 | 44 | /** 45 | * 数据向量化处理的handler 46 | */ 47 | private VectorTypeHandler vectorTypeHandler; 48 | 49 | 50 | /** 51 | * 是否是基本数据类型 52 | */ 53 | // private final boolean isPrimitive; 54 | /** 55 | * 属性是否是 CharSequence 类型 56 | */ 57 | // private final boolean isCharSequence; 58 | 59 | /** 60 | * 字段描述 61 | */ 62 | private String desc; 63 | 64 | /** 65 | * 66 | */ 67 | private SFunction column; 68 | 69 | 70 | /** 71 | * 字段 72 | */ 73 | private Field field; 74 | 75 | /** 76 | * 向量维度,其他类型不用指定 77 | */ 78 | private Integer vectorDimension = 1024; 79 | 80 | /** 81 | * varchar类型最大长度, 其他类型不用指定 82 | */ 83 | private Integer maxLength = 512; 84 | 85 | /** 86 | * 是否将该字段置为分区键 87 | */ 88 | private Boolean partitionKey = false; 89 | 90 | /** 91 | * 是否基于该字段创建索引 92 | */ 93 | private boolean index = false; 94 | 95 | /** 96 | * 索引类型 97 | * ... 98 | */ 99 | private IndexType indexType = IndexType.HNSW; 100 | 101 | /** 102 | * 度量类型 103 | * ... 104 | */ 105 | private MetricType metricType = MetricType.L2; 106 | 107 | public ColumnDefinition() { 108 | this.property = ""; 109 | } 110 | 111 | /** 112 | * 全新的 存在 CollectionField 注解时使用的构造函数 113 | * 114 | * @param globalConfig 全局配置 115 | * @param collectionDefinition collection信息 116 | * @param field 字段 117 | * @param collectionColumn 字段注解 118 | * @param vectorTypeHandler 向量化处理器 119 | * @param existTableLogic 是否存在逻辑删除 120 | */ 121 | public ColumnDefinition(GlobalConfig globalConfig, CollectionDefinition collectionDefinition, Field field, VectorCollectionColumn collectionColumn, 122 | VectorTypeHandler vectorTypeHandler, boolean existTableLogic) { 123 | 124 | GlobalConfig.MilvusConfig dbConfig = globalConfig.getMilvusConfig(); 125 | field.setAccessible(true); 126 | this.field = field; 127 | this.property = field.getName(); 128 | this.desc = collectionColumn.desc(); 129 | this.primary = collectionColumn.primary(); 130 | this.dataType = collectionColumn.dataType(); 131 | this.vectorTypeHandler = vectorTypeHandler; 132 | this.partitionKey = collectionColumn.partitionKey(); 133 | this.vectorDimension = collectionColumn.vectorDimension(); 134 | this.maxLength = collectionColumn.maxLength(); 135 | this.index = collectionColumn.index(); 136 | this.indexType = collectionColumn.indexType(); 137 | this.metricType = collectionColumn.metricType(); 138 | this.elementType = collectionColumn.elementType(); 139 | this.maxCapacity = collectionColumn.maxCapacity(); 140 | // this.propertyType = reflector.getGetterType(this.property); 141 | // this.isPrimitive = this.propertyType.isPrimitive(); 142 | // this.isCharSequence = StringUtils.isCharSequence(this.propertyType); 143 | // this.fieldFill = collectionColumn.fill(); 144 | // this.withInsertFill = this.fieldFill == FieldFill.INSERT || this.fieldFill == FieldFill.INSERT_UPDATE; 145 | // this.withUpdateFill = this.fieldFill == FieldFill.UPDATE || this.fieldFill == FieldFill.INSERT_UPDATE; 146 | // this.initLogicDelete(globalConfig, field, existTableLogic); 147 | 148 | String column = collectionColumn.name(); 149 | if (StringUtils.isBlank(column)) { 150 | column = this.property; 151 | if (collectionDefinition.isUnderCamel()) { 152 | /* 开启字段下划线申明 */ 153 | column = StringUtils.camelToUnderline(column); 154 | } 155 | if (dbConfig.isCapitalMode()) { 156 | /* 开启字段全大写申明 */ 157 | column = column.toUpperCase(); 158 | } 159 | } 160 | String columnFormat = dbConfig.getColumnFormat(); 161 | if (StringUtils.isNotBlank(columnFormat)) { 162 | column = String.format(columnFormat, column); 163 | } 164 | 165 | this.name = column; 166 | 167 | // this.insertStrategy = this.chooseFieldStrategy(collectionColumn.insertStrategy(), dbConfig.getInsertStrategy()); 168 | // this.updateStrategy = this.chooseFieldStrategy(collectionColumn.updateStrategy(), dbConfig.getUpdateStrategy()); 169 | 170 | } 171 | 172 | /** 173 | * 不存在 CollectionField 注解时, 使用的构造函数 174 | * 175 | * @param globalConfig 全局配置 176 | * @param collectionDefinition collection信息 177 | * @param field 字段 178 | * @param existCollectionLogic 是否存在逻辑删除 179 | */ 180 | public ColumnDefinition(GlobalConfig globalConfig, CollectionDefinition collectionDefinition, Field field, 181 | boolean existCollectionLogic) { 182 | field.setAccessible(true); 183 | this.field = field; 184 | this.property = field.getName(); 185 | // this.propertyType = reflector.getGetterType(this.property); 186 | // this.isPrimitive = this.propertyType.isPrimitive(); 187 | // this.isCharSequence = StringUtils.isCharSequence(this.propertyType); 188 | GlobalConfig.MilvusConfig dbConfig = globalConfig.getMilvusConfig(); 189 | // this.insertStrategy = dbConfig.getInsertStrategy(); 190 | // this.updateStrategy = dbConfig.getUpdateStrategy(); 191 | // this.initLogicDelete(globalConfig, field, existCollectionLogic); 192 | 193 | String column = this.property; 194 | if (collectionDefinition.isUnderCamel()) { 195 | /* 开启字段下划线申明 */ 196 | column = StringUtils.camelToUnderline(column); 197 | } 198 | if (dbConfig.isCapitalMode()) { 199 | /* 开启字段全大写申明 */ 200 | column = column.toUpperCase(); 201 | } 202 | 203 | String columnFormat = dbConfig.getColumnFormat(); 204 | if (StringUtils.isNotBlank(columnFormat)) { 205 | column = String.format(columnFormat, column); 206 | } 207 | 208 | this.name = column; 209 | } 210 | 211 | @SuppressWarnings("unchecked") 212 | public VectorTypeHandler getVectorTypeHandler() { 213 | return (VectorTypeHandler) vectorTypeHandler; 214 | } 215 | 216 | public boolean canBePartitionKey() { 217 | return dataType == DataType.Int64 || dataType == DataType.VarChar; 218 | } 219 | 220 | public boolean vectorColumn() { 221 | return DataType.BinaryVector == dataType || DataType.FloatVector == dataType; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/metadata/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.metadata; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/ANNOYIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * ... 8 | */ 9 | @Data 10 | public class ANNOYIndexExtra implements IIndexExtra { 11 | 12 | /** 13 | * Index building parameters 14 | * The number of trees. [1, 1024] 15 | */ 16 | @SerializedName("n_trees") 17 | private Integer nTrees; 18 | 19 | /** 20 | * Search parameters 21 | * The parameters that controls the search scope. [k, inf] 22 | */ 23 | @SerializedName("search_k") 24 | private Integer searchK; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/FLATIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import io.milvus.param.MetricType; 5 | import lombok.Data; 6 | 7 | /** 8 | * ... 9 | */ 10 | @Data 11 | public class FLATIndexExtra implements IIndexExtra { 12 | 13 | @SerializedName("metric_type") 14 | private MetricType metricType; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/HNSWIIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * ... 8 | * HNSW (Hierarchical Navigable Small World Graph) is a graph-based indexing algorithm. It builds a multi-layer navigation structure for an image according to certain rules. In this structure, the upper layers are more sparse and the distances between nodes are farther; 9 | * the lower layers are denser and the distances between nodes are closer. The search starts from the uppermost layer, finds the node closest to the target in this layer, and then enters the next layer to begin another search. After multiple iterations, it can quickly approach the target position. 10 | * In order to improve performance, HNSW limits the maximum degree of nodes on each layer of the graph to M. In addition, you can use efConstruction (when building index) or ef (when searching targets) to specify a search range. 11 | */ 12 | @Data 13 | public class HNSWIIndexExtra implements IIndexExtra { 14 | 15 | /** 16 | * Index building parameters 17 | * Maximum degree of the node, range in [4, 64] 18 | */ 19 | @SerializedName("M") 20 | private Integer m; 21 | 22 | /** 23 | * Index building parameters 24 | * Search scope, range in [8, 512] 25 | */ 26 | @SerializedName("efConstruction") 27 | private Integer efConstruction; 28 | 29 | /** 30 | * Search parameters 31 | * Search scope, range in [top_k, 32768] 32 | */ 33 | @SerializedName("ef") 34 | private Integer ef; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/IIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | /** 4 | * build_index 5 | * floating 6 | */ 7 | public interface IIndexExtra { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/IVF_FLATIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * ... 8 | */ 9 | @Data 10 | public class IVF_FLATIndexExtra implements IIndexExtra { 11 | 12 | /** 13 | * Index building parameters 14 | * Number of cluster units [1, 65536] 15 | */ 16 | @SerializedName("nlist") 17 | private Integer nList; 18 | 19 | /** 20 | * Search parameters 21 | * Number of units to query CPU: [1, nlist] 22 | */ 23 | @SerializedName("nprobe") 24 | private Integer nProbe; 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/IVF_PQIndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * ... 8 | */ 9 | @Data 10 | public class IVF_PQIndexExtra implements IIndexExtra { 11 | 12 | /** 13 | * Index building parameters 14 | * Number of cluster units [1, 65536] 15 | */ 16 | @SerializedName("nlist") 17 | private Integer nList; 18 | 19 | /** 20 | * Index building parameters 21 | * Number of factors of product quantization, range in dim mod m == 0 22 | */ 23 | @SerializedName("m") 24 | private Integer m; 25 | 26 | /** 27 | * Index building parameters 28 | * [Optional] Number of bits in which each low-dimensional vector is stored. range in [1, 16] (8 by default) 29 | */ 30 | @SerializedName("nbits") 31 | private Integer nBits; 32 | 33 | /** 34 | * Search parameters 35 | * Number of units to query , range in [1, nlist] 36 | */ 37 | @SerializedName("nprobe") 38 | private Integer nProbe; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/IVF_SQ8IndexExtra.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Data; 5 | 6 | /** 7 | * ... 8 | */ 9 | @Data 10 | public class IVF_SQ8IndexExtra implements IIndexExtra { 11 | 12 | /** 13 | * Index building parameters 14 | * Number of cluster units [1, 65536] 15 | */ 16 | @SerializedName("nlist") 17 | private Integer nList; 18 | 19 | /** 20 | * Search parameters 21 | * Number of units to query CPU: [1, nlist] 22 | */ 23 | @SerializedName("nprobe") 24 | private Integer nProbe; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/Page.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | @Data 9 | public class Page { 10 | 11 | /** 12 | * 第几页 13 | */ 14 | private Long page = 0L; 15 | 16 | /** 17 | * 每页多少条数据 18 | */ 19 | private Long pageSize = 20L; 20 | 21 | /** 22 | * 数据列表 23 | */ 24 | private List instances = new ArrayList<>(); 25 | 26 | 27 | public boolean hasNext() { 28 | return !instances.isEmpty(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/model/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.model; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus; 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/record/VectorModel.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.record; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.io.Serializable; 7 | 8 | 9 | @Setter 10 | @Getter 11 | public abstract class VectorModel> implements Serializable { 12 | 13 | /** 14 | * 使用向量模糊搜索时的向量距离, 仅当使用向量相似性查找时会赋值 15 | */ 16 | private Float distance; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/record/VectorModelRepository.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.record; 2 | 3 | import io.milvus.grpc.LoadState; 4 | import plus.jdk.milvus.common.MilvusException; 5 | import plus.jdk.milvus.model.IIndexExtra; 6 | import plus.jdk.milvus.model.Page; 7 | import plus.jdk.milvus.toolkit.support.SFunction; 8 | import plus.jdk.milvus.wrapper.LambdaQueryWrapper; 9 | import plus.jdk.milvus.wrapper.LambdaSearchWrapper; 10 | 11 | import java.util.List; 12 | 13 | public interface VectorModelRepository>> { 14 | boolean insert(T vectorModel) throws MilvusException; 15 | 16 | boolean remove(Object pk) throws MilvusException; 17 | 18 | boolean batchRemove(LambdaQueryWrapper wrapper) throws MilvusException; 19 | 20 | boolean createCollection() throws MilvusException; 21 | 22 | void loadCollection() throws MilvusException; 23 | 24 | LoadState getLoadState() throws MilvusException; 25 | 26 | Long getLoadProgress() throws MilvusException; 27 | 28 | 29 | void releaseCollection() throws MilvusException; 30 | 31 | boolean createIndex(String indexName, SFunction column, IIndexExtra extraParam); 32 | 33 | boolean dropIndex(String indexName) throws MilvusException; 34 | 35 | void dropCollection() throws MilvusException; 36 | 37 | boolean hasCollection() throws MilvusException; 38 | 39 | List search(LambdaSearchWrapper wrapper) throws MilvusException; 40 | 41 | List query(LambdaQueryWrapper wrapper) throws MilvusException; 42 | 43 | Page queryPage(LambdaQueryWrapper wrapper, Long page, Long pageSize) throws MilvusException; 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/record/VectorModelRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.record; 2 | 3 | import io.milvus.grpc.LoadState; 4 | import plus.jdk.milvus.common.MilvusException; 5 | import plus.jdk.milvus.global.MilvusClientService; 6 | import plus.jdk.milvus.model.IIndexExtra; 7 | import plus.jdk.milvus.model.Page; 8 | import plus.jdk.milvus.selector.MilvusSelector; 9 | import plus.jdk.milvus.toolkit.support.SFunction; 10 | import plus.jdk.milvus.wrapper.LambdaQueryWrapper; 11 | import plus.jdk.milvus.wrapper.LambdaSearchWrapper; 12 | 13 | import java.io.Serializable; 14 | import java.lang.reflect.ParameterizedType; 15 | import java.lang.reflect.Type; 16 | import java.util.List; 17 | 18 | public abstract class VectorModelRepositoryImpl> 19 | implements VectorModelRepository, Serializable { 20 | 21 | protected final Class entityType; 22 | protected MilvusClientService milvusClientService; 23 | 24 | @SuppressWarnings("unchecked") 25 | protected VectorModelRepositoryImpl() { 26 | Type superClass = getClass().getGenericSuperclass(); 27 | if (superClass instanceof ParameterizedType) { 28 | ParameterizedType parameterizedType = (ParameterizedType) superClass; 29 | entityType = (Class) parameterizedType.getActualTypeArguments()[0]; 30 | } else { 31 | throw new IllegalArgumentException("Unable to determine the entity type."); 32 | } 33 | } 34 | 35 | public boolean insert(T vectorModel) throws MilvusException { 36 | return getMilvusClientService().insert(vectorModel); 37 | } 38 | 39 | public boolean remove(Object pk) throws MilvusException { 40 | return getMilvusClientService().remove(pk, entityType); 41 | } 42 | 43 | public boolean batchRemove(LambdaQueryWrapper wrapper) throws MilvusException { 44 | wrapper.setEntityClass(entityType); 45 | return getMilvusClientService().batchRemove(wrapper); 46 | } 47 | 48 | public boolean createCollection() throws MilvusException { 49 | return getMilvusClientService().createCollection(entityType); 50 | } 51 | 52 | public void loadCollection() throws MilvusException { 53 | getMilvusClientService().loadCollection(entityType); 54 | } 55 | 56 | public LoadState getLoadState() throws MilvusException { 57 | return getMilvusClientService().getLoadState(entityType); 58 | } 59 | 60 | public Long getLoadProgress() throws MilvusException { 61 | return getMilvusClientService().getLoadProgress(entityType); 62 | } 63 | 64 | 65 | public void releaseCollection() throws MilvusException { 66 | getMilvusClientService().releaseCollection(entityType); 67 | } 68 | 69 | public boolean createIndex(String indexName, SFunction column, IIndexExtra extraParam) throws MilvusException { 70 | return getMilvusClientService().createIndex(entityType, indexName, column, extraParam); 71 | } 72 | 73 | public boolean dropIndex(String indexName) throws MilvusException { 74 | return getMilvusClientService().dropIndex(entityType, indexName); 75 | } 76 | 77 | public void dropCollection() throws MilvusException { 78 | getMilvusClientService().dropCollection(entityType); 79 | } 80 | 81 | public boolean hasCollection() throws MilvusException { 82 | return getMilvusClientService().hasCollection(entityType); 83 | } 84 | 85 | public List search(LambdaSearchWrapper wrapper) throws MilvusException { 86 | wrapper.setEntityClass(entityType); 87 | return getMilvusClientService().search(wrapper); 88 | } 89 | 90 | public List query(LambdaQueryWrapper wrapper) throws MilvusException { 91 | wrapper.setEntityClass(entityType); 92 | return getMilvusClientService().query(wrapper); 93 | } 94 | 95 | public Page queryPage(LambdaQueryWrapper wrapper, Long page, Long pageSize) throws MilvusException { 96 | wrapper.setEntityClass(entityType); 97 | return getMilvusClientService().queryPage(wrapper, page, pageSize); 98 | } 99 | 100 | protected MilvusClientService getMilvusClientService() { 101 | if (this.milvusClientService != null) { 102 | return this.milvusClientService; 103 | } 104 | this.milvusClientService = MilvusSelector.applicationContext.getBean(MilvusClientService.class); 105 | return this.milvusClientService; 106 | } 107 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/record/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.record; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/selector/MilvusSelector.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.selector; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import plus.jdk.milvus.global.handler.UnknownTypeHandler; 9 | 10 | import javax.annotation.Nullable; 11 | 12 | @Configuration 13 | public class MilvusSelector implements ApplicationContextAware { 14 | 15 | public static ApplicationContext applicationContext; 16 | 17 | @Bean 18 | public UnknownTypeHandler defaultEmbeddingTypeHandler() { 19 | return new UnknownTypeHandler(); 20 | } 21 | 22 | @Override 23 | public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException { 24 | MilvusSelector.applicationContext = applicationContext; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/selector/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.selector; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/Assert.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * 断言类 11 | */ 12 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 13 | public final class Assert { 14 | 15 | /** 16 | * 断言这个 boolean 为 true 17 | *

为 false 则抛出异常

18 | * 19 | * @param expression boolean 值 20 | * @param message 消息 21 | * @param params 参数 22 | */ 23 | public static void isTrue(boolean expression, String message, Object... params) { 24 | if (!expression) { 25 | throw ExceptionUtils.mpe(message, params); 26 | } 27 | } 28 | 29 | /** 30 | * 断言这个 boolean 为 false 31 | *

为 true 则抛出异常

32 | * 33 | * @param expression boolean 值 34 | * @param message 消息 35 | * @param params 参数 36 | */ 37 | public static void isFalse(boolean expression, String message, Object... params) { 38 | isTrue(!expression, message, params); 39 | } 40 | 41 | /** 42 | * 断言这个 object 不为 null 43 | *

为 null 则抛异常

44 | * 45 | * @param object 对象 46 | * @param message 消息 47 | * @param params 参数 48 | */ 49 | public static void notNull(Object object, String message, Object... params) { 50 | isTrue(object != null, message, params); 51 | } 52 | 53 | /** 54 | * 断言这个 map 为 empty 55 | *

为 empty 则抛异常

56 | * 57 | * @param map 集合 58 | * @param message 消息 59 | * @param params 参数 60 | */ 61 | public static void isEmpty(Map map, String message, Object... params) { 62 | isTrue(CollectionUtils.isEmpty(map), message, params); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/ClassUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | *

11 | * ClassUtils 12 | *

13 | */ 14 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 15 | public final class ClassUtils { 16 | /** 17 | * 代理 class 的名称 18 | */ 19 | private static final List PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory" 20 | // cglib 21 | , "org.springframework.cglib.proxy.Factory" 22 | , "javassist.util.proxy.ProxyObject" 23 | // javassist 24 | , "org.apache.ibatis.javassist.util.proxy.ProxyObject"); 25 | private static ClassLoader systemClassLoader; 26 | 27 | static { 28 | try { 29 | systemClassLoader = ClassLoader.getSystemClassLoader(); 30 | } catch (SecurityException ignored) { 31 | // AccessControlException on Google App Engine 32 | } 33 | } 34 | 35 | /** 36 | * 判断传入的类型是否是布尔类型 37 | * 38 | * @param type 类型 39 | * @return 如果是原生布尔或者包装类型布尔,均返回 true 40 | */ 41 | public static boolean isBoolean(Class type) { 42 | return type == boolean.class || Boolean.class == type; 43 | } 44 | 45 | /** 46 | * 判断是否为代理对象 47 | * 48 | * @param clazz 传入 class 对象 49 | * @return 如果对象class是代理 class,返回 true 50 | */ 51 | public static boolean isProxy(Class clazz) { 52 | if (clazz != null) { 53 | for (Class cls : clazz.getInterfaces()) { 54 | if (PROXY_CLASS_NAMES.contains(cls.getName())) { 55 | return true; 56 | } 57 | } 58 | } 59 | return false; 60 | } 61 | 62 | /** 63 | *

64 | * 获取当前对象的 class 65 | *

66 | * 67 | * @param clazz 传入 68 | * @return 如果是代理的class,返回父 class,否则返回自身 69 | */ 70 | public static Class getUserClass(Class clazz) { 71 | Assert.notNull(clazz, "Class must not be null"); 72 | return isProxy(clazz) ? clazz.getSuperclass() : clazz; 73 | } 74 | 75 | private static Class loadClass(String className, ClassLoader[] classLoaders) throws ClassNotFoundException { 76 | for (ClassLoader classLoader : classLoaders) { 77 | if (classLoader != null) { 78 | try { 79 | return Class.forName(className, true, classLoader); 80 | } catch (ClassNotFoundException e) { 81 | // ignore 82 | } 83 | } 84 | } 85 | throw new ClassNotFoundException("Cannot find class: " + className); 86 | } 87 | 88 | /** 89 | *

90 | * 请仅在确定类存在的情况下调用该方法 91 | *

92 | * 93 | * @param name 类名称 94 | * @return 返回转换后的 Class 95 | */ 96 | public static Class toClassConfident(String name) { 97 | return toClassConfident(name, null); 98 | } 99 | 100 | /** 101 | * @param name 类名称 102 | * @param classLoader 类加载器 103 | * @return 返回转换后的 Class 104 | */ 105 | public static Class toClassConfident(String name, ClassLoader classLoader) { 106 | try { 107 | return loadClass(name, getClassLoaders(classLoader)); 108 | } catch (ClassNotFoundException e) { 109 | throw ExceptionUtils.mpe("找不到指定的class!请仅在明确确定会有 class 的时候,调用该方法", e); 110 | } 111 | } 112 | 113 | private static ClassLoader[] getClassLoaders(ClassLoader classLoader) { 114 | return new ClassLoader[]{ 115 | classLoader, 116 | Thread.currentThread().getContextClassLoader(), 117 | ClassUtils.class.getClassLoader(), 118 | systemClassLoader 119 | }; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/CollectionUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.*; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Collection工具类 12 | */ 13 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 14 | public class CollectionUtils { 15 | 16 | private static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); 17 | 18 | /** 19 | * 校验集合是否为空 20 | * 21 | * @param coll 入参 22 | * @return boolean 23 | */ 24 | public static boolean isEmpty(Collection coll) { 25 | return (coll == null || coll.isEmpty()); 26 | } 27 | 28 | /** 29 | * 校验集合是否不为空 30 | * 31 | * @param coll 入参 32 | * @return boolean 33 | */ 34 | public static boolean isNotEmpty(Collection coll) { 35 | return !isEmpty(coll); 36 | } 37 | 38 | /** 39 | * 判断Map是否为空 40 | * 41 | * @param map 入参 42 | * @return boolean 43 | */ 44 | public static boolean isEmpty(Map map) { 45 | return (map == null || map.isEmpty()); 46 | } 47 | 48 | /** 49 | * 判断Map是否不为空 50 | * 51 | * @param map 入参 52 | * @return boolean 53 | */ 54 | public static boolean isNotEmpty(Map map) { 55 | return !isEmpty(map); 56 | } 57 | 58 | /** 59 | * 创建默认HashMap 60 | * 61 | * @param K 62 | * @param V 63 | * @return HashMap 64 | * @see com.google.common.collect.Maps#newHashMap() 65 | * @since 3.4.0 66 | */ 67 | public static Map newHashMap() { 68 | return new HashMap<>(); 69 | } 70 | 71 | /** 72 | * 根据预期大小创建HashMap. 73 | * 74 | * @param expectedSize 预期大小 75 | * @param K 76 | * @param V 77 | * @return HashMap 78 | * @see com.google.common.collect.Maps#newHashMapWithExpectedSize 79 | * @since 3.4.0 80 | */ 81 | public static Map newHashMapWithExpectedSize(int expectedSize) { 82 | return new HashMap<>(capacity(expectedSize)); 83 | } 84 | 85 | /** 86 | * 用来过渡下Jdk1.8下ConcurrentHashMap的性能bug 87 | * ... 88 | * 89 | * @param concurrentHashMap ConcurrentHashMap 没限制类型了,非ConcurrentHashMap就别调用这方法了 90 | * @param key key 91 | * @param mappingFunction function 92 | * @param k 93 | * @param v 94 | * @return V 95 | * @since 3.4.0 96 | */ 97 | public static V computeIfAbsent(Map concurrentHashMap, K key, Function mappingFunction) { 98 | V v = concurrentHashMap.get(key); 99 | if (v != null) { 100 | return v; 101 | } 102 | return concurrentHashMap.computeIfAbsent(key, mappingFunction); 103 | } 104 | 105 | /** 106 | * Returns a capacity that is sufficient to keep the map from being resized as 107 | * long as it grows no larger than expectedSize and the load factor is >= its 108 | * default (0.75). 109 | * 110 | * @see com.google.common.collect.Maps#capacity(int) 111 | * @since 3.4.0 112 | */ 113 | private static int capacity(int expectedSize) { 114 | if (expectedSize < 3) { 115 | if (expectedSize < 0) { 116 | throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize); 117 | } 118 | return expectedSize + 1; 119 | } 120 | if (expectedSize < MAX_POWER_OF_TWO) { 121 | // This is the calculation used in JDK8 to resize when a putAll 122 | // happens; it seems to be the most conservative calculation we 123 | // can make. 0.75 is the default load factor. 124 | return (int) (expectedSize / 0.75F + 1.0F); 125 | } 126 | return Integer.MAX_VALUE; // any large value 127 | } 128 | 129 | // 提供处理Map多key取值工具方法 130 | 131 | /** 132 | * 批量取出Map中的值 133 | * 134 | * @param map map 135 | * @param keys 键的集合 136 | * @param key的泛型 137 | * @param value的泛型 138 | * @return value的泛型的集合 139 | */ 140 | public static List getCollection(Map map, Iterable keys) { 141 | List result = new ArrayList<>(); 142 | if (map != null && !map.isEmpty() && keys != null) { 143 | keys.forEach(key -> Optional.ofNullable(map.get(key)).ifPresent(result::add)); 144 | } 145 | return result; 146 | } 147 | 148 | /** 149 | * 批量取出Map中的值 150 | * 151 | * @param map map 152 | * @param keys 键的集合 153 | * @param comparator 排序器 154 | * @param key的泛型 155 | * @param value的泛型 156 | * @return value的泛型的集合 157 | */ 158 | public static List getCollection(Map map, Iterable keys, Comparator comparator) { 159 | Objects.requireNonNull(comparator); 160 | List result = getCollection(map, keys); 161 | Collections.sort(result, comparator); 162 | return result; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/Constants.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 自用常量集中管理 7 | */ 8 | public interface Constants extends StringPool, Serializable { 9 | 10 | /** 11 | * project name 12 | */ 13 | String MILVUS = "milvus"; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/ExceptionUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | 4 | import plus.jdk.milvus.common.MilvusException; 5 | 6 | /** 7 | * 异常辅助工具类 8 | */ 9 | public final class ExceptionUtils { 10 | 11 | private ExceptionUtils() { 12 | } 13 | 14 | /** 15 | * 返回一个新的异常,统一构建,方便统一处理 16 | * 17 | * @param msg 消息 18 | * @param t 异常信息 19 | * @param params 参数 20 | * @return 返回异常 21 | */ 22 | public static MilvusException mpe(String msg, Throwable t, Object... params) { 23 | return new MilvusException(String.format(msg, params), t); 24 | } 25 | 26 | /** 27 | * 重载的方法 28 | * 29 | * @param msg 消息 30 | * @param params 参数 31 | * @return 返回异常 32 | */ 33 | public static MilvusException mpe(String msg, Object... params) { 34 | return new MilvusException(String.format(msg, params)); 35 | } 36 | 37 | /** 38 | * 重载的方法 39 | * 40 | * @param t 异常 41 | * @return 返回异常 42 | */ 43 | public static MilvusException mpe(Throwable t) { 44 | return new MilvusException(t); 45 | } 46 | 47 | public static void throwMpe(boolean condition, String msg, Object... params) { 48 | if (condition) { 49 | throw mpe(msg, params); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/GenericTypeUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import plus.jdk.milvus.toolkit.reflect.IGenericTypeResolver; 6 | import plus.jdk.milvus.toolkit.reflect.SpringReflectionHelper; 7 | 8 | /** 9 | * 泛型类工具(用于隔离Spring的代码) 10 | */ 11 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 12 | public class GenericTypeUtils { 13 | private static IGenericTypeResolver genericTypeResolver; 14 | 15 | /** 16 | * 获取泛型工具助手 17 | * 18 | * @param clazz 类 19 | * @param genericIfc 泛型接口 20 | * @return 泛型工具助手 21 | */ 22 | public static Class[] resolveTypeArguments(final Class clazz, final Class genericIfc) { 23 | if (null == genericTypeResolver) { 24 | // 直接使用 spring 静态方法,减少对象创建 25 | return SpringReflectionHelper.resolveTypeArguments(clazz, genericIfc); 26 | } 27 | return genericTypeResolver.resolveTypeArguments(clazz, genericIfc); 28 | } 29 | 30 | /** 31 | * 设置泛型工具助手。如果不想使用Spring封装,可以使用前替换掉 32 | * 33 | * @param genericTypeResolver 通用类型解析器 34 | */ 35 | public static void setGenericTypeResolver(IGenericTypeResolver genericTypeResolver) { 36 | GenericTypeUtils.genericTypeResolver = genericTypeResolver; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/GlobalConfigUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import plus.jdk.milvus.config.GlobalConfig; 4 | import plus.jdk.milvus.enums.IdType; 5 | import plus.jdk.milvus.global.handler.AnnotationHandler; 6 | 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * Milvus全局缓存工具类 13 | */ 14 | public class GlobalConfigUtils { 15 | 16 | /** 17 | * 缓存全局信息 18 | */ 19 | private static final Map GLOBAL_CONFIG = new ConcurrentHashMap<>(); 20 | 21 | 22 | /** 23 | * 获取默认 MilvusGlobalConfig 24 | * 25 | * @return 默认配置 26 | */ 27 | public static GlobalConfig defaults() { 28 | return new GlobalConfig().setMilvusConfig(new GlobalConfig.MilvusConfig()); 29 | } 30 | 31 | /** 32 | * 获取MilvusGlobalConfig (统一所有入口) 33 | * 34 | * @param clazz Milvus 容器配置对象 35 | * @return 全局配置 36 | */ 37 | public static GlobalConfig getGlobalConfig(Class clazz) { 38 | Assert.notNull(clazz, "Error: You need Initialize MilvusConfiguration !"); 39 | final String key = Integer.toHexString(clazz.hashCode()); 40 | return CollectionUtils.computeIfAbsent(GLOBAL_CONFIG, key, k -> defaults()); 41 | } 42 | 43 | public static IdType getIdType(Class clazz) { 44 | return getGlobalConfig(clazz).getMilvusConfig().getIdType(); 45 | } 46 | 47 | public static GlobalConfig.MilvusConfig getDbConfig(Class clazz) { 48 | return getGlobalConfig(clazz).getMilvusConfig(); 49 | } 50 | 51 | // public static Optional getMetaObjectHandler(Class clazz) { 52 | // return Optional.ofNullable(getGlobalConfig(clazz).getMetaObjectHandler()); 53 | // } 54 | 55 | public static Optional getAnnotationHandler(Class clazz) { 56 | return Optional.ofNullable(getGlobalConfig(clazz).getAnnotationHandler()); 57 | } 58 | 59 | public static Class getSuperMapperClass(Class clazz) { 60 | return getGlobalConfig(clazz).getSuperMapperClass(); 61 | } 62 | 63 | public static boolean isSupperMapperChildren(Class clazz, Class mapperClass) { 64 | return getSuperMapperClass(clazz).isAssignableFrom(mapperClass); 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/IdWorker.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import plus.jdk.milvus.incrementer.DefaultIdentifierGenerator; 4 | import plus.jdk.milvus.incrementer.IdentifierGenerator; 5 | 6 | import java.time.LocalDateTime; 7 | import java.time.format.DateTimeFormatter; 8 | import java.util.UUID; 9 | import java.util.concurrent.ThreadLocalRandom; 10 | 11 | /** 12 | * id 获取器 13 | */ 14 | public class IdWorker { 15 | 16 | /** 17 | * 毫秒格式化时间 18 | */ 19 | public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"); 20 | /** 21 | * 主机和进程的机器码 22 | */ 23 | private static IdentifierGenerator IDENTIFIER_GENERATOR = entity -> DefaultIdentifierGenerator.getInstance().nextId(entity); 24 | 25 | /** 26 | * 获取唯一ID 27 | * 28 | * @return id 29 | */ 30 | public static long getId() { 31 | return getId(null); 32 | } 33 | 34 | /** 35 | * 获取唯一ID 36 | * 37 | * @param entity entity 38 | * @return id 39 | */ 40 | public static long getId(Object entity) { 41 | return IDENTIFIER_GENERATOR.nextId(entity).longValue(); 42 | } 43 | 44 | /** 45 | * 获取唯一ID 46 | * 47 | * @return id 48 | */ 49 | public static String getIdStr() { 50 | return getIdStr(null); 51 | } 52 | 53 | /** 54 | * 获取唯一ID 55 | * 56 | * @param entity entity 57 | * @return id 58 | */ 59 | public static String getIdStr(Object entity) { 60 | return IDENTIFIER_GENERATOR.nextId(entity).toString(); 61 | } 62 | 63 | /** 64 | * 格式化的毫秒时间 65 | * 66 | * @return 时间 67 | */ 68 | public static String getMillisecond() { 69 | return LocalDateTime.now().format(MILLISECOND); 70 | } 71 | 72 | /** 73 | * 时间 ID = Time + ID 74 | *

例如:可用于商品订单 ID

75 | * 76 | * @return 时间 ID = Time + ID 77 | */ 78 | public static String getTimeId() { 79 | return getMillisecond() + getIdStr(); 80 | } 81 | 82 | /** 83 | * 有参构造器 84 | * 85 | * @param workerId 工作机器 ID 86 | * @param dataCenterId 序列号 87 | * @see #setIdentifierGenerator(IdentifierGenerator) 88 | */ 89 | public static void initSequence(long workerId, long dataCenterId) { 90 | IDENTIFIER_GENERATOR = new DefaultIdentifierGenerator(workerId, dataCenterId); 91 | } 92 | 93 | /** 94 | * 自定义id 生成方式 95 | * 96 | * @param identifierGenerator id 生成器 97 | */ 98 | public static void setIdentifierGenerator(IdentifierGenerator identifierGenerator) { 99 | IDENTIFIER_GENERATOR = identifierGenerator; 100 | } 101 | 102 | /** 103 | * 使用ThreadLocalRandom获取UUID获取更优的效果 去掉"-" 104 | * 105 | * @return UUID去掉"-" 106 | */ 107 | public static String get32UUID() { 108 | ThreadLocalRandom random = ThreadLocalRandom.current(); 109 | return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/LambdaUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import plus.jdk.milvus.metadata.CollectionDefinition; 6 | import plus.jdk.milvus.metadata.CollectionHelper; 7 | import plus.jdk.milvus.toolkit.support.*; 8 | 9 | import java.lang.invoke.SerializedLambda; 10 | import java.lang.reflect.AccessibleObject; 11 | import java.lang.reflect.Method; 12 | import java.lang.reflect.Proxy; 13 | import java.security.AccessController; 14 | import java.util.Map; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | import static java.util.Locale.ENGLISH; 18 | 19 | /** 20 | * Lambda 解析工具类 21 | */ 22 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 23 | public final class LambdaUtils { 24 | 25 | /** 26 | * 字段映射 27 | */ 28 | private static final Map> COLUMN_CACHE_MAP = new ConcurrentHashMap<>(); 29 | 30 | /** 31 | * 该缓存可能会在任意不定的时间被清除 32 | * 33 | * @param func 需要解析的 lambda 对象 34 | * @param 类型,被调用的 Function 对象的目标类型 35 | * @return 返回解析后的结果 36 | */ 37 | public static LambdaMeta extract(SFunction func) { 38 | // 1. IDEA 调试模式下 lambda 表达式是一个代理 39 | if (func instanceof Proxy) { 40 | return new IdeaProxyLambdaMeta((Proxy) func); 41 | } 42 | // 2. 反射读取 43 | try { 44 | Method method = func.getClass().getDeclaredMethod("writeReplace"); 45 | return new ReflectLambdaMeta((SerializedLambda) setAccessible(method).invoke(func), func.getClass().getClassLoader()); 46 | } catch (Exception e) { 47 | // 3. 反射失败使用序列化的方式读取 48 | return new ShadowLambdaMeta(plus.jdk.milvus.toolkit.support.SerializedLambda.extract(func)); 49 | } 50 | } 51 | 52 | /** 53 | * 格式化 key 将传入的 key 变更为大写格式 54 | * 55 | *
 56 |      *     Assert.assertEquals("USERID", formatKey("userId"))
 57 |      * 
58 | * 59 | * @param key key 60 | * @return 大写的 key 61 | */ 62 | public static String formatKey(String key) { 63 | return key.toUpperCase(ENGLISH); 64 | } 65 | 66 | /** 67 | * 设置可访问对象的可访问权限为 true 68 | * 69 | * @param object 可访问的对象 70 | * @param 类型 71 | * @return 返回设置后的对象 72 | */ 73 | public static T setAccessible(T object) { 74 | return AccessController.doPrivileged(new SetAccessibleAction<>(object)); 75 | } 76 | 77 | /** 78 | * 将传入的表信息加入缓存 79 | * 80 | * @param collectionDefinition 表信息 81 | */ 82 | public static void installCache(CollectionDefinition collectionDefinition) { 83 | COLUMN_CACHE_MAP.put(collectionDefinition.getEntityType().getName(), createColumnCacheMap(collectionDefinition)); 84 | } 85 | 86 | /** 87 | * 缓存实体字段 MAP 信息 88 | * 89 | * @param info 表信息 90 | * @return 缓存 map 91 | */ 92 | private static Map createColumnCacheMap(CollectionDefinition info) { 93 | Map map; 94 | 95 | if (info.havePK()) { 96 | map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size() + 1); 97 | map.put(formatKey(info.getKeyProperty()), new ColumnCache(info.getKeyColumn())); 98 | } else { 99 | map = CollectionUtils.newHashMapWithExpectedSize(info.getFieldList().size()); 100 | } 101 | 102 | info.getFieldList().forEach(i -> 103 | map.put(formatKey(i.getProperty()), new ColumnCache(i.getName())) 104 | ); 105 | return map; 106 | } 107 | 108 | /** 109 | * 获取实体对应字段 MAP 110 | * 111 | * @param clazz 实体类 112 | * @return 缓存 map 113 | */ 114 | public static Map getColumnMap(Class clazz) { 115 | if (COLUMN_CACHE_MAP.containsKey(clazz.getName())) { 116 | return COLUMN_CACHE_MAP.get(clazz.getName()); 117 | } 118 | CollectionDefinition info = CollectionHelper.getCollectionInfo(clazz); 119 | if (info == null) { 120 | return null; 121 | } 122 | Map columnCacheMap = createColumnCacheMap(info); 123 | COLUMN_CACHE_MAP.put(clazz.getName(), columnCacheMap); 124 | return columnCacheMap; 125 | // return COLUMN_CACHE_MAP.computeIfAbsent(clazz.getName(), key -> { 126 | // CollectionDefinition info = CollectionHelper.getCollectionInfo(clazz); 127 | // return info == null ? null : createColumnCacheMap(info); 128 | // }); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/ReflectionKit.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | 4 | import lombok.AccessLevel; 5 | import lombok.NoArgsConstructor; 6 | import plus.jdk.milvus.common.MilvusException; 7 | 8 | import java.lang.reflect.AccessibleObject; 9 | import java.lang.reflect.Field; 10 | import java.lang.reflect.Modifier; 11 | import java.security.AccessController; 12 | import java.util.*; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.function.Function; 15 | import java.util.stream.Collectors; 16 | import java.util.stream.Stream; 17 | 18 | import static java.util.function.Function.identity; 19 | import static java.util.stream.Collectors.toMap; 20 | 21 | /** 22 | * 反射工具类,提供反射相关的快捷操作 23 | */ 24 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 25 | public final class ReflectionKit { 26 | /** 27 | * class field cache 28 | */ 29 | private static final Map, List> CLASS_FIELD_CACHE = new ConcurrentHashMap<>(); 30 | 31 | private static final Map, Class> PRIMITIVE_TYPE_TO_WRAPPER_MAP = new IdentityHashMap<>(8); 32 | 33 | static { 34 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Boolean.class, boolean.class); 35 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Byte.class, byte.class); 36 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Character.class, char.class); 37 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Double.class, double.class); 38 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Float.class, float.class); 39 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Integer.class, int.class); 40 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Long.class, long.class); 41 | PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(Short.class, short.class); 42 | } 43 | 44 | /** 45 | * 获取字段值 46 | * 47 | * @param entity 实体 48 | * @param fieldName 字段名称 49 | * @return 属性值 50 | */ 51 | public static Object getFieldValue(Object entity, String fieldName) { 52 | Class cls = entity.getClass(); 53 | Map fieldMaps = getFieldMap(cls); 54 | try { 55 | Field field = fieldMaps.get(fieldName); 56 | field.setAccessible(true); 57 | return field.get(entity); 58 | } catch (ReflectiveOperationException e) { 59 | throw new MilvusException("Error: Cannot read field in %s. Cause:" + e.getMessage()); 60 | } 61 | } 62 | 63 | /** 64 | *

65 | * 反射对象获取泛型 66 | *

67 | * 68 | * @param clazz 对象 69 | * @param genericIfc 所属泛型父类 70 | * @param index 泛型所在位置 71 | * @return Class 72 | */ 73 | public static Class getSuperClassGenericType(final Class clazz, final Class genericIfc, final int index) { 74 | Class[] typeArguments = GenericTypeUtils.resolveTypeArguments(ClassUtils.getUserClass(clazz), genericIfc); 75 | return null == typeArguments ? null : typeArguments[index]; 76 | } 77 | 78 | /** 79 | *

80 | * 获取该类的所有属性列表 81 | *

82 | * 83 | * @param clazz 反射类 84 | * @return 所有属性列表 85 | */ 86 | public static Map getFieldMap(Class clazz) { 87 | List fieldList = getFieldList(clazz); 88 | return CollectionUtils.isNotEmpty(fieldList) ? fieldList.stream().collect(Collectors.toMap(Field::getName, Function.identity())) : Collections.emptyMap(); 89 | } 90 | 91 | /** 92 | *

93 | * 获取该类的所有属性列表 94 | *

95 | * 96 | * @param clazz 反射类 97 | * @return 所有属性列表 98 | */ 99 | public static List getFieldList(Class clazz) { 100 | if (Objects.isNull(clazz)) { 101 | return Collections.emptyList(); 102 | } 103 | return CollectionUtils.computeIfAbsent(CLASS_FIELD_CACHE, clazz, k -> { 104 | Field[] fields = k.getDeclaredFields(); 105 | List superFields = new ArrayList<>(); 106 | Class currentClass = k.getSuperclass(); 107 | while (currentClass != null) { 108 | Field[] declaredFields = currentClass.getDeclaredFields(); 109 | Collections.addAll(superFields, declaredFields); 110 | currentClass = currentClass.getSuperclass(); 111 | } 112 | /* 排除重载属性 */ 113 | Map fieldMap = excludeOverrideSuperField(fields, superFields); 114 | /* 115 | * 重写父类属性过滤后处理忽略部分,支持过滤父类属性功能 116 | * 场景:中间表不需要记录创建时间,忽略父类 createTime 公共属性 117 | * 中间表实体重写父类属性 ` private transient Date createTime; ` 118 | */ 119 | return fieldMap.values().stream() 120 | /* 过滤静态属性 */ 121 | .filter(f -> !Modifier.isStatic(f.getModifiers())) 122 | /* 过滤 transient关键字修饰的属性 */ 123 | .filter(f -> !Modifier.isTransient(f.getModifiers())) 124 | .collect(Collectors.toList()); 125 | }); 126 | } 127 | 128 | /** 129 | *

130 | * 排序重置父类属性 131 | *

132 | * 133 | * @param fields 子类属性 134 | * @param superFieldList 父类属性 135 | * @return 字段映射 136 | */ 137 | public static Map excludeOverrideSuperField(Field[] fields, List superFieldList) { 138 | // 子类属性 139 | Map fieldMap = Stream.of(fields).collect(toMap(Field::getName, identity(), 140 | (u, v) -> { 141 | throw new IllegalStateException(String.format("Duplicate key %s", u)); 142 | }, 143 | LinkedHashMap::new)); 144 | superFieldList.stream().filter(field -> !fieldMap.containsKey(field.getName())) 145 | .forEach(f -> fieldMap.put(f.getName(), f)); 146 | return fieldMap; 147 | } 148 | 149 | public static Class resolvePrimitiveIfNecessary(Class clazz) { 150 | return (clazz.isPrimitive() && clazz != void.class ? PRIMITIVE_TYPE_TO_WRAPPER_MAP.get(clazz) : clazz); 151 | } 152 | 153 | /** 154 | * 设置可访问对象的可访问权限为 true 155 | * 156 | * @param object 可访问的对象 157 | * @param 类型 158 | * @return 返回设置后的对象 159 | */ 160 | public static T setAccessible(T object) { 161 | return AccessController.doPrivileged(new SetAccessibleAction<>(object)); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/SetAccessibleAction.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import java.lang.reflect.AccessibleObject; 4 | import java.security.PrivilegedAction; 5 | 6 | public class SetAccessibleAction implements PrivilegedAction { 7 | private final T obj; 8 | 9 | public SetAccessibleAction(T obj) { 10 | this.obj = obj; 11 | } 12 | 13 | @Override 14 | public T run() { 15 | obj.setAccessible(true); 16 | return obj; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/Snowflake.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.util.Random; 6 | 7 | @Slf4j 8 | public class Snowflake { 9 | private static final Random RANDOM = new Random(); 10 | private final long workerId; 11 | private long sequence = 0L; 12 | private long lastTimestamp = -1L; 13 | 14 | public Snowflake(long workerId) { 15 | if (workerId <= 1023L && workerId >= 0L) { 16 | this.workerId = workerId; 17 | } else { 18 | String message = String.format("worker Id can't be greater than %d or less than 0", 1023L); 19 | throw new IllegalArgumentException(message); 20 | } 21 | } 22 | 23 | public static Snowflake create(long workerId) { 24 | return new Snowflake(workerId); 25 | } 26 | 27 | public long[] nextId(int size) { 28 | if (size > 0 && size <= 100000) { 29 | long[] ids = new long[size]; 30 | 31 | for (int i = 0; i < size; ++i) { 32 | ids[i] = this.nextId(); 33 | } 34 | 35 | return ids; 36 | } else { 37 | String message = String.format("Size can't be greater than %d or less than 0", 100000); 38 | throw new IllegalArgumentException(message); 39 | } 40 | } 41 | 42 | public synchronized long nextId() { 43 | long timestamp = this.timeGen(); 44 | if (this.lastTimestamp == timestamp) { 45 | this.sequence = this.sequence + 1L & 4095L; 46 | if (this.sequence == 0L) { 47 | this.sequence = RANDOM.nextInt(100); 48 | timestamp = this.tilNextMillis(this.lastTimestamp); 49 | } 50 | } else { 51 | this.sequence = RANDOM.nextInt(100); 52 | } 53 | 54 | if (timestamp < this.lastTimestamp) { 55 | String message = String.format("Clock moved backwards. Refusing to generate id for %d milliseconds.", this.lastTimestamp - timestamp); 56 | log.error(message); 57 | throw new RuntimeException(message); 58 | } else { 59 | this.lastTimestamp = timestamp; 60 | return timestamp - 1483200000000L << 22 | this.workerId << 12 | this.sequence; 61 | } 62 | } 63 | 64 | private long tilNextMillis(long lastTimestamp) { 65 | long timestamp; 66 | timestamp = this.timeGen(); 67 | while (timestamp <= lastTimestamp) { 68 | timestamp = this.timeGen(); 69 | } 70 | 71 | return timestamp; 72 | } 73 | 74 | private long timeGen() { 75 | return System.currentTimeMillis(); 76 | } 77 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/StringPool.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | /** 4 | * Copy to jodd.util 5 | *

6 | * Pool of String constants to prevent repeating of 7 | * hard-coded String literals in the code. 8 | * Due to fact that these are public static final 9 | * they will be inlined by java compiler and 10 | * reference to this class will be dropped. 11 | * There is no performance gain of using this pool. 12 | * Read: https://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.5 13 | *

    14 | *
  • Literal strings within the same class in the same package represent references to the same String object.
  • 15 | *
  • Literal strings within different classes in the same package represent references to the same String object.
  • 16 | *
  • Literal strings within different classes in different packages likewise represent references to the same String object.
  • 17 | *
  • Strings computed by constant expressions are computed at compile time and then treated as if they were literals.
  • 18 | *
  • Strings computed by concatenation at run time are newly created and therefore distinct.
  • 19 | *
20 | */ 21 | public interface StringPool { 22 | 23 | String AMPERSAND = "&"; 24 | String AND = "and"; 25 | String AT = "@"; 26 | String ASTERISK = "*"; 27 | String STAR = ASTERISK; 28 | String BACK_SLASH = "\\"; 29 | String COLON = ":"; 30 | String COMMA = ","; 31 | String DASH = "-"; 32 | String DOLLAR = "$"; 33 | String DOT = "."; 34 | String DOTDOT = ".."; 35 | String DOT_CLASS = ".class"; 36 | String DOT_JAVA = ".java"; 37 | String DOT_XML = ".xml"; 38 | String EMPTY = ""; 39 | String EQUALS = "="; 40 | String FALSE = "false"; 41 | String SLASH = "/"; 42 | String HASH = "#"; 43 | String HAT = "^"; 44 | String LEFT_BRACE = "{"; 45 | String LEFT_BRACKET = "("; 46 | String LEFT_CHEV = "<"; 47 | String DOT_NEWLINE = ",\n"; 48 | String NEWLINE = "\n"; 49 | String N = "n"; 50 | String NO = "no"; 51 | String NULL = "null"; 52 | String NUM = "NUM"; 53 | String OFF = "off"; 54 | String ON = "on"; 55 | String PERCENT = "%"; 56 | String PIPE = "|"; 57 | String PLUS = "+"; 58 | String QUESTION_MARK = "?"; 59 | String EXCLAMATION_MARK = "!"; 60 | String QUOTE = "\""; 61 | String RETURN = "\r"; 62 | String TAB = "\t"; 63 | String RIGHT_BRACE = "}"; 64 | String RIGHT_BRACKET = ")"; 65 | String RIGHT_CHEV = ">"; 66 | String SEMICOLON = ";"; 67 | String SINGLE_QUOTE = "'"; 68 | String BACKTICK = "`"; 69 | String SPACE = " "; 70 | String SQL = "sql"; 71 | String TILDA = "~"; 72 | String LEFT_SQ_BRACKET = "["; 73 | String RIGHT_SQ_BRACKET = "]"; 74 | String TRUE = "true"; 75 | String UNDERSCORE = "_"; 76 | String UTF_8 = "UTF-8"; 77 | String US_ASCII = "US-ASCII"; 78 | String ISO_8859_1 = "ISO-8859-1"; 79 | String Y = "y"; 80 | String YES = "yes"; 81 | String ONE = "1"; 82 | String ZERO = "0"; 83 | String DOLLAR_LEFT_BRACE = "${"; 84 | String HASH_LEFT_BRACE = "#{"; 85 | String CRLF = "\r\n"; 86 | 87 | String HTML_NBSP = " "; 88 | String HTML_AMP = "&"; 89 | String HTML_QUOTE = """; 90 | String HTML_LT = "<"; 91 | String HTML_GT = ">"; 92 | 93 | // ---------------------------------------------------------------- array 94 | 95 | String[] EMPTY_ARRAY = new String[0]; 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/StringUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import plus.jdk.milvus.toolkit.expr.StringEscape; 6 | import plus.jdk.milvus.toolkit.support.BiIntFunction; 7 | 8 | import java.util.Collection; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import static java.util.stream.Collectors.joining; 13 | 14 | /** 15 | * String 工具类 16 | */ 17 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 18 | public final class StringUtils { 19 | 20 | /** 21 | * 字符串 is 22 | */ 23 | public static final String IS = "is"; 24 | /** 25 | * 下划线字符 26 | */ 27 | public static final char UNDERLINE = '_'; 28 | /** 29 | * 验证字符串是否是数据库字段 30 | */ 31 | private static final Pattern P_IS_COLUMN = Pattern.compile("^\\w\\S*\\w*$"); 32 | 33 | /** 34 | * 是否为大写命名 35 | */ 36 | private static final Pattern CAPITAL_MODE = Pattern.compile("^[0-9A-Z/_]+$"); 37 | 38 | /** 39 | * 字符串去除空白内容 40 | * 41 | *
  • '"<>&*+=#-; sql注入黑名单
  • \n 回车
  • \t 水平制表符
  • \s 空格
  • \r 换行
42 | */ 43 | private static final Pattern REPLACE_BLANK = Pattern.compile("'|\"|<|>|&|\\*|\\+|=|#|-|;|\\s*|\t|\r|\n"); 44 | 45 | /** 46 | * 判断字符串中是否全是空白字符 47 | * 48 | * @param cs 需要判断的字符串 49 | * @return 如果字符串序列是 null 或者全是空白,返回 true 50 | */ 51 | public static boolean isBlank(CharSequence cs) { 52 | if (cs != null) { 53 | int length = cs.length(); 54 | for (int i = 0; i < length; i++) { 55 | if (!Character.isWhitespace(cs.charAt(i))) { 56 | return false; 57 | } 58 | } 59 | } 60 | return true; 61 | } 62 | 63 | /** 64 | * 对象转为字符串去除左右空格 65 | * 66 | * @param o 带转换对象 67 | * @return 去除左右空格 68 | */ 69 | public static String toStringTrim(Object o) { 70 | return String.valueOf(o).trim(); 71 | } 72 | 73 | /** 74 | * @param cs 字符 75 | * @return !isBlank(CharSequence) 76 | * @see #isBlank(CharSequence) 77 | */ 78 | public static boolean isNotBlank(CharSequence cs) { 79 | return !isBlank(cs); 80 | } 81 | 82 | public static boolean isEmpty(CharSequence cs) { 83 | return cs == null || cs.length() == 0; 84 | } 85 | 86 | public static boolean isNotEmpty(CharSequence cs) { 87 | return !isEmpty(cs); 88 | } 89 | 90 | /** 91 | * 判断字符串是不是驼峰命名 92 | *
    93 | *
  • 包含 '_' 不算
  • 94 | *
  • 首字母大写的不算
  • 95 | *
96 | * 97 | * @param str 字符串 98 | * @return 结果 99 | */ 100 | public static boolean isCamel(String str) { 101 | return Character.isLowerCase(str.charAt(0)) && !str.contains(StringPool.UNDERSCORE); 102 | } 103 | 104 | /** 105 | * 判断字符串是否符合数据库字段的命名 106 | * 107 | * @param str 字符串 108 | * @return 判断结果 109 | */ 110 | public static boolean isNotColumnName(String str) { 111 | return !P_IS_COLUMN.matcher(str).matches(); 112 | } 113 | 114 | /** 115 | * 获取真正的字段名 116 | * 117 | * @param column 字段名 118 | * @return 字段名 119 | */ 120 | public static String getTargetColumn(String column) { 121 | if (isNotColumnName(column)) { 122 | return column.substring(1, column.length() - 1); 123 | } 124 | return column; 125 | } 126 | 127 | /** 128 | * 字符串驼峰转下划线格式 129 | * 130 | * @param param 需要转换的字符串 131 | * @return 转换好的字符串 132 | */ 133 | public static String camelToUnderline(String param) { 134 | if (isBlank(param)) { 135 | return StringPool.EMPTY; 136 | } 137 | int len = param.length(); 138 | StringBuilder sb = new StringBuilder(len); 139 | for (int i = 0; i < len; i++) { 140 | char c = param.charAt(i); 141 | if (Character.isUpperCase(c) && i > 0) { 142 | sb.append(UNDERLINE); 143 | } 144 | sb.append(Character.toLowerCase(c)); 145 | } 146 | return sb.toString(); 147 | } 148 | 149 | /** 150 | * 首字母转换小写 151 | * 152 | * @param param 需要转换的字符串 153 | * @return 转换好的字符串 154 | */ 155 | public static String firstToLowerCase(String param) { 156 | if (isBlank(param)) { 157 | return StringPool.EMPTY; 158 | } 159 | return param.substring(0, 1).toLowerCase() + param.substring(1); 160 | } 161 | 162 | /** 163 | * 正则表达式匹配 164 | * 165 | * @param regex 正则表达式字符串 166 | * @param input 要匹配的字符串 167 | * @return 如果 input 符合 regex 正则表达式格式, 返回true, 否则返回 false; 168 | */ 169 | public static boolean matches(String regex, String input) { 170 | if (null == regex || null == input) { 171 | return false; 172 | } 173 | return Pattern.matches(regex, input); 174 | } 175 | 176 | /** 177 | * 根据指定的表达式替换字符串中指定格式的部分 178 | *

179 | * BiIntFunction 中的 第二个 参数将传递 参数在字符串中的索引 180 | *

181 | * 182 | * @param src 源字符串 183 | * @param ptn 需要替换部分的正则表达式 184 | * @param replacer 替换处理器 185 | * @return 返回字符串构建起 186 | */ 187 | public static StringBuilder replace(CharSequence src, Pattern ptn, BiIntFunction replacer) { 188 | int idx = 0; 189 | int last = 0; 190 | int len = src.length(); 191 | Matcher m = ptn.matcher(src); 192 | StringBuilder sb = new StringBuilder(); 193 | 194 | // 扫描一次字符串 195 | while (m.find()) { 196 | sb.append(src, last, m.start()).append(replacer.apply(m, idx++)); 197 | last = m.end(); 198 | } 199 | // 如果表达式没有匹配或者匹配未到末尾,该判断保证字符串完整性 200 | if (last < len) { 201 | sb.append(src, last, len); 202 | } 203 | 204 | return sb; 205 | } 206 | 207 | /** 208 | * 使用单引号包含字符串 209 | * 210 | * @param obj 原字符串 211 | * @return 单引号包含的原字符串 212 | */ 213 | public static String quotaMark(Object obj) { 214 | String srcStr = String.valueOf(obj); 215 | if (obj instanceof CharSequence) { 216 | // fix #79 217 | return StringEscape.escapeString(srcStr); 218 | } 219 | return srcStr; 220 | } 221 | 222 | /** 223 | * 使用单引号包含字符串 224 | * 225 | * @param coll 集合 226 | * @return 单引号包含的原字符串的集合形式 227 | */ 228 | public static String quotaMarkList(Collection coll) { 229 | return coll.stream().map(StringUtils::quotaMark) 230 | .collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET)); 231 | } 232 | 233 | /** 234 | * 拼接字符串第二个字符串第一个字母大写 235 | * 236 | * @param concatStr 第一个字符串 237 | * @param str 第二个字符串 238 | * @return 拼接字符串第二个字符串第一个字母大写 239 | */ 240 | public static String concatCapitalize(String concatStr, final String str) { 241 | if (isBlank(concatStr)) { 242 | concatStr = StringPool.EMPTY; 243 | } 244 | if (str == null || str.isEmpty()) { 245 | return str; 246 | } 247 | 248 | final char firstChar = str.charAt(0); 249 | if (Character.isTitleCase(firstChar)) { 250 | // already capitalized 251 | return str; 252 | } 253 | 254 | return concatStr + Character.toTitleCase(firstChar) + str.substring(1); 255 | } 256 | 257 | /** 258 | * 判断对象是否不为空 259 | * 260 | * @param object ignore 261 | * @return ignore 262 | */ 263 | public static boolean checkValNotNull(Object object) { 264 | if (object instanceof CharSequence) { 265 | return isNotEmpty((CharSequence) object); 266 | } 267 | return object != null; 268 | } 269 | 270 | /** 271 | * 判断对象是否为空 272 | * 273 | * @param object ignore 274 | * @return ignore 275 | */ 276 | public static boolean checkValNull(Object object) { 277 | return !checkValNotNull(object); 278 | } 279 | 280 | /** 281 | * 包含大写字母 282 | * 283 | * @param word 待判断字符串 284 | * @return ignore 285 | */ 286 | public static boolean containsUpperCase(String word) { 287 | for (int i = 0; i < word.length(); i++) { 288 | char c = word.charAt(i); 289 | if (Character.isUpperCase(c)) { 290 | return true; 291 | } 292 | } 293 | return false; 294 | } 295 | 296 | /** 297 | * 是否为大写命名 298 | * 299 | * @param word 待判断字符串 300 | * @return ignore 301 | */ 302 | public static boolean isCapitalMode(String word) { 303 | return null != word && CAPITAL_MODE.matcher(word).matches(); 304 | } 305 | 306 | /** 307 | * 是否为驼峰下划线混合命名 308 | * 309 | * @param word 待判断字符串 310 | * @return ignore 311 | */ 312 | public static boolean isMixedMode(String word) { 313 | return matches(".*[A-Z]+.*", word) && matches(".*[/_]+.*", word); 314 | } 315 | 316 | /** 317 | * 判断是否以某个字符串结尾(区分大小写) 318 | * Check if a String ends with a specified suffix. 319 | *

320 | * nulls are handled without exceptions. Two null 321 | * references are considered to be equal. The comparison is case sensitive. 322 | *

323 | *
324 |      * StringUtils.endsWith(null, null)      = true
325 |      * StringUtils.endsWith(null, "abcdef")  = false
326 |      * StringUtils.endsWith("def", null)     = false
327 |      * StringUtils.endsWith("def", "abcdef") = true
328 |      * StringUtils.endsWith("def", "ABCDEF") = false
329 |      * 
330 | * 331 | * @param str the String to check, may be null 332 | * @param suffix the suffix to find, may be null 333 | * @return true if the String ends with the suffix, case 334 | * sensitive, or both null 335 | * @see String#endsWith(String) 336 | * @since 2.4 337 | */ 338 | public static boolean endsWith(String str, String suffix) { 339 | return endsWith(str, suffix, false); 340 | } 341 | 342 | /** 343 | * Check if a String ends with a specified suffix (optionally case 344 | * insensitive). 345 | * 346 | * @param str the String to check, may be null 347 | * @param suffix the suffix to find, may be null 348 | * @param ignoreCase inidicates whether the compare should ignore case (case 349 | * insensitive) or not. 350 | * @return true if the String starts with the prefix or both 351 | * null 352 | * @see String#endsWith(String) 353 | */ 354 | private static boolean endsWith(String str, String suffix, boolean ignoreCase) { 355 | if (str == null || suffix == null) { 356 | return (str == null && suffix == null); 357 | } 358 | if (suffix.length() > str.length()) { 359 | return false; 360 | } 361 | int strOffset = str.length() - suffix.length(); 362 | return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length()); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/SystemClock.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; 2 | 3 | import java.sql.Timestamp; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ScheduledExecutorService; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.atomic.AtomicLong; 8 | 9 | /** 10 | * 高并发场景下System.currentTimeMillis()的性能问题的优化 11 | * 12 | *

System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)

13 | *

System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道

14 | *

后台定时更新时钟,JVM退出时,线程自动回收

15 | *

10亿:43410,206,210.72815533980582%

16 | *

1亿:4699,29,162.0344827586207%

17 | *

1000万:480,12,40.0%

18 | *

100万:50,10,5.0%

19 | */ 20 | public class SystemClock { 21 | 22 | private final long period; 23 | private final AtomicLong now; 24 | 25 | private SystemClock(long period) { 26 | this.period = period; 27 | this.now = new AtomicLong(System.currentTimeMillis()); 28 | scheduleClockUpdating(); 29 | } 30 | 31 | private static SystemClock instance() { 32 | return InstanceHolder.INSTANCE; 33 | } 34 | 35 | public static long now() { 36 | return instance().currentTimeMillis(); 37 | } 38 | 39 | public static String nowDate() { 40 | return new Timestamp(instance().currentTimeMillis()).toString(); 41 | } 42 | 43 | private void scheduleClockUpdating() { 44 | ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> { 45 | Thread thread = new Thread(runnable, "System Clock"); 46 | thread.setDaemon(true); 47 | return thread; 48 | }); 49 | scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS); 50 | } 51 | 52 | private long currentTimeMillis() { 53 | return now.get(); 54 | } 55 | 56 | private static class InstanceHolder { 57 | public static final SystemClock INSTANCE = new SystemClock(1); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/expr/ExprUtils.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.expr; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import plus.jdk.milvus.enums.ExprLike; 6 | import plus.jdk.milvus.toolkit.Constants; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.regex.Matcher; 11 | import java.util.regex.Pattern; 12 | 13 | /** 14 | * ExprUtils工具类 15 | */ 16 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 17 | public abstract class ExprUtils implements Constants { 18 | 19 | private static final Pattern pattern = Pattern.compile("\\{@((\\w+?)|(\\w+?:\\w+?)|(\\w+?:\\w+?:\\w+?))}"); 20 | 21 | /** 22 | * 用%连接like 23 | * 24 | * @param str 原字符串 25 | * @param type like 类型 26 | * @return like 的值 27 | */ 28 | public static String concatLike(Object str, ExprLike type) { 29 | switch (type) { 30 | case LEFT: 31 | return PERCENT + str; 32 | case RIGHT: 33 | return str + PERCENT; 34 | default: 35 | return PERCENT + str + PERCENT; 36 | } 37 | } 38 | 39 | public static List findPlaceholder(String expr) { 40 | Matcher matcher = pattern.matcher(expr); 41 | List list = new ArrayList<>(); 42 | while (matcher.find()) { 43 | list.add(matcher.group()); 44 | } 45 | return list; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/expr/StringEscape.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.expr; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | 6 | /** 7 | * StringEscape ,数据库字符串转义 8 | */ 9 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 10 | public class StringEscape { 11 | 12 | /** 13 | * 字符串是否需要转义 14 | * 15 | * @param str ignore 16 | * @param len ignore 17 | * @return 是否需要转义 18 | */ 19 | private static boolean isEscapeNeededForString(String str, int len) { 20 | boolean needsHexEscape = false; 21 | for (int i = 0; i < len; ++i) { 22 | char c = str.charAt(i); 23 | switch (c) { 24 | /* Must be escaped for 'mysql' */ 25 | case 0: 26 | needsHexEscape = true; 27 | break; 28 | /* Must be escaped for logs */ 29 | case '\n': 30 | needsHexEscape = true; 31 | break; 32 | case '\r': 33 | needsHexEscape = true; 34 | break; 35 | case '\\': 36 | needsHexEscape = true; 37 | break; 38 | case '\'': 39 | needsHexEscape = true; 40 | break; 41 | /* Better safe than sorry */ 42 | case '"': 43 | needsHexEscape = true; 44 | break; 45 | /* This gives problems on Win32 */ 46 | case '\032': 47 | needsHexEscape = true; 48 | break; 49 | default: 50 | break; 51 | } 52 | if (needsHexEscape) { 53 | // no need to scan more 54 | break; 55 | } 56 | } 57 | return needsHexEscape; 58 | } 59 | 60 | /** 61 | * 转义字符串。纯转义,不添加单引号。 62 | * 63 | * @param escapeStr 被转义的字符串 64 | * @return 转义后的字符串 65 | */ 66 | public static String escapeRawString(String escapeStr) { 67 | int stringLength = escapeStr.length(); 68 | if (isEscapeNeededForString(escapeStr, stringLength)) { 69 | StringBuilder buf = new StringBuilder((int) (escapeStr.length() * 1.1)); 70 | // 71 | // Note: buf.append(char) is _faster_ than appending in blocks, 72 | // because the block append requires a System.arraycopy().... go 73 | // figure... 74 | // 75 | for (int i = 0; i < stringLength; ++i) { 76 | char c = escapeStr.charAt(i); 77 | switch (c) { 78 | /* Must be escaped for 'mysql' */ 79 | case 0: 80 | buf.append('\\'); 81 | buf.append('0'); 82 | 83 | break; 84 | /* Must be escaped for logs */ 85 | case '\n': 86 | buf.append('\\'); 87 | buf.append('n'); 88 | 89 | break; 90 | 91 | case '\r': 92 | buf.append('\\'); 93 | buf.append('r'); 94 | 95 | break; 96 | 97 | case '\\': 98 | buf.append('\\'); 99 | buf.append('\\'); 100 | 101 | break; 102 | 103 | case '\'': 104 | buf.append('\\'); 105 | buf.append('\''); 106 | 107 | break; 108 | /* Better safe than sorry */ 109 | case '"': 110 | buf.append('\\'); 111 | buf.append('"'); 112 | 113 | break; 114 | /* This gives problems on Win32 */ 115 | case '\032': 116 | buf.append('\\'); 117 | buf.append('Z'); 118 | break; 119 | default: 120 | buf.append(c); 121 | } 122 | } 123 | return buf.toString(); 124 | } else { 125 | return escapeStr; 126 | } 127 | } 128 | 129 | /** 130 | * 转义字符串 131 | * 132 | * @param escapeStr 被转义的字符串 133 | * @return 转义后的字符串 134 | */ 135 | public static String escapeString(String escapeStr) { 136 | if (escapeStr.matches("'(.+)'")) { 137 | escapeStr = escapeStr.substring(1, escapeStr.length() - 1); 138 | } 139 | return "'" + escapeRawString(escapeStr) + "'"; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/expr/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.expr; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/reflect/IGenericTypeResolver.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.reflect; 2 | 3 | /** 4 | * 泛型类助手(用于隔离Spring的代码) 5 | */ 6 | public interface IGenericTypeResolver { 7 | 8 | Class[] resolveTypeArguments(final Class clazz, final Class genericIfc); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/reflect/SpringReflectionHelper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.reflect; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import org.springframework.core.GenericTypeResolver; 6 | 7 | /** 8 | * Spring 反射辅助类 9 | */ 10 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 11 | public class SpringReflectionHelper { 12 | 13 | public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { 14 | return GenericTypeResolver.resolveTypeArguments(clazz, genericIfc); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/reflect/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.reflect; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/BiIntFunction.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | /** 4 | * 接受 Int 小类型的处理函数,使用小类型来避免 Java 自动装箱 5 | */ 6 | @FunctionalInterface 7 | public interface BiIntFunction { 8 | 9 | /** 10 | * 函数主接口 11 | * 12 | * @param t 被执行类型 T 13 | * @param i 参数 14 | * @return 返回 15 | */ 16 | R apply(T t, int i); 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/ColumnCache.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | public class ColumnCache implements Serializable { 11 | 12 | private static final long serialVersionUID = -4586291538088403456L; 13 | 14 | /** 15 | * 使用 column 16 | */ 17 | private String column; 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/IdeaProxyLambdaMeta.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | import plus.jdk.milvus.common.MilvusException; 4 | import plus.jdk.milvus.toolkit.LambdaUtils; 5 | 6 | import java.lang.invoke.MethodHandle; 7 | import java.lang.invoke.MethodHandles; 8 | import java.lang.reflect.Executable; 9 | import java.lang.reflect.InvocationHandler; 10 | import java.lang.reflect.Proxy; 11 | 12 | /** 13 | * 在 IDEA 的 Evaluate 中执行的 Lambda 表达式元数据需要使用该类处理元数据 14 | */ 15 | public class IdeaProxyLambdaMeta implements LambdaMeta { 16 | private final Class clazz; 17 | private final String name; 18 | 19 | public IdeaProxyLambdaMeta(Proxy func) { 20 | InvocationHandler handler = Proxy.getInvocationHandler(func); 21 | try { 22 | MethodHandle dmh = (MethodHandle) LambdaUtils.setAccessible(handler.getClass().getDeclaredField("val$target")).get(handler); 23 | Executable executable = MethodHandles.reflectAs(Executable.class, dmh); 24 | clazz = executable.getDeclaringClass(); 25 | name = executable.getName(); 26 | } catch (IllegalAccessException | NoSuchFieldException e) { 27 | throw new MilvusException(e.getMessage()); 28 | } 29 | } 30 | 31 | @Override 32 | public String getImplMethodName() { 33 | return name; 34 | } 35 | 36 | @Override 37 | public Class getInstantiatedClass() { 38 | return clazz; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return clazz.getSimpleName() + "::" + name; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/LambdaMeta.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | /** 4 | * Lambda 信息 5 | */ 6 | public interface LambdaMeta { 7 | /** 8 | * 获取 lambda 表达式实现方法的名称 9 | * 10 | * @return lambda 表达式对应的实现方法名称 11 | */ 12 | String getImplMethodName(); 13 | 14 | /** 15 | * 实例化该方法的类 16 | * 17 | * @return 返回对应的类名称 18 | */ 19 | Class getInstantiatedClass(); 20 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/ReflectLambdaMeta.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | 4 | import lombok.extern.slf4j.Slf4j; 5 | import plus.jdk.milvus.toolkit.ClassUtils; 6 | import plus.jdk.milvus.toolkit.StringPool; 7 | 8 | import java.lang.invoke.SerializedLambda; 9 | 10 | @Slf4j 11 | public class ReflectLambdaMeta implements LambdaMeta { 12 | 13 | private final SerializedLambda lambda; 14 | 15 | private final ClassLoader classLoader; 16 | 17 | public ReflectLambdaMeta(SerializedLambda lambda, ClassLoader classLoader) { 18 | this.lambda = lambda; 19 | this.classLoader = classLoader; 20 | } 21 | 22 | @Override 23 | public String getImplMethodName() { 24 | return lambda.getImplMethodName(); 25 | } 26 | 27 | @Override 28 | public Class getInstantiatedClass() { 29 | String instantiatedMethodType = lambda.getInstantiatedMethodType(); 30 | String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)).replace(StringPool.SLASH, StringPool.DOT); 31 | return ClassUtils.toClassConfident(instantiatedType, this.classLoader); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/SFunction.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | import java.io.Serializable; 4 | import java.util.function.Function; 5 | 6 | @FunctionalInterface 7 | public interface SFunction extends Function, Serializable { 8 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/SerializedLambda.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | 4 | import plus.jdk.milvus.common.MilvusException; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * 当前类是 {@link java.lang.invoke.SerializedLambda } 的一个镜像 10 | */ 11 | @SuppressWarnings("ALL") 12 | public class SerializedLambda implements Serializable { 13 | private static final long serialVersionUID = 8025925345765570181L; 14 | 15 | private Class capturingClass; 16 | private String implMethodName; 17 | private String instantiatedMethodType; 18 | 19 | public static SerializedLambda extract(Serializable serializable) { 20 | try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 21 | ObjectOutputStream oos = new ObjectOutputStream(baos)) { 22 | oos.writeObject(serializable); 23 | oos.flush(); 24 | try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) { 25 | @Override 26 | protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { 27 | Class clazz = super.resolveClass(desc); 28 | return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz; 29 | } 30 | 31 | }) { 32 | return (SerializedLambda) ois.readObject(); 33 | } 34 | } catch (IOException | ClassNotFoundException e) { 35 | throw new MilvusException(e); 36 | } 37 | } 38 | 39 | public String getInstantiatedMethodType() { 40 | return instantiatedMethodType; 41 | } 42 | 43 | public Class getCapturingClass() { 44 | return capturingClass; 45 | } 46 | 47 | public String getImplMethodName() { 48 | return implMethodName; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/ShadowLambdaMeta.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; 2 | 3 | import plus.jdk.milvus.toolkit.ClassUtils; 4 | import plus.jdk.milvus.toolkit.StringPool; 5 | 6 | public class ShadowLambdaMeta implements LambdaMeta { 7 | private final SerializedLambda lambda; 8 | 9 | public ShadowLambdaMeta(SerializedLambda lambda) { 10 | this.lambda = lambda; 11 | } 12 | 13 | @Override 14 | public String getImplMethodName() { 15 | return lambda.getImplMethodName(); 16 | } 17 | 18 | @Override 19 | public Class getInstantiatedClass() { 20 | String instantiatedMethodType = lambda.getInstantiatedMethodType(); 21 | String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(StringPool.SEMICOLON)).replace(StringPool.SLASH, StringPool.DOT); 22 | return ClassUtils.toClassConfident(instantiatedType, lambda.getCapturingClass().getClassLoader()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/toolkit/support/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.toolkit.support; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/utils/Converter.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Converter { 7 | public static List objectToList(Object object, Class clazz) { 8 | List result = new ArrayList<>(); 9 | if (object instanceof List) { 10 | for (Object o : (List) object) { 11 | result.add(clazz.cast(o)); 12 | } 13 | } 14 | return result; 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/utils/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.utils; -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/wrapper/LambdaQueryWrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.wrapper; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.experimental.Accessors; 6 | import plus.jdk.milvus.conditions.AbstractLambdaWrapper; 7 | import plus.jdk.milvus.conditions.SharedString; 8 | import plus.jdk.milvus.conditions.query.Query; 9 | import plus.jdk.milvus.conditions.segments.MergeSegments; 10 | import plus.jdk.milvus.record.VectorModel; 11 | import plus.jdk.milvus.toolkit.support.SFunction; 12 | 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | 15 | /** 16 | * Lambda 语法使用 Wrapper 17 | */ 18 | public class LambdaQueryWrapper>> extends AbstractLambdaWrapper> 19 | implements Query, T, SFunction> { 20 | 21 | @Getter 22 | @Setter 23 | @Accessors(chain = true) 24 | private Long offset; 25 | 26 | @Getter 27 | @Setter 28 | @Accessors(chain = true) 29 | private Long limit = 10L; 30 | 31 | /** 32 | * 查询字段 33 | */ 34 | private SharedString exprSelect = new SharedString(); 35 | 36 | public LambdaQueryWrapper() { 37 | this((T) null); 38 | } 39 | 40 | public LambdaQueryWrapper(T entity) { 41 | super.setEntity(entity); 42 | super.initNeed(); 43 | } 44 | 45 | public LambdaQueryWrapper(Class entityClass) { 46 | super.setEntityClass(entityClass); 47 | super.initNeed(); 48 | } 49 | 50 | public LambdaQueryWrapper(T entity, Class entityClass, SharedString exprSelect, AtomicInteger paramNameSeq, MergeSegments mergeSegments, 51 | Long offset, Long limit 52 | ) { 53 | super.setEntity(entity); 54 | super.setEntityClass(entityClass); 55 | this.paramNameSeq = paramNameSeq; 56 | this.expression = mergeSegments; 57 | this.exprSelect = exprSelect; 58 | this.offset = offset; 59 | this.limit = limit; 60 | } 61 | 62 | @Override 63 | public String getExprSelect() { 64 | return exprSelect.getStringValue(); 65 | } 66 | 67 | /** 68 | * 用于生成嵌套 Expr 69 | *

故 ExprSelect 不向下传递

70 | */ 71 | @Override 72 | protected LambdaQueryWrapper instance() { 73 | return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, 74 | new MergeSegments(), offset, limit); 75 | } 76 | 77 | @Override 78 | public void clear() { 79 | super.clear(); 80 | exprSelect.toNull(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/wrapper/LambdaSearchWrapper.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.wrapper; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.experimental.Accessors; 6 | import plus.jdk.milvus.conditions.AbstractLambdaWrapper; 7 | import plus.jdk.milvus.conditions.SharedString; 8 | import plus.jdk.milvus.conditions.search.Search; 9 | import plus.jdk.milvus.conditions.segments.MergeSegments; 10 | import plus.jdk.milvus.model.IIndexExtra; 11 | import plus.jdk.milvus.record.VectorModel; 12 | import plus.jdk.milvus.toolkit.support.SFunction; 13 | 14 | import java.io.Serializable; 15 | import java.util.List; 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | /** 19 | * Lambda 更新封装 20 | */ 21 | public class LambdaSearchWrapper>> extends AbstractLambdaWrapper> 22 | implements Search, T, SFunction>, Serializable { 23 | 24 | private static final long serialVersionUID = -1L; 25 | 26 | /** 27 | * 额外的索引查询参数 28 | * Search parameter(s) specific to the index. 29 | * See Vector Index for more information. 30 | */ 31 | @Getter 32 | @Setter 33 | @Accessors(chain = true) 34 | private IIndexExtra extra; 35 | 36 | /** 37 | * 查询最相似的多少条数据 38 | * Number of the most similar results to return. 39 | */ 40 | @Getter 41 | @Setter 42 | @Accessors(chain = true) 43 | private Integer topK = 10; 44 | 45 | 46 | /** 47 | * 指定要检索的向量列 48 | */ 49 | @Getter 50 | @Setter 51 | @Accessors(chain = true) 52 | private SFunction vectorColumn; 53 | 54 | /** 55 | * 指定输入向量 56 | */ 57 | @Getter 58 | @Setter 59 | @Accessors(chain = true) 60 | private transient List vectorValue; 61 | 62 | private SharedString exprSelect = new SharedString(); 63 | 64 | public LambdaSearchWrapper() { 65 | this((T) null); 66 | } 67 | 68 | public LambdaSearchWrapper(T entity) { 69 | super.setEntity(entity); 70 | super.initNeed(); 71 | } 72 | 73 | public LambdaSearchWrapper(Class entityClass) { 74 | super.setEntityClass(entityClass); 75 | super.initNeed(); 76 | } 77 | 78 | public LambdaSearchWrapper(T entity, Class entityClass, SharedString exprSelect, AtomicInteger paramNameSeq, 79 | MergeSegments mergeSegments, IIndexExtra extra, Integer topK, SFunction vectorColumn, List vectorValue) { 80 | super.setEntity(entity); 81 | super.setEntityClass(entityClass); 82 | this.exprSelect = exprSelect; 83 | this.paramNameSeq = paramNameSeq; 84 | this.expression = mergeSegments; 85 | this.extra = extra; 86 | this.topK = topK; 87 | this.vectorColumn = vectorColumn; 88 | this.vectorValue = vectorValue; 89 | } 90 | 91 | public LambdaSearchWrapper vector(SFunction column, R value) { 92 | this.vectorColumn = column; 93 | this.vectorValue = (List) value; 94 | return this; 95 | } 96 | 97 | @Override 98 | public String getExprSelect() { 99 | return exprSelect.getStringValue(); 100 | } 101 | 102 | @Override 103 | protected LambdaSearchWrapper instance() { 104 | return new LambdaSearchWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, 105 | new MergeSegments(), extra, topK, vectorColumn, vectorValue); 106 | } 107 | 108 | @Override 109 | public void clear() { 110 | super.clear(); 111 | expression.clear(); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/plus/jdk/milvus/wrapper/package-info.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.wrapper; -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | plus.jdk.milvus.autoconfigure.MilvusPlusAutoConfiguration,\ 3 | plus.jdk.milvus.autoconfigure.IdentifierGeneratorAutoConfiguration -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/MilvusTestApplication.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus; 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication; 4 | 5 | @SpringBootApplication 6 | public class MilvusTestApplication { 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/UserBlogVectorServiceTest.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Order; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import plus.jdk.milvus.collection.UserBlogVector; 10 | import plus.jdk.milvus.common.MilvusException; 11 | import plus.jdk.milvus.common.chat.ChatClient; 12 | import plus.jdk.milvus.dao.UserBlogVectorDao; 13 | import plus.jdk.milvus.model.HNSWIIndexExtra; 14 | import plus.jdk.milvus.wrapper.LambdaQueryWrapper; 15 | import plus.jdk.milvus.wrapper.LambdaSearchWrapper; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | 23 | @Slf4j 24 | @SpringBootTest 25 | class UserBlogVectorServiceTest { 26 | 27 | @Autowired 28 | private UserBlogVectorDao userBlogVectorDao; 29 | 30 | @Autowired 31 | private ChatClient chatClient; 32 | 33 | 34 | /** 35 | * 创建集合和索引 36 | */ 37 | void createCollection() throws MilvusException { 38 | boolean ret = userBlogVectorDao.createCollection(); 39 | HNSWIIndexExtra extra = new HNSWIIndexExtra(); 40 | extra.setM(16); 41 | extra.setEfConstruction(8); 42 | userBlogVectorDao.createIndex("idx_blog_vector", 43 | UserBlogVector::getBlogTextVector, extra); 44 | userBlogVectorDao.loadCollection(); 45 | } 46 | 47 | 48 | /** 49 | * 向集合插入记录 50 | */ 51 | @Test 52 | @Order(2) 53 | void insertVector() throws MilvusException { 54 | if (!userBlogVectorDao.hasCollection()) { 55 | createCollection(); 56 | } 57 | String text = "宝贝们!!没睡吧啊啊啊 刚出炉的九图 投票!喜欢图几"; 58 | Long uid = 2656274875L; 59 | UserBlogVector userBlogVector = new UserBlogVector(); 60 | userBlogVector.setBlogText(text); 61 | userBlogVector.setUid(uid); 62 | userBlogVector.setBlogType(new ArrayList() {{ 63 | addAll(Arrays.asList("1", "2")); 64 | }}); 65 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 66 | userBlogVector.setBlogTextVector(embedding.get(0)); 67 | boolean ret = userBlogVectorDao.insert(userBlogVector); 68 | log.info("{}", ret); 69 | } 70 | 71 | /** 72 | * 使用其他字段查找相关内容 73 | */ 74 | @Test 75 | @Order(3) 76 | void query() throws MilvusException { 77 | if (!userBlogVectorDao.hasCollection()) { 78 | createCollection(); 79 | } 80 | LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); 81 | wrapper.eq(UserBlogVector::getUid, 2656274875L) 82 | .or() 83 | .ne(UserBlogVector::getUid, 1234567890L) 84 | .or(jsonWrapper -> 85 | jsonWrapper 86 | .jsonContains(UserBlogVector::getBlogType, 1, "type") 87 | .jsonContainsAll(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type") 88 | .or() 89 | .jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("112", "312"), "tasd") 90 | ); 91 | Assertions.assertEquals("(uid == 2656274875 or uid != 1234567890 or (json_contains (blog_type['type'], 1) and json_contains_all (blog_type['type'], ['1','2']) or json_contains_any (blog_type['tasd'], ['112','312'])))", wrapper.getExprSegment(), ""); 92 | List queryResults = userBlogVectorDao.query(wrapper); 93 | log.info("{}", wrapper.getExprSegment()); 94 | } 95 | 96 | /** 97 | * 使用向量查找相似度最高的内容。可以结合其他字段做条件查询过滤 98 | */ 99 | @Order(3) 100 | void search() throws MilvusException { 101 | String text = "宝贝们!!没睡吧啊啊啊 刚出炉的九图 投票!喜欢图几"; 102 | LambdaSearchWrapper wrapper = new LambdaSearchWrapper<>(); 103 | List> embedding = chatClient.getEmbedding(Collections.singletonList(text)); 104 | wrapper.vector(UserBlogVector::getBlogTextVector, embedding.get(0)) 105 | .setTopK(10) 106 | .eq(UserBlogVector::getUid, 2656274875L); 107 | wrapper.jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type"); 108 | if (!userBlogVectorDao.hasCollection()) { 109 | createCollection(); 110 | } 111 | List searchResults = userBlogVectorDao.search(wrapper); 112 | log.info("{}", searchResults); 113 | } 114 | 115 | 116 | /** 117 | * 使用主键删除记录 118 | * = 119 | */ 120 | @Test 121 | @Order(4) 122 | void deleteRecord() throws MilvusException { 123 | if (!userBlogVectorDao.hasCollection()) { 124 | createCollection(); 125 | } 126 | boolean ret = userBlogVectorDao.remove(12345556); 127 | log.info("{}", ret); 128 | } 129 | 130 | @Test 131 | @Order(999) 132 | void deleteCollection() throws MilvusException { 133 | if (!userBlogVectorDao.hasCollection()) { 134 | createCollection(); 135 | } 136 | userBlogVectorDao.dropCollection(); 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/collection/UserBlogVector.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.collection; 2 | 3 | import io.milvus.grpc.DataType; 4 | import io.milvus.param.MetricType; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import plus.jdk.milvus.annotation.VectorCollectionColumn; 8 | import plus.jdk.milvus.annotation.VectorCollectionName; 9 | import plus.jdk.milvus.record.VectorModel; 10 | 11 | import java.util.List; 12 | 13 | @Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @VectorCollectionName(name = "user_blog2", description = "用户博文向量表") 16 | public class UserBlogVector extends VectorModel { 17 | 18 | /** 19 | * 主键 20 | */ 21 | @VectorCollectionColumn(name = "id", dataType = DataType.Int64, primary = true) 22 | private Long id; 23 | 24 | /** 25 | * uid 26 | */ 27 | @VectorCollectionColumn(name = "uid", dataType = DataType.Int64) 28 | private Long uid; 29 | 30 | /** 31 | * uid 32 | */ 33 | @VectorCollectionColumn(name = "user_id", dataType = DataType.Int64) 34 | private Long userId; 35 | 36 | /** 37 | * 博文文本 38 | */ 39 | @VectorCollectionColumn(name = "blog_text", dataType = DataType.VarChar, maxLength = 1024) 40 | private String blogText; 41 | 42 | /** 43 | * 博文分类 44 | */ 45 | @VectorCollectionColumn(name = "blog_type", dataType = DataType.Array, elementType = DataType.VarChar) 46 | private List blogType; 47 | 48 | /** 49 | * 博文文本向量, 此处的博文文本向量使用m3e embedding, 所以是768 50 | */ 51 | @VectorCollectionColumn(name = "v_blog_text", dataType = DataType.FloatVector, vectorDimension = 768, metricType = MetricType.COSINE) 52 | private List blogTextVector; 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/common/chat/ChatClient.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common.chat; 2 | 3 | import java.util.List; 4 | 5 | public interface ChatClient { 6 | List> getEmbedding(List texts); 7 | } 8 | -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/common/chat/DefaultChatClient.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common.chat; 2 | 3 | import com.azure.core.http.HttpClient; 4 | import com.azure.core.http.HttpMethod; 5 | import com.azure.core.http.HttpRequest; 6 | import com.azure.core.http.HttpResponse; 7 | import com.azure.core.util.serializer.TypeReference; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.time.Duration; 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | import static com.alibaba.fastjson.JSON.toJSONString; 18 | import static com.azure.core.http.HttpHeaderName.CONTENT_TYPE; 19 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 20 | 21 | @Component 22 | @RequiredArgsConstructor 23 | public class DefaultChatClient implements ChatClient { 24 | 25 | private final HttpClient httpClient; 26 | 27 | @Override 28 | public List> getEmbedding(List texts) { 29 | Map> request = new HashMap<>(); 30 | request.put("sentences", texts); 31 | String DEFAULT_EMBEDDING_URL = "http://192.168.1.193:8084/m3e_encode"; 32 | HttpRequest httpRequest = new HttpRequest(HttpMethod.POST, DEFAULT_EMBEDDING_URL); 33 | httpRequest.setBody(toJSONString(request)) 34 | .setHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE); 35 | HttpResponse block = httpClient.send(httpRequest).retry(5).block(Duration.ofSeconds(20)); 36 | if (block == null) return null; 37 | List> embeddings = block.getBodyAsBinaryData().toObject(new TypeReference>>() { 38 | }); 39 | block.close(); 40 | if (embeddings == null) return new ArrayList<>(); 41 | return embeddings; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/common/config/HttpClientConfigurer.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.common.config; 2 | 3 | import com.azure.core.http.HttpClient; 4 | import com.azure.core.http.netty.NettyAsyncHttpClientProvider; 5 | import com.azure.core.util.HttpClientOptions; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | import static java.time.Duration.ZERO; 10 | import static java.time.Duration.ofSeconds; 11 | 12 | @Configuration 13 | public class HttpClientConfigurer { 14 | 15 | @Bean 16 | public HttpClient httpClient() { 17 | return HttpClient.createDefault(new HttpClientOptions() 18 | .setConnectTimeout(ofSeconds(5)) 19 | .setConnectionIdleTimeout(ZERO) 20 | .setMaximumConnectionPoolSize(128) 21 | .setHttpClientProvider(NettyAsyncHttpClientProvider.class) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/dao/UserBlogVectorDao.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.dao; 2 | 3 | import plus.jdk.milvus.annotation.VectorRepository; 4 | import plus.jdk.milvus.collection.UserBlogVector; 5 | import plus.jdk.milvus.record.VectorModelRepositoryImpl; 6 | 7 | @VectorRepository 8 | public class UserBlogVectorDao extends VectorModelRepositoryImpl { 9 | } -------------------------------------------------------------------------------- /src/test/java/plus/jdk/milvus/wrapper/LambdaQueryWrapperTest.java: -------------------------------------------------------------------------------- 1 | package plus.jdk.milvus.wrapper; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | import plus.jdk.milvus.collection.UserBlogVector; 6 | 7 | import java.util.Arrays; 8 | 9 | @SpringBootTest 10 | class LambdaQueryWrapperTest { 11 | 12 | @Test 13 | void test_json_wrapper() { 14 | LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); 15 | wrapper.eq(UserBlogVector::getUserId, 2656274875L) 16 | .or() 17 | .ne(UserBlogVector::getUserId, 1234567890L) 18 | //下面json查询,运行时提示递归更新异常? 19 | .or(jsonWrapper -> 20 | jsonWrapper 21 | .jsonContains(UserBlogVector::getBlogType, 1, "type") 22 | .jsonContainsAll(UserBlogVector::getBlogType, Arrays.asList("1", "2"), "type") 23 | .or() 24 | .jsonContainsAny(UserBlogVector::getBlogType, Arrays.asList("112", "312"), "tasd") 25 | ); 26 | System.out.println(wrapper.getExprSegment()); 27 | } 28 | } -------------------------------------------------------------------------------- /src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | plus: 2 | jdk: 3 | milvus: 4 | enabled: true 5 | host: 127.0.0.1 6 | port: 19530 7 | connect-timeout: 1000 8 | idle-timeout: 5000 --------------------------------------------------------------------------------