Skip to content

Commit e29e039

Browse files
authored
Support AddFunctionField/DropCollectionField/DropFunctionField (#1923)
Signed-off-by: yhmo <yihua.mo@zilliz.com>
1 parent ee4b440 commit e29e039

11 files changed

Lines changed: 792 additions & 8 deletions

File tree

examples/src/main/java/io/milvus/v2/AddFieldExample.java

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,21 @@
2222
import com.google.gson.Gson;
2323
import com.google.gson.JsonObject;
2424
import io.milvus.v1.CommonUtils;
25+
import io.milvus.common.clientenum.FunctionType;
2526
import io.milvus.v2.client.ConnectConfig;
2627
import io.milvus.v2.client.MilvusClientV2;
2728
import io.milvus.v2.common.ConsistencyLevel;
2829
import io.milvus.v2.common.DataType;
2930
import io.milvus.v2.common.IndexParam;
3031
import io.milvus.v2.service.collection.request.AddCollectionFieldReq;
3132
import io.milvus.v2.service.collection.request.AddFieldReq;
33+
import io.milvus.v2.service.collection.request.AddFunctionFieldReq;
3234
import io.milvus.v2.service.collection.request.CreateCollectionReq;
35+
import io.milvus.v2.service.collection.request.DescribeCollectionReq;
36+
import io.milvus.v2.service.collection.request.DropCollectionFieldReq;
3337
import io.milvus.v2.service.collection.request.DropCollectionReq;
38+
import io.milvus.v2.service.collection.request.DropFunctionFieldReq;
39+
import io.milvus.v2.service.collection.response.DescribeCollectionResp;
3440
import io.milvus.v2.service.vector.request.InsertReq;
3541
import io.milvus.v2.service.vector.request.QueryReq;
3642
import io.milvus.v2.service.vector.response.QueryResp;
@@ -107,6 +113,15 @@ private static void query(long id) {
107113
System.out.println("=============================================================");
108114
}
109115

116+
private static void describeCollection() {
117+
DescribeCollectionResp resp = client.describeCollection(DescribeCollectionReq.builder()
118+
.collectionName(COLLECTION_NAME)
119+
.build());
120+
System.out.println("\nCollection fields:");
121+
System.out.println(resp.getFieldNames());
122+
System.out.println("=============================================================");
123+
}
124+
110125
public static void main(String[] args) {
111126
createCollection();
112127

@@ -132,6 +147,8 @@ public static void main(String[] args) {
132147
.isNullable(true)
133148
.build());
134149

150+
describeCollection();
151+
135152
// Query the previous row
136153
query(100L);
137154
}
@@ -151,6 +168,54 @@ public static void main(String[] args) {
151168
query(500L);
152169
}
153170

171+
{
172+
// Drop the field added above
173+
client.dropCollectionField(DropCollectionFieldReq.builder()
174+
.collectionName(COLLECTION_NAME)
175+
.fieldName("text")
176+
.build());
177+
System.out.println("Field 'text' dropped");
178+
describeCollection();
179+
}
180+
181+
{
182+
// Add the text field back for function-field demo
183+
client.addCollectionField(AddCollectionFieldReq.builder()
184+
.collectionName(COLLECTION_NAME)
185+
.fieldName("text")
186+
.dataType(DataType.VarChar)
187+
.maxLength(100)
188+
.enableAnalyzer(true)
189+
.enableMatch(true)
190+
.isNullable(true)
191+
.build());
192+
193+
// Add a function-backed sparse field
194+
client.addFunctionField(AddFunctionFieldReq.builder()
195+
.collectionName(COLLECTION_NAME)
196+
.fieldName("sparse")
197+
.dataType(DataType.SparseFloatVector)
198+
.function(CreateCollectionReq.Function.builder()
199+
.name("bm25")
200+
.functionType(FunctionType.BM25)
201+
.inputFieldNames(Collections.singletonList("text"))
202+
.outputFieldNames(Collections.singletonList("sparse"))
203+
.build())
204+
.build());
205+
System.out.println("Function-backed field 'sparse' added");
206+
describeCollection();
207+
}
208+
209+
{
210+
// Drop the function and its output field
211+
client.dropFunctionField(DropFunctionFieldReq.builder()
212+
.collectionName(COLLECTION_NAME)
213+
.functionName("bm25")
214+
.build());
215+
System.out.println("Function-backed field 'sparse' dropped");
216+
describeCollection();
217+
}
218+
154219
client.close();
155220
}
156221
}

