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 extends IExprSegment> 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 super K, ? extends V> 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 | * null
s 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
--------------------------------------------------------------------------------