From fd9be066f9d63796a41fbece447ba8707f2b6607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=91=E5=8B=87?= <1218639030@qq.com> Date: Sun, 28 Sep 2025 21:23:26 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(Converter):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=AD=97=E6=AE=B5=E6=98=A0=E5=B0=84=E4=B8=8E?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 MilvusConverter 中添加对动态字段的处理逻辑(目的为了query()无outputFields能获取到$meta) - 在 SearchRespConverter 中增加对 $meta 字段的解析 --- .../dromara/milvus/plus/converter/MilvusConverter.java | 3 +++ .../milvus/plus/converter/SearchRespConverter.java | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java index f1df017..d05f562 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java @@ -94,6 +94,9 @@ public class MilvusConverter { for (Field field : fields) { MilvusField fieldAnnotation = field.getAnnotation(MilvusField.class); if (Objects.isNull(fieldAnnotation)) { + if (collectionAnnotation.enableDynamicField()) { + propertyCache.functionToPropertyMap.put("$meta", "$meta"); + } continue; } // 处理字段名,优先使用注解中的字段名,若无则用反射获取的字段名 diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java index dc4fcfc..0cb51f6 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java @@ -41,7 +41,13 @@ public class SearchRespConverter { Map entityMap = new HashMap<>(); for (Map.Entry entry : searchResult.getEntity().entrySet()) { String key = propertyCache.findKeyByValue(entry.getKey()); - if(key!=null){ + if (conversionCache.getMilvusEntity().getEnableDynamicField() + && "$meta".equals(entry.getKey())) { + if (entry.getValue() == null) { + continue; + } + entityMap.putAll(GsonUtil.fromJsonToMap(entry.getValue().toString())); + } else if(key!=null){ Object value = entry.getValue(); entityMap.put(key,value); } From 69eb4cc0c9eb9aa46bd614a9a37835ee44b0d123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=91=E5=8B=87?= <1218639030@qq.com> Date: Mon, 29 Sep 2025 21:18:22 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E6=9F=A5=E8=AF=A2=E4=B8=8E=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 outputMetaFields 字段用于存储动态字段输出字段 - 修改 SearchRespConverter 中的转换方法,支持动态字段过滤与映射 - 更新 LambdaQueryWrapper 的 query 方法,处理动态字段输出逻辑 - 扩展 PropertyCache 缓存结构,增加动态字段相关集合与映射 - 优化 MilvusConverter 初始化逻辑,注册动态字段信息 --- .../milvus/plus/cache/PropertyCache.java | 6 ++++ .../plus/converter/MilvusConverter.java | 2 ++ .../plus/converter/SearchRespConverter.java | 24 +++++++++----- .../core/conditions/LambdaQueryWrapper.java | 33 +++++++++++++++---- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/cache/PropertyCache.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/cache/PropertyCache.java index c319d24..7ecd94a 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/cache/PropertyCache.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/cache/PropertyCache.java @@ -1,7 +1,10 @@ package org.dromara.milvus.plus.cache; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; + /** * @author xgc **/ @@ -13,6 +16,9 @@ public class PropertyCache { public Map methodToPropertyMap = new HashMap<>(); //属性get方法名称->集合属性名称 + public Set metaFunctionSet = new HashSet<>(); //动态字段属性名称 + + public Map metaMethodMap = new HashMap<>(); //属性get方法名称 // 根据值查找第一个匹配的键 public String findKeyByValue(String value) { diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java index d05f562..2c0e917 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/MilvusConverter.java @@ -96,6 +96,8 @@ public class MilvusConverter { if (Objects.isNull(fieldAnnotation)) { if (collectionAnnotation.enableDynamicField()) { propertyCache.functionToPropertyMap.put("$meta", "$meta"); + propertyCache.metaFunctionSet.add(field.getName()); + propertyCache.metaMethodMap.put(getGetMethodName(field), field.getName()); } continue; } diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java index 0cb51f6..7cc2874 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java @@ -27,7 +27,7 @@ public class SearchRespConverter { * @param entityType 指定的Java实体类类型,用于将搜索结果的每个实体转换为该类型。 * @return 转换后的MilvusResp对象,其中包含了列表形式的搜索结果以及操作是否成功的标志。 */ - public static MilvusResp>> convertSearchRespToMilvusResp(SearchResp searchResp, Class entityType) { + public static MilvusResp>> convertSearchRespToMilvusResp(SearchResp searchResp, Class entityType, List outputMetaFields) { // 从缓存中获取对应实体类型的转换缓存和属性缓存 ConversionCache conversionCache = MilvusCache.milvusCache.get(entityType.getName()); PropertyCache propertyCache = conversionCache.getPropertyCache(); @@ -46,7 +46,11 @@ public class SearchRespConverter { if (entry.getValue() == null) { continue; } - entityMap.putAll(GsonUtil.fromJsonToMap(entry.getValue().toString())); + Map metaMap = GsonUtil.fromJsonToMap(entry.getValue().toString()); + //metaFunctionSet 类型匹配,并完成 map 放入 entityMap 的操作 + metaMap.entrySet().stream() + .filter(mapEntry -> propertyCache.metaFunctionSet.contains(String.valueOf(mapEntry.getKey())) && outputMetaFields.contains(mapEntry.getKey())) + .forEach(mapEntry -> entityMap.put(String.valueOf(mapEntry.getKey()), mapEntry.getValue())); } else if(key!=null){ Object value = entry.getValue(); entityMap.put(key,value); @@ -75,10 +79,10 @@ public class SearchRespConverter { * @param entityType 实体类型,用于泛型结果的类型转换。 * @return 返回一个包含Milvus结果列表的MilvusResp对象。 */ - public static MilvusResp>> convertGetRespToMilvusResp(QueryResp getResp, Class entityType) { + public static MilvusResp>> convertGetRespToMilvusResp(QueryResp getResp, Class entityType, List outputMetaFields) { // 从QueryResp中提取查询结果 List queryResults = getResp.getQueryResults(); - return convertQuery(queryResults, entityType); + return convertQuery(queryResults, entityType, outputMetaFields); } public static MilvusResp convertGetRespToCount(QueryResp getResp) { // 从QueryResp中提取查询结果 @@ -92,10 +96,10 @@ public class SearchRespConverter { * @param entityType 实体类型,用于泛型结果的类型转换。 * @return 返回一个包含Milvus结果列表的MilvusResp对象。 */ - public static MilvusResp>> convertGetRespToMilvusResp(GetResp getResp, Class entityType) { + public static MilvusResp>> convertGetRespToMilvusResp(GetResp getResp, Class entityType, List outputMetaFields) { // 从GetResp中提取结果 List getResults = getResp.getResults; - return convertQuery(getResults, entityType); + return convertQuery(getResults, entityType, outputMetaFields); } @@ -106,7 +110,7 @@ public class SearchRespConverter { * @param entityType 需要转换成的实体类型,指定了转换的目标。 * @return MilvusResp对象,包含转换后的实体列表。每个实体都包装在一个MilvusResult对象中,同时设置成功状态为true。 */ - private static MilvusResp>> convertQuery(List getResults, Class entityType){ + private static MilvusResp>> convertQuery(List getResults, Class entityType, List outputFields){ // 初始化转换缓存和属性缓存,用于帮助将查询结果映射到Java实体 ConversionCache conversionCache = MilvusCache.milvusCache.get(entityType.getName()); PropertyCache propertyCache = conversionCache.getPropertyCache(); @@ -119,7 +123,11 @@ public class SearchRespConverter { // 通过属性缓存转换键名,以适应Java实体的字段命名 for (Map.Entry entry : entityMap.entrySet()) { String key = propertyCache.findKeyByValue(entry.getKey()); - if(key!=null){ + if (conversionCache.getMilvusEntity().getEnableDynamicField() + && propertyCache.metaFunctionSet.contains(entry.getKey()) + && outputFields.contains(entry.getKey())) { + entityMap2.put(String.valueOf(entry.getKey()), entry.getValue()); + } else if(key!=null){ Object value = entry.getValue(); entityMap2.put(key,value); } diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java index 2999fe9..23222b0 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java @@ -36,6 +36,7 @@ import java.util.stream.Collectors; public class LambdaQueryWrapper extends AbstractChainWrapper implements Wrapper, T> { private ConversionCache conversionCache; private List outputFields; + private List outputMetaFields; private Class entityType; private String collectionName; private String collectionAlias; @@ -977,18 +978,18 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr HybridSearchReq hybridSearchReq = buildHybrid(); log.info("Build HybridSearch Param--> {}", GsonUtil.toJson(hybridSearchReq)); SearchResp searchResp = client.hybridSearch(hybridSearchReq); - return SearchRespConverter.convertSearchRespToMilvusResp(searchResp, entityType); + return SearchRespConverter.convertSearchRespToMilvusResp(searchResp, entityType, outputMetaFields); } if (!vectors.isEmpty()) { SearchReq searchReq = buildSearch(); log.info("Build Search Param--> {}", GsonUtil.toJson(searchReq)); SearchResp searchResp = client.search(searchReq); - return SearchRespConverter.convertSearchRespToMilvusResp(searchResp, entityType); + return SearchRespConverter.convertSearchRespToMilvusResp(searchResp, entityType, outputMetaFields); } else { QueryReq queryReq = buildQuery(); log.info("Build Query param--> {}", GsonUtil.toJson(queryReq)); QueryResp queryResp = client.query(queryReq); - return SearchRespConverter.convertGetRespToMilvusResp(queryResp, entityType); + return SearchRespConverter.convertGetRespToMilvusResp(queryResp, entityType, outputMetaFields); } }, "collection not loaded", @@ -1002,7 +1003,15 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr public MilvusResp>> query(FieldFunction... outputFields) throws MilvusException { List otf = new ArrayList<>(); for (FieldFunction outputField : outputFields) { - otf.add(outputField.getFieldName(outputField)); + String methodName = outputField.getSerializedLambda(outputField).getImplMethodName(); + if (conversionCache.getMilvusEntity().getEnableDynamicField() && + conversionCache.getPropertyCache().metaMethodMap.containsKey(methodName)){ + outputMetaFields = Optional.ofNullable(outputMetaFields).orElse(new ArrayList<>()); + outputMetaFields.add(conversionCache.getPropertyCache().metaMethodMap.get(methodName)); + otf.add("$meta"); + } else { + otf.add(outputField.getFieldName(outputField)); + } } this.outputFields = otf; return query(); @@ -1027,7 +1036,19 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr } public MilvusResp>> query(String... outputFields) throws MilvusException { - this.outputFields = Arrays.stream(outputFields).collect(Collectors.toList()); + this.outputFields = Arrays.stream(outputFields) + .map(field -> { + String milvusField = conversionCache.getPropertyCache().functionToPropertyMap.get(field); + if (milvusField == null && + conversionCache.getMilvusEntity().getEnableDynamicField() && + conversionCache.getPropertyCache().metaFunctionSet.contains(field)) { + outputMetaFields = Optional.ofNullable(outputMetaFields).orElse(new ArrayList<>()); + outputMetaFields.add(field); + return "$meta"; + } + return milvusField; + }) + .collect(Collectors.toList()); return query(); } @@ -1041,7 +1062,7 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr GetReq getReq = builder.build(); GetResp getResp = client.get(getReq); - return SearchRespConverter.convertGetRespToMilvusResp(getResp, entityType); + return SearchRespConverter.convertGetRespToMilvusResp(getResp, entityType, outputMetaFields); } @Override From a316368aa3f183e2edcb5ba8b0ae66b4df884cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=91=E5=8B=87?= <1218639030@qq.com> Date: Mon, 29 Sep 2025 21:29:50 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E8=BD=AC=E6=8D=A2=E6=97=B6=E7=9A=84=E7=A9=BA?= =?UTF-8?q?=E6=8C=87=E9=92=88=E9=A3=8E=E9=99=A9=EF=BC=8C=E5=88=A4=E6=96=AD?= =?UTF-8?q?outputMetaFields=E9=9D=9E=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milvus/plus/converter/SearchRespConverter.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java index 7cc2874..f66dc60 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/converter/SearchRespConverter.java @@ -3,6 +3,7 @@ package org.dromara.milvus.plus.converter; import io.milvus.v2.service.vector.response.GetResp; import io.milvus.v2.service.vector.response.QueryResp; import io.milvus.v2.service.vector.response.SearchResp; +import org.apache.commons.collections4.CollectionUtils; import org.dromara.milvus.plus.cache.ConversionCache; import org.dromara.milvus.plus.cache.MilvusCache; import org.dromara.milvus.plus.cache.PropertyCache; @@ -42,7 +43,8 @@ public class SearchRespConverter { for (Map.Entry entry : searchResult.getEntity().entrySet()) { String key = propertyCache.findKeyByValue(entry.getKey()); if (conversionCache.getMilvusEntity().getEnableDynamicField() - && "$meta".equals(entry.getKey())) { + && "$meta".equals(entry.getKey()) + && CollectionUtils.isNotEmpty(outputMetaFields)) { if (entry.getValue() == null) { continue; } @@ -110,7 +112,7 @@ public class SearchRespConverter { * @param entityType 需要转换成的实体类型,指定了转换的目标。 * @return MilvusResp对象,包含转换后的实体列表。每个实体都包装在一个MilvusResult对象中,同时设置成功状态为true。 */ - private static MilvusResp>> convertQuery(List getResults, Class entityType, List outputFields){ + private static MilvusResp>> convertQuery(List getResults, Class entityType, List outputMetaFields){ // 初始化转换缓存和属性缓存,用于帮助将查询结果映射到Java实体 ConversionCache conversionCache = MilvusCache.milvusCache.get(entityType.getName()); PropertyCache propertyCache = conversionCache.getPropertyCache(); @@ -125,7 +127,8 @@ public class SearchRespConverter { String key = propertyCache.findKeyByValue(entry.getKey()); if (conversionCache.getMilvusEntity().getEnableDynamicField() && propertyCache.metaFunctionSet.contains(entry.getKey()) - && outputFields.contains(entry.getKey())) { + && CollectionUtils.isNotEmpty(outputMetaFields) + && outputMetaFields.contains(entry.getKey())) { entityMap2.put(String.valueOf(entry.getKey()), entry.getValue()); } else if(key!=null){ Object value = entry.getValue(); From e6f7bcaf0a489d0d9e432b779d90b9c098bc0009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=91=E5=8B=87?= <1218639030@qq.com> Date: Mon, 29 Sep 2025 22:37:07 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=BD=93=20outputMetaFields=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E4=B8=94=20metaFunctionSet=20=E9=9D=9E=E7=A9=BA?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E8=87=AA=E5=8A=A8=E5=A1=AB=E5=85=85=20meta?= =?UTF-8?q?=20=E5=AD=97=E6=AE=B5=20&=20=E4=BC=98=E5=8C=96=E5=88=86?= =?UTF-8?q?=E5=8C=BA=E5=90=8D=E7=A7=B0=E8=AE=BE=E7=BD=AE=E5=89=8D=E7=9A=84?= =?UTF-8?q?=E9=9B=86=E5=90=88=E9=9D=9E=E7=A9=BA=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milvus/plus/builder/CollectionSchemaBuilder.java | 2 +- .../milvus/plus/core/conditions/LambdaQueryWrapper.java | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/builder/CollectionSchemaBuilder.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/builder/CollectionSchemaBuilder.java index 03d8403..7b33750 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/builder/CollectionSchemaBuilder.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/builder/CollectionSchemaBuilder.java @@ -54,7 +54,7 @@ public class CollectionSchemaBuilder { } public void addNumPartitions(Integer numPartitions){ - if (numPartitions < 1) { + if (numPartitions == null || numPartitions < 1) { return; } this.numPartitions=numPartitions; diff --git a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java index 23222b0..2eee1a2 100644 --- a/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java +++ b/milvus-plus-core/src/main/java/org/dromara/milvus/plus/core/conditions/LambdaQueryWrapper.java @@ -859,6 +859,9 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr Collection values = conversionCache.getPropertyCache().functionToPropertyMap.values(); builder.outputFields(new ArrayList<>(values)); } + if (CollectionUtils.isEmpty(outputMetaFields) && !CollectionUtils.isEmpty(conversionCache.getPropertyCache().metaFunctionSet)) { + outputMetaFields = new ArrayList<>(conversionCache.getPropertyCache().metaFunctionSet); + } if (!searchParams.isEmpty()) { builder.searchParams(searchParams); } @@ -915,6 +918,9 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr Collection values = conversionCache.getPropertyCache().functionToPropertyMap.values(); builder.outputFields(new ArrayList<>(values)); } + if (CollectionUtils.isEmpty(outputMetaFields) && !CollectionUtils.isEmpty(conversionCache.getPropertyCache().metaFunctionSet)) { + outputMetaFields = new ArrayList<>(conversionCache.getPropertyCache().metaFunctionSet); + } return builder.build(); } private HybridSearchReq buildHybrid(){ @@ -956,6 +962,9 @@ public class LambdaQueryWrapper extends AbstractChainWrapper implements Wr Collection values = conversionCache.getPropertyCache().functionToPropertyMap.values(); reqBuilder.outFields(new ArrayList<>(values)); } + if (CollectionUtils.isEmpty(outputMetaFields) && !CollectionUtils.isEmpty(conversionCache.getPropertyCache().metaFunctionSet)) { + outputMetaFields = new ArrayList<>(conversionCache.getPropertyCache().metaFunctionSet); + } if (!CollectionUtils.isEmpty(partitionNames)) { reqBuilder.partitionNames(partitionNames); }