|
@@ -19,17 +19,18 @@
|
|
|
|
|
|
package io.milvus.v2.utils;
|
|
|
|
|
|
-import com.google.gson.JsonElement;
|
|
|
-import com.google.gson.JsonNull;
|
|
|
-import com.google.gson.JsonObject;
|
|
|
-import io.milvus.exception.ParamException;
|
|
|
+import com.google.gson.*;
|
|
|
import io.milvus.grpc.*;
|
|
|
import io.milvus.param.Constant;
|
|
|
import io.milvus.param.ParamUtils;
|
|
|
-import io.milvus.param.collection.FieldType;
|
|
|
-import io.milvus.response.DescCollResponseWrapper;
|
|
|
+import io.milvus.v2.exception.ErrorCode;
|
|
|
+import io.milvus.v2.exception.MilvusClientException;
|
|
|
+import io.milvus.v2.service.collection.request.CreateCollectionReq;
|
|
|
+import io.milvus.v2.service.collection.response.DescribeCollectionResp;
|
|
|
import io.milvus.v2.service.vector.request.InsertReq;
|
|
|
import io.milvus.v2.service.vector.request.UpsertReq;
|
|
|
+import lombok.Builder;
|
|
|
+import lombok.Getter;
|
|
|
import lombok.NonNull;
|
|
|
|
|
|
import java.util.*;
|
|
@@ -41,7 +42,7 @@ public class DataUtils {
|
|
|
private UpsertRequest.Builder upsertBuilder;
|
|
|
|
|
|
public InsertRequest convertGrpcInsertRequest(@NonNull InsertReq requestParam,
|
|
|
- DescCollResponseWrapper wrapper) {
|
|
|
+ DescribeCollectionResp descColl) {
|
|
|
String collectionName = requestParam.getCollectionName();
|
|
|
|
|
|
// generate insert request builder
|
|
@@ -51,18 +52,18 @@ public class DataUtils {
|
|
|
.setBase(msgBase)
|
|
|
.setNumRows(requestParam.getData().size());
|
|
|
upsertBuilder = null;
|
|
|
- fillFieldsData(requestParam, wrapper);
|
|
|
+ fillFieldsData(requestParam, descColl);
|
|
|
return insertBuilder.build();
|
|
|
}
|
|
|
|
|
|
public UpsertRequest convertGrpcUpsertRequest(@NonNull UpsertReq requestParam,
|
|
|
- DescCollResponseWrapper wrapper) {
|
|
|
+ DescribeCollectionResp descColl) {
|
|
|
String collectionName = requestParam.getCollectionName();
|
|
|
|
|
|
// currently, not allow to upsert for collection whose primary key is auto-generated
|
|
|
- FieldType pk = wrapper.getPrimaryField();
|
|
|
- if (pk.isAutoID()) {
|
|
|
- throw new ParamException(String.format("Upsert don't support autoID==True, collection: %s",
|
|
|
+ CreateCollectionReq.FieldSchema primaryField = descColl.getCollectionSchema().getField(descColl.getPrimaryFieldName());
|
|
|
+ if (primaryField.getAutoID() == Boolean.TRUE) {
|
|
|
+ throw new MilvusClientException(ErrorCode.INVALID_PARAMS, String.format("Upsert don't support autoID==True, collection: %s",
|
|
|
requestParam.getCollectionName()));
|
|
|
}
|
|
|
|
|
@@ -73,7 +74,7 @@ public class DataUtils {
|
|
|
.setBase(msgBase)
|
|
|
.setNumRows(requestParam.getData().size());
|
|
|
insertBuilder = null;
|
|
|
- fillFieldsData(requestParam, wrapper);
|
|
|
+ fillFieldsData(requestParam, descColl);
|
|
|
return upsertBuilder.build();
|
|
|
}
|
|
|
|
|
@@ -93,20 +94,24 @@ public class DataUtils {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void fillFieldsData(UpsertReq requestParam, DescCollResponseWrapper wrapper) {
|
|
|
- // set partition name only when there is no partition key field
|
|
|
- String partitionName = requestParam.getPartitionName();
|
|
|
- boolean isPartitionKeyEnabled = false;
|
|
|
- for (FieldType fieldType : wrapper.getFields()) {
|
|
|
- if (fieldType.isPartitionKey()) {
|
|
|
- isPartitionKeyEnabled = true;
|
|
|
- break;
|
|
|
+ private static boolean hasPartitionKey(DescribeCollectionResp descColl) {
|
|
|
+ CreateCollectionReq.CollectionSchema collectionSchema = descColl.getCollectionSchema();
|
|
|
+ List<CreateCollectionReq.FieldSchema> fieldsList = collectionSchema.getFieldSchemaList();
|
|
|
+ for (CreateCollectionReq.FieldSchema field : fieldsList) {
|
|
|
+ if (field.getIsPartitionKey() == Boolean.TRUE) {
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
- if (isPartitionKeyEnabled) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void fillFieldsData(UpsertReq requestParam, DescribeCollectionResp descColl) {
|
|
|
+ // set partition name only when there is no partition key field
|
|
|
+ String partitionName = requestParam.getPartitionName();
|
|
|
+ if (hasPartitionKey(descColl)) {
|
|
|
if (partitionName != null && !partitionName.isEmpty()) {
|
|
|
String msg = "Collection " + requestParam.getCollectionName() + " has partition key, not allow to specify partition name";
|
|
|
- throw new ParamException(msg);
|
|
|
+ throw new MilvusClientException(ErrorCode.INVALID_PARAMS, msg);
|
|
|
}
|
|
|
} else if (partitionName != null) {
|
|
|
this.setPartitionName(partitionName);
|
|
@@ -114,23 +119,16 @@ public class DataUtils {
|
|
|
|
|
|
// convert insert data
|
|
|
List<JsonObject> rowFields = requestParam.getData();
|
|
|
- checkAndSetRowData(wrapper, rowFields);
|
|
|
+ checkAndSetRowData(descColl, rowFields);
|
|
|
}
|
|
|
|
|
|
- private void fillFieldsData(InsertReq requestParam, DescCollResponseWrapper wrapper) {
|
|
|
+ private void fillFieldsData(InsertReq requestParam, DescribeCollectionResp descColl) {
|
|
|
// set partition name only when there is no partition key field
|
|
|
String partitionName = requestParam.getPartitionName();
|
|
|
- boolean isPartitionKeyEnabled = false;
|
|
|
- for (FieldType fieldType : wrapper.getFields()) {
|
|
|
- if (fieldType.isPartitionKey()) {
|
|
|
- isPartitionKeyEnabled = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (isPartitionKeyEnabled) {
|
|
|
+ if (hasPartitionKey(descColl)) {
|
|
|
if (partitionName != null && !partitionName.isEmpty()) {
|
|
|
String msg = "Collection " + requestParam.getCollectionName() + " has partition key, not allow to specify partition name";
|
|
|
- throw new ParamException(msg);
|
|
|
+ throw new MilvusClientException(ErrorCode.INVALID_PARAMS, msg);
|
|
|
}
|
|
|
} else if (partitionName != null) {
|
|
|
this.setPartitionName(partitionName);
|
|
@@ -138,53 +136,69 @@ public class DataUtils {
|
|
|
|
|
|
// convert insert data
|
|
|
List<JsonObject> rowFields = requestParam.getData();
|
|
|
- checkAndSetRowData(wrapper, rowFields);
|
|
|
+ checkAndSetRowData(descColl, rowFields);
|
|
|
}
|
|
|
|
|
|
- private void checkAndSetRowData(DescCollResponseWrapper wrapper, List<JsonObject> rows) {
|
|
|
- List<FieldType> fieldTypes = wrapper.getFields();
|
|
|
+ private void checkAndSetRowData(DescribeCollectionResp descColl, List<JsonObject> rows) {
|
|
|
+ CreateCollectionReq.CollectionSchema collectionSchema = descColl.getCollectionSchema();
|
|
|
+ List<CreateCollectionReq.Function> functionsList = collectionSchema.getFunctionList();
|
|
|
+ List<String> outputFieldNames = new ArrayList<>();
|
|
|
+ for (CreateCollectionReq.Function function : functionsList) {
|
|
|
+ outputFieldNames.addAll(function.getOutputFieldNames());
|
|
|
+ }
|
|
|
|
|
|
- Map<String, ParamUtils.InsertDataInfo> nameInsertInfo = new HashMap<>();
|
|
|
- ParamUtils.InsertDataInfo insertDynamicDataInfo = ParamUtils.InsertDataInfo.builder().fieldType(
|
|
|
- FieldType.newBuilder()
|
|
|
- .withName(Constant.DYNAMIC_FIELD_NAME)
|
|
|
- .withDataType(DataType.JSON)
|
|
|
- .withIsDynamic(true)
|
|
|
+ List<CreateCollectionReq.FieldSchema> fieldsList = collectionSchema.getFieldSchemaList();
|
|
|
+ Map<String, InsertDataInfo> nameInsertInfo = new HashMap<>();
|
|
|
+ InsertDataInfo insertDynamicDataInfo = InsertDataInfo.builder().field(
|
|
|
+ CreateCollectionReq.FieldSchema.builder()
|
|
|
+ .name(Constant.DYNAMIC_FIELD_NAME)
|
|
|
+ .dataType(io.milvus.v2.common.DataType.JSON)
|
|
|
.build())
|
|
|
.data(new LinkedList<>()).build();
|
|
|
for (JsonObject row : rows) {
|
|
|
- for (FieldType fieldType : fieldTypes) {
|
|
|
- String fieldName = fieldType.getName();
|
|
|
- ParamUtils.InsertDataInfo insertDataInfo = nameInsertInfo.getOrDefault(fieldName, ParamUtils.InsertDataInfo.builder()
|
|
|
- .fieldType(fieldType).data(new LinkedList<>()).build());
|
|
|
+ for (CreateCollectionReq.FieldSchema field : fieldsList) {
|
|
|
+ String fieldName = field.getName();
|
|
|
+ InsertDataInfo insertDataInfo = nameInsertInfo.getOrDefault(fieldName, InsertDataInfo.builder()
|
|
|
+ .field(field).data(new LinkedList<>()).build());
|
|
|
|
|
|
// check normalField
|
|
|
JsonElement rowFieldData = row.get(fieldName);
|
|
|
if (rowFieldData == null) {
|
|
|
- // check if autoId
|
|
|
- if (fieldType.isAutoID()) {
|
|
|
+ // if the field is auto-id, no need to provide value
|
|
|
+ if (field.getAutoID() == Boolean.TRUE) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // if the field is an output field of doc-in-doc-out, no need to provide value
|
|
|
+ if (outputFieldNames.contains(field.getName())) {
|
|
|
continue;
|
|
|
}
|
|
|
+
|
|
|
// if the field doesn't have default value, require user provide the value
|
|
|
- if (!fieldType.isNullable() && fieldType.getDefaultValue() == null) {
|
|
|
- String msg = String.format("The field: %s is not provided.", fieldType.getName());
|
|
|
- throw new ParamException(msg);
|
|
|
+ if (!field.getIsNullable() && field.getDefaultValue() == null) {
|
|
|
+ String msg = String.format("The field: %s is not provided.", field.getName());
|
|
|
+ throw new MilvusClientException(ErrorCode.INVALID_PARAMS, msg);
|
|
|
}
|
|
|
|
|
|
rowFieldData = JsonNull.INSTANCE;
|
|
|
}
|
|
|
|
|
|
- if (fieldType.isAutoID()) {
|
|
|
+ if (field.getAutoID() == Boolean.TRUE) {
|
|
|
String msg = String.format("The primary key: %s is auto generated, no need to input.", fieldName);
|
|
|
- throw new ParamException(msg);
|
|
|
+ throw new MilvusClientException(ErrorCode.INVALID_PARAMS, msg);
|
|
|
}
|
|
|
- Object fieldValue = ParamUtils.checkFieldValue(fieldType, rowFieldData);
|
|
|
+
|
|
|
+ // here we convert the v2 FieldSchema to grpc.FieldSchema then to v1 FieldType
|
|
|
+ // the reason is the logic in ParamUtils.checkFieldValue is complicated, we don't intend to
|
|
|
+ // write duplicate code here
|
|
|
+ FieldSchema grpcField = SchemaUtils.convertToGrpcFieldSchema(field);
|
|
|
+ Object fieldValue = ParamUtils.checkFieldValue(ParamUtils.ConvertField(grpcField), rowFieldData);
|
|
|
insertDataInfo.getData().add(fieldValue);
|
|
|
nameInsertInfo.put(fieldName, insertDataInfo);
|
|
|
}
|
|
|
|
|
|
// deal with dynamicField
|
|
|
- if (wrapper.getEnableDynamicField()) {
|
|
|
+ if (collectionSchema.isEnableDynamicField()) {
|
|
|
JsonObject dynamicField = new JsonObject();
|
|
|
for (String rowFieldName : row.keySet()) {
|
|
|
if (!nameInsertInfo.containsKey(rowFieldName)) {
|
|
@@ -196,12 +210,27 @@ public class DataUtils {
|
|
|
}
|
|
|
|
|
|
for (String fieldNameKey : nameInsertInfo.keySet()) {
|
|
|
- ParamUtils.InsertDataInfo insertDataInfo = nameInsertInfo.get(fieldNameKey);
|
|
|
- this.addFieldsData(ParamUtils.genFieldData(insertDataInfo.getFieldType(), insertDataInfo.getData()));
|
|
|
+ InsertDataInfo insertDataInfo = nameInsertInfo.get(fieldNameKey);
|
|
|
+ // here we convert the v2 FieldSchema to grpc.FieldSchema then to v1 FieldType
|
|
|
+ // the reason is the logic in ParamUtils.genFieldData is complicated, we don't intend to
|
|
|
+ // write duplicate code here
|
|
|
+ FieldSchema grpcField = SchemaUtils.convertToGrpcFieldSchema(insertDataInfo.getField());
|
|
|
+ this.addFieldsData(ParamUtils.genFieldData(ParamUtils.ConvertField(grpcField), insertDataInfo.getData()));
|
|
|
}
|
|
|
- if (wrapper.getEnableDynamicField()) {
|
|
|
- this.addFieldsData(ParamUtils.genFieldData(insertDynamicDataInfo.getFieldType(), insertDynamicDataInfo.getData(), Boolean.TRUE));
|
|
|
+ if (collectionSchema.isEnableDynamicField()) {
|
|
|
+ // here we convert the v2 FieldSchema to grpc.FieldSchema then to v1 FieldType
|
|
|
+ // the reason is the logic in ParamUtils.genFieldData is complicated, we don't intend to
|
|
|
+ // write duplicate code here
|
|
|
+ FieldSchema grpcField = SchemaUtils.convertToGrpcFieldSchema(insertDynamicDataInfo.getField());
|
|
|
+ this.addFieldsData(ParamUtils.genFieldData(ParamUtils.ConvertField(grpcField), insertDynamicDataInfo.getData(), Boolean.TRUE));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ @Builder
|
|
|
+ @Getter
|
|
|
+ public static class InsertDataInfo {
|
|
|
+ private final CreateCollectionReq.FieldSchema field;
|
|
|
+ private final LinkedList<Object> data;
|
|
|
+ }
|
|
|
}
|