examples/src/main/java/io/milvus/v2/CDCExample.java

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,25 @@
2222
import io.milvus.v2.client.ConnectConfig;
2323
import io.milvus.v2.client.MilvusClientV2;
2424
import io.milvus.v2.service.cdc.request.CrossClusterTopology;
25+
import io.milvus.v2.service.cdc.request.DumpMessagesReq;
26+
import io.milvus.v2.service.cdc.request.GetReplicateInfoReq;
2527
import io.milvus.v2.service.cdc.request.MilvusCluster;
2628
import io.milvus.v2.service.cdc.request.ReplicateConfiguration;
2729
import io.milvus.v2.service.cdc.request.UpdateReplicateConfigurationReq;
30+
import io.milvus.v2.service.cdc.response.DumpMessageInfo;
31+
import io.milvus.v2.service.cdc.response.DumpMessagesResp;
32+
import io.milvus.v2.service.cdc.response.GetReplicateConfigurationResp;
33+
import io.milvus.v2.service.cdc.response.GetReplicateInfoResp;
2834

2935
import java.util.ArrayList;
3036
import java.util.List;
3137

3238
public class CDCExample {
33-
private static final String clusterAURI = "http://192.168.1.1:19530";
34-
private static final String clusterBURI = "http://192.168.1.1:19500";
39+
private static final String clusterAURI = "http://localhost:19530";
40+
private static final String clusterBURI = "http://localhost:29530";
3541

36-
private static final String clusterAId = "cdc-test-upstream";
37-
private static final String clusterBId = "cdc-test-downstream";
42+
private static final String clusterAId = "cdc-a";
43+
private static final String clusterBId = "cdc-b";
3844

3945
private static final Integer pchannelNum = 16;
4046

@@ -51,11 +57,13 @@ public static void main(String[] args) {
5157
.uri(clusterAURI)
5258
.build();
5359
MilvusClientV2 clusterAClient = new MilvusClientV2(clusterA);
60+
System.out.println("Cluster A connected: " + clusterAClient.getServerVersion());
5461

5562
ConnectConfig clusterB = ConnectConfig.builder()
5663
.uri(clusterBURI)
5764
.build();
5865
MilvusClientV2 clusterBClient = new MilvusClientV2(clusterB);
66+
System.out.println("Cluster B connected: " + clusterBClient.getServerVersion());
5967

6068
MilvusCluster milvusClusterA = MilvusCluster.builder()
6169
.clusterId(clusterAId)
@@ -88,6 +96,32 @@ public static void main(String[] args) {
8896
.build();
8997

9098
clusterAClient.updateReplicateConfiguration(updateReq);
99+
System.out.println("Replicate configuration updated for cluster A");
91100
clusterBClient.updateReplicateConfiguration(updateReq);
101+
System.out.println("Replicate configuration updated for cluster B");
102+
103+
GetReplicateConfigurationResp replicateConfigurationResp = clusterAClient.getReplicateConfiguration();
104+
System.out.println("Replicate configuration: " + replicateConfigurationResp.getReplicateConfiguration());
105+
106+
GetReplicateInfoResp replicateInfoResp = clusterBClient.getReplicateInfo(GetReplicateInfoReq.builder()
107+
.sourceClusterId(clusterAId)
108+
.targetPchannel(generatePChannels(clusterBId).get(0))
109+
.build());
110+
System.out.println("Replicate info: " + replicateInfoResp);
111+
112+
String dumpStartMessageId = replicateInfoResp.getSalvageCheckpoint().getMessageID().getId();
113+
114+
DumpMessagesResp dumpMessagesResp = clusterAClient.dumpMessages(DumpMessagesReq.builder()
115+
.pchannel(generatePChannels(clusterAId).get(0))
116+
.startMessageID(GetReplicateInfoResp.MessageID.builder()
117+
.id(dumpStartMessageId)
118+
.walName("RocksMQ")
119+
.build())
120+
.build());
121+
System.out.println("Dump messages:");
122+
for (DumpMessageInfo message : dumpMessagesResp) {
123+
System.out.println("\tmessage id: " + message.getMessageID().getId());
124+
System.out.println("\tpayload size: " + message.getPayload().length);
125+
}
92126
}
93127
}

sdk-core/src/main/java/io/milvus/v2/client/MilvusClientV2.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,15 @@ public void alterCollectionField(AlterCollectionFieldReq request) {
509509
rpcUtils.retry(() -> collectionService.alterCollectionField(this.getRpcStub(), request));
510510
}
511511

512+
/**
513+
* Drop a field from collection.
514+
*
515+
* @param request drop field request
516+
*/
517+
public void dropCollectionField(DropCollectionFieldReq request) {
518+
rpcUtils.retry(() -> collectionService.dropCollectionField(this.getRpcStub(), request));
519+
}
520+
512521
/**
513522
* drop a collection's properties.
514523
*
@@ -643,6 +652,15 @@ public void addCollectionFunction(AddCollectionFunctionReq request) {
643652
rpcUtils.retry(() -> collectionService.addCollectionFunction(this.getRpcStub(), request));
644653
}
645654

655+
/**
656+
* Add a function-backed field to collection.
657+
*
658+
* @param request add function field request
659+
*/
660+
public void addFunctionField(AddFunctionFieldReq request) {
661+
rpcUtils.retry(() -> collectionService.addFunctionField(this.getRpcStub(), request));
662+
}
663+
646664
/**
647665
* Alter a function of collection.
648666
*
@@ -654,13 +672,25 @@ public void alterCollectionFunction(AlterCollectionFunctionReq request) {
654672

655673
/**
656674
* Drop a function of collection.
675+
* This removes only the function definition and keeps its output field(s).
657676
*
658677
* @param request drop function request
659678
*/
660679
public void dropCollectionFunction(DropCollectionFunctionReq request) {
661680
rpcUtils.retry(() -> collectionService.dropCollectionFunction(this.getRpcStub(), request));
662681
}
663682

683+
/**
684+
* Drop a function-backed field from collection.
685+
* This removes the function and its output field(s), unlike dropCollectionFunction,
686+
* which only drops the function definition.
687+
*
688+
* @param request drop function field request
689+
*/
690+
public void dropFunctionField(DropFunctionFieldReq request) {
691+
rpcUtils.retry(() -> collectionService.dropFunctionField(this.getRpcStub(), request));
692+
}
693+
664694
/////////////////////////////////////////////////////////////////////////////////////////////
665695
// Index Operations
666696
/////////////////////////////////////////////////////////////////////////////////////////////

sdk-core/src/main/java/io/milvus/v2/service/collection/CollectionService.java

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,40 @@ public Void alterCollectionField(MilvusServiceGrpc.MilvusServiceBlockingStub blo
343343
return null;
344344
}
345345

346+
public Void dropCollectionField(MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub, DropCollectionFieldReq request) {
347+
String dbName = request.getDatabaseName();
348+
String collectionName = request.getCollectionName();
349+
String fieldName = request.getFieldName();
350+
Long fieldId = request.getFieldId();
351+
boolean hasFieldName = StringUtils.isNotBlank(fieldName);
352+
boolean hasFieldId = fieldId != null && fieldId > 0;
353+
if (hasFieldName == hasFieldId) {
354+
throw new MilvusClientException(ErrorCode.INVALID_PARAMS,
355+
"Exactly one of fieldName or fieldId must be provided.");
356+
}
357+
358+
String title = String.format("Drop field of collection: '%s' in database: '%s'", collectionName, dbName);
359+
AlterCollectionSchemaRequest.DropRequest.Builder dropBuilder = AlterCollectionSchemaRequest.DropRequest.newBuilder();
360+
if (hasFieldName) {
361+
dropBuilder.setFieldName(fieldName);
362+
} else {
363+
dropBuilder.setFieldId(fieldId);
364+
}
365+
366+
AlterCollectionSchemaRequest.Builder builder = AlterCollectionSchemaRequest.newBuilder()
367+
.setCollectionName(collectionName)
368+
.setAction(AlterCollectionSchemaRequest.Action.newBuilder()
369+
.setDropRequest(dropBuilder)
370+
.build());
371+
if (StringUtils.isNotEmpty(dbName)) {
372+
builder.setDbName(dbName);
373+
}
374+
375+
AlterCollectionSchemaResponse response = blockingStub.alterCollectionSchema(builder.build());
376+
rpcUtils.handleResponse(title, response.getAlterStatus());
377+
return null;
378+
}
379+
346380
public Void dropCollectionProperties(MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub, DropCollectionPropertiesReq request) {
347381
String dbName = request.getDatabaseName();
348382
String collectionName = request.getCollectionName();
@@ -697,6 +731,49 @@ public Void addCollectionFunction(MilvusServiceGrpc.MilvusServiceBlockingStub bl
697731
return null;
698732
}
699733

734+
public Void addFunctionField(MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub,
735+
AddFunctionFieldReq request) {
736+
if (request.getFunction() == null) {
737+
throw new MilvusClientException(ErrorCode.INVALID_PARAMS, "Function cannot be null.");
738+
}
739+
if (CollectionUtils.isEmpty(request.getFunction().getOutputFieldNames())
740+
|| request.getFunction().getOutputFieldNames().size() != 1
741+
|| !StringUtils.equals(request.getFieldName(), request.getFunction().getOutputFieldNames().get(0))) {
742+
throw new MilvusClientException(ErrorCode.INVALID_PARAMS,
743+
"Function must have exactly one output field matching the field being added.");
744+
}
745+
746+
String dbName = request.getDatabaseName();
747+
String collectionName = request.getCollectionName();
748+
String title = String.format("Add function field to collection: '%s' in database: '%s'", collectionName, dbName);
749+
750+
CreateCollectionReq.FieldSchema fieldSchema = SchemaUtils.convertFieldReqToFieldSchema(request);
751+
FieldSchema grpcFieldSchema = SchemaUtils.convertToGrpcFieldSchema(fieldSchema)
752+
.toBuilder()
753+
.setIsFunctionOutput(true)
754+
.build();
755+
AlterCollectionSchemaRequest.FieldInfo fieldInfo = AlterCollectionSchemaRequest.FieldInfo.newBuilder()
756+
.setFieldSchema(grpcFieldSchema)
757+
.build();
758+
AlterCollectionSchemaRequest.AddRequest addRequest = AlterCollectionSchemaRequest.AddRequest.newBuilder()
759+
.addFieldInfos(fieldInfo)
760+
.addFuncSchema(SchemaUtils.convertToGrpcFunction(request.getFunction()))
761+
.build();
762+
763+
AlterCollectionSchemaRequest.Builder builder = AlterCollectionSchemaRequest.newBuilder()
764+
.setCollectionName(collectionName)
765+
.setAction(AlterCollectionSchemaRequest.Action.newBuilder()
766+
.setAddRequest(addRequest)
767+
.build());
768+
if (StringUtils.isNotEmpty(dbName)) {
769+
builder.setDbName(dbName);
770+
}
771+
772+
AlterCollectionSchemaResponse response = blockingStub.alterCollectionSchema(builder.build());
773+
rpcUtils.handleResponse(title, response.getAlterStatus());
774+
return null;
775+
}
776+
700777
public Void alterCollectionFunction(MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub,
701778
AlterCollectionFunctionReq request) {
702779
if (request.getFunction() == null) {
@@ -728,15 +805,47 @@ public Void dropCollectionFunction(MilvusServiceGrpc.MilvusServiceBlockingStub b
728805
String dbName = request.getDatabaseName();
729806
String collectionName = request.getCollectionName();
730807
String title = String.format("Drop function to collection: '%s' in database: '%s'", collectionName, dbName);
731-
DropCollectionFunctionRequest.Builder builder = DropCollectionFunctionRequest.newBuilder()
808+
AlterCollectionSchemaRequest.Builder builder = AlterCollectionSchemaRequest.newBuilder()
732809
.setCollectionName(collectionName)
733-
.setFunctionName(request.getFunctionName());
810+
.setAction(AlterCollectionSchemaRequest.Action.newBuilder()
811+
.setDropRequest(AlterCollectionSchemaRequest.DropRequest.newBuilder()
812+
.setFunctionName(request.getFunctionName())
813+
.setDropFunctionOutputFields(false)
814+
.build())
815+
.build());
816+
if (StringUtils.isNotEmpty(dbName)) {
817+
builder.setDbName(dbName);
818+
}
819+
820+
AlterCollectionSchemaResponse response = blockingStub.alterCollectionSchema(builder.build());
821+
rpcUtils.handleResponse(title, response.getAlterStatus());
822+
823+
return null;
824+
}
825+
826+
public Void dropFunctionField(MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub,
827+
DropFunctionFieldReq request) {
828+
if (StringUtils.isEmpty(request.getFunctionName())) {
829+
throw new MilvusClientException(ErrorCode.INVALID_PARAMS, "Function name cannot be empty.");
830+
}
831+
832+
String dbName = request.getDatabaseName();
833+
String collectionName = request.getCollectionName();
834+
String title = String.format("Drop function field of collection: '%s' in database: '%s'", collectionName, dbName);
835+
AlterCollectionSchemaRequest.Builder builder = AlterCollectionSchemaRequest.newBuilder()
836+
.setCollectionName(collectionName)
837+
.setAction(AlterCollectionSchemaRequest.Action.newBuilder()
838+
.setDropRequest(AlterCollectionSchemaRequest.DropRequest.newBuilder()
839+
.setFunctionName(request.getFunctionName())
840+
.setDropFunctionOutputFields(true)
841+
.build())
842+
.build());
734843
if (StringUtils.isNotEmpty(dbName)) {
735844
builder.setDbName(dbName);
736845
}
737-
Status status = blockingStub.dropCollectionFunction(builder.build());
738-
rpcUtils.handleResponse(title, status);
739846

847+
AlterCollectionSchemaResponse response = blockingStub.alterCollectionSchema(builder.build());
848+
rpcUtils.handleResponse(title, response.getAlterStatus());
740849
return null;
741850
}
742851

0 commit comments

Comments
 (0)