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/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 f1df017..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 @@ -94,6 +94,11 @@ 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"); + 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 dc4fcfc..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; @@ -27,7 +28,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(); @@ -41,7 +42,18 @@ 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()) + && CollectionUtils.isNotEmpty(outputMetaFields)) { + if (entry.getValue() == null) { + continue; + } + 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); } @@ -69,10 +81,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中提取查询结果 @@ -86,10 +98,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); } @@ -100,7 +112,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 outputMetaFields){ // 初始化转换缓存和属性缓存,用于帮助将查询结果映射到Java实体 ConversionCache conversionCache = MilvusCache.milvusCache.get(entityType.getName()); PropertyCache propertyCache = conversionCache.getPropertyCache(); @@ -113,7 +125,12 @@ 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()) + && CollectionUtils.isNotEmpty(outputMetaFields) + && outputMetaFields.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..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 @@ -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; @@ -858,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); } @@ -914,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(){ @@ -955,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); } @@ -977,18 +987,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 +1012,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 +1045,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 +1071,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