Browse Source

Fix some problems (#225)

Signed-off-by: yhmo <yihua.mo@zilliz.com>
groot 3 years ago
parent
commit
6db47d10ba

+ 2 - 2
.github/mergify.yml

@@ -2,7 +2,7 @@ pull_request_rules:
   - name: Test passed for code changed
   - name: Test passed for code changed
     conditions:
     conditions:
       - base=master
       - base=master
-      - "status-success=Build with Maven"
+      - "status-success=Build and test"
     actions:
     actions:
       label:
       label:
         add:
         add:
@@ -11,7 +11,7 @@ pull_request_rules:
   - name: Remove ci-passed when code check failed
   - name: Remove ci-passed when code check failed
     conditions:
     conditions:
       - base=master
       - base=master
-      - "check-failure=Build with Maven"
+      - "check-failure=Build and test"
     actions:
     actions:
       label:
       label:
         remove:
         remove:

+ 1 - 1
.github/workflows/maven.yml

@@ -11,7 +11,7 @@ on:
 
 
 jobs:
 jobs:
   build:
   build:
-
+    name: Build and test
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
 
 
     steps:
     steps:

+ 32 - 11
examples/main/io/milvus/GeneralExample.java

@@ -31,7 +31,7 @@ import io.milvus.Response.*;
 import java.util.*;
 import java.util.*;
 
 
 public class GeneralExample {
 public class GeneralExample {
-    private static MilvusServiceClient milvusClient;
+    private static final MilvusServiceClient milvusClient;
 
 
     static {
     static {
         ConnectParam connectParam = ConnectParam.newBuilder()
         ConnectParam connectParam = ConnectParam.newBuilder()
@@ -45,6 +45,7 @@ public class GeneralExample {
     private static final String ID_FIELD = "userID";
     private static final String ID_FIELD = "userID";
     private static final String VECTOR_FIELD = "userFace";
     private static final String VECTOR_FIELD = "userFace";
     private static final Integer VECTOR_DIM = 64;
     private static final Integer VECTOR_DIM = 64;
+    private static final String AGE_FIELD = "userAge";
 
 
     private static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
     private static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
     private static final String INDEX_PARAM = "{\"nlist\":128}";
     private static final String INDEX_PARAM = "{\"nlist\":128}";
@@ -59,8 +60,8 @@ public class GeneralExample {
                 .withName(ID_FIELD)
                 .withName(ID_FIELD)
                 .withDescription("user identification")
                 .withDescription("user identification")
                 .withDataType(DataType.Int64)
                 .withDataType(DataType.Int64)
-                .withAutoID(false)
                 .withPrimaryKey(true)
                 .withPrimaryKey(true)
+                .withAutoID(true)
                 .build();
                 .build();
 
 
         FieldType fieldType2 = FieldType.newBuilder()
         FieldType fieldType2 = FieldType.newBuilder()
@@ -70,12 +71,19 @@ public class GeneralExample {
                 .withDimension(VECTOR_DIM)
                 .withDimension(VECTOR_DIM)
                 .build();
                 .build();
 
 
+        FieldType fieldType3 = FieldType.newBuilder()
+                .withName(AGE_FIELD)
+                .withDescription("user age")
+                .withDataType(DataType.Int8)
+                .build();
+
         CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
         CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withCollectionName(COLLECTION_NAME)
                 .withDescription("customer info")
                 .withDescription("customer info")
                 .withShardsNum(2)
                 .withShardsNum(2)
                 .addFieldType(fieldType1)
                 .addFieldType(fieldType1)
                 .addFieldType(fieldType2)
                 .addFieldType(fieldType2)
+                .addFieldType(fieldType3)
                 .build();
                 .build();
         R<RpcStatus> response = milvusClient.createCollection(createCollectionReq);
         R<RpcStatus> response = milvusClient.createCollection(createCollectionReq);
 
 
@@ -209,6 +217,7 @@ public class GeneralExample {
                 .withIndexType(INDEX_TYPE)
                 .withIndexType(INDEX_TYPE)
                 .withMetricType(METRIC_TYPE)
                 .withMetricType(METRIC_TYPE)
                 .withExtraParam(INDEX_PARAM)
                 .withExtraParam(INDEX_PARAM)
+                .withSyncMode(Boolean.TRUE)
                 .build());
                 .build());
         System.out.println(response);
         System.out.println(response);
         return response;
         return response;
@@ -295,7 +304,7 @@ public class GeneralExample {
 
 
         R<SearchResults> response = milvusClient.search(searchParam);
         R<SearchResults> response = milvusClient.search(searchParam);
 
 
-        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData());
+        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData().getResults());
         for (int i = 0; i < vectors.size(); ++i) {
         for (int i = 0; i < vectors.size(); ++i) {
             System.out.println("Search result of No." + i);
             System.out.println("Search result of No." + i);
             List<SearchResultsWrapper.IDScore> scores = wrapper.GetIDScore(i);
             List<SearchResultsWrapper.IDScore> scores = wrapper.GetIDScore(i);
@@ -327,35 +336,38 @@ public class GeneralExample {
 
 
     private R<QueryResults> query(String expr) {
     private R<QueryResults> query(String expr) {
         System.out.println("========== query() ==========");
         System.out.println("========== query() ==========");
-        List<String> fields = Arrays.asList(ID_FIELD, VECTOR_FIELD);
+        List<String> fields = Arrays.asList(ID_FIELD, AGE_FIELD);
         QueryParam test = QueryParam.newBuilder()
         QueryParam test = QueryParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withCollectionName(COLLECTION_NAME)
                 .withExpr(expr)
                 .withExpr(expr)
                 .withOutFields(fields)
                 .withOutFields(fields)
                 .build();
                 .build();
         R<QueryResults> response = milvusClient.query(test);
         R<QueryResults> response = milvusClient.query(test);
-        System.out.println(response);
+        QueryResultsWrapper wrapper = new QueryResultsWrapper(response.getData());
+        System.out.println(ID_FIELD + ":" + wrapper.getFieldWrapper(ID_FIELD).getFieldData().toString());
+        System.out.println(AGE_FIELD + ":" + wrapper.getFieldWrapper(AGE_FIELD).getFieldData().toString());
+        System.out.println("Query row count: " + wrapper.getFieldWrapper(ID_FIELD).getRowCount());
         return response;
         return response;
     }
     }
 
 
     private R<MutationResult> insert(String partitionName, Long count) {
     private R<MutationResult> insert(String partitionName, Long count) {
         System.out.println("========== insert() ==========");
         System.out.println("========== insert() ==========");
-        List<Long> ids = new ArrayList<>();
         List<List<Float>> vectors = new ArrayList<>();
         List<List<Float>> vectors = new ArrayList<>();
+        List<Integer> ages = new ArrayList<>();
 
 
         Random ran=new Random();
         Random ran=new Random();
         for (long i = 0L; i < count; ++i) {
         for (long i = 0L; i < count; ++i) {
-            ids.add(i + 100L);
             List<Float> vector = new ArrayList<>();
             List<Float> vector = new ArrayList<>();
             for (int d = 0; d < VECTOR_DIM; ++d) {
             for (int d = 0; d < VECTOR_DIM; ++d) {
                 vector.add(ran.nextFloat());
                 vector.add(ran.nextFloat());
             }
             }
             vectors.add(vector);
             vectors.add(vector);
+            ages.add(ran.nextInt(99));
         }
         }
 
 
         List<InsertParam.Field> fields = new ArrayList<>();
         List<InsertParam.Field> fields = new ArrayList<>();
-        fields.add(new InsertParam.Field(ID_FIELD, DataType.Int64, ids));
         fields.add(new InsertParam.Field(VECTOR_FIELD, DataType.FloatVector, vectors));
         fields.add(new InsertParam.Field(VECTOR_FIELD, DataType.FloatVector, vectors));
+        fields.add(new InsertParam.Field(AGE_FIELD, DataType.Int8, ages));
 
 
         InsertParam insertParam = InsertParam.newBuilder()
         InsertParam insertParam = InsertParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withCollectionName(COLLECTION_NAME)
@@ -385,7 +397,14 @@ public class GeneralExample {
         example.showPartitions();
         example.showPartitions();
 
 
         final Long row_count = 10000L;
         final Long row_count = 10000L;
-        example.insert(partitionName, row_count);
+        List<Long> deleteIds = new ArrayList<>();
+        Random ran = new Random();
+        for (int i = 0; i < 100; ++i) {
+            R<MutationResult> result = example.insert(partitionName, row_count);
+            InsertResultWrapper wrapper = new InsertResultWrapper(result.getData());
+            List<Long> ids = wrapper.getLongIDs();
+            deleteIds.add(ids.get(ran.nextInt(row_count.intValue())));
+        }
         example.getCollectionStatistics();
         example.getCollectionStatistics();
 
 
         example.createIndex();
         example.createIndex();
@@ -393,8 +412,10 @@ public class GeneralExample {
         example.getIndexBuildProgress();
         example.getIndexBuildProgress();
         example.getIndexState();
         example.getIndexState();
 
 
-        example.delete(partitionName, ID_FIELD + " in [105, 106, 107]");
-        example.query(ID_FIELD + " in [101, 102]");
+        String deleteExpr = ID_FIELD + " in " + deleteIds.toString();
+        example.delete(partitionName, deleteExpr);
+        String queryExpr = AGE_FIELD + " == 60";
+        example.query(queryExpr);
         example.search("");
         example.search("");
         example.calDistance();
         example.calDistance();
 
 

+ 47 - 0
src/main/java/io/milvus/Response/FieldDataWrapper.java

@@ -40,6 +40,53 @@ public class FieldDataWrapper {
         return (int) fieldData.getVectors().getDim();
         return (int) fieldData.getVectors().getDim();
     }
     }
 
 
+    /**
+     * Get row count of a field.
+     * * Throws {@link IllegalResponseException} if the field type is illegal.
+     *
+     * @return <code>long</code> row count of the field
+     */
+    public long getRowCount() throws IllegalResponseException {
+        DataType dt = fieldData.getType();
+        switch (dt) {
+            case FloatVector: {
+                int dim = getDim();
+                System.out.println(fieldData.getVectors().getFloatVector().getDataCount());
+                List<Float> data = fieldData.getVectors().getFloatVector().getDataList();
+                if (data.size() % dim != 0) {
+                    throw new IllegalResponseException("Returned float vector field data array size doesn't match dimension");
+                }
+
+                return data.size()/dim;
+            }
+            case BinaryVector: {
+                int dim = getDim();
+                ByteString data = fieldData.getVectors().getBinaryVector();
+                if (data.size() % dim != 0) {
+                    throw new IllegalResponseException("Returned binary vector field data array size doesn't match dimension");
+                }
+
+                return data.size()/dim;
+            }
+            case Int64:
+            case Int32:
+            case Int16:
+                return fieldData.getScalars().getLongData().getDataList().size();
+            case Int8:
+                return fieldData.getScalars().getIntData().getDataList().size();
+            case Bool:
+                return fieldData.getScalars().getBoolData().getDataList().size();
+            case Float:
+                return fieldData.getScalars().getFloatData().getDataList().size();
+            case Double:
+                return fieldData.getScalars().getDoubleData().getDataList().size();
+            case String:
+                return fieldData.getScalars().getStringData().getDataList().size();
+            default:
+                throw new IllegalResponseException("Unsupported data type returned by FieldData");
+        }
+    }
+
     /**
     /**
      * Return field data according to its type:
      * Return field data according to its type:
      *      float vector field return List<List<Float>>
      *      float vector field return List<List<Float>>

+ 60 - 9
src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java

@@ -122,12 +122,12 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 case UNRECOGNIZED:
                 case UNRECOGNIZED:
                     throw new ParamException("Cannot support this dataType:" + dataType);
                     throw new ParamException("Cannot support this dataType:" + dataType);
                 case Int64:
                 case Int64:
-                case Int32:
-                case Int16:
                     List<Long> longs = objects.stream().map(p -> (Long) p).collect(Collectors.toList());
                     List<Long> longs = objects.stream().map(p -> (Long) p).collect(Collectors.toList());
                     LongArray longArray = LongArray.newBuilder().addAllData(longs).build();
                     LongArray longArray = LongArray.newBuilder().addAllData(longs).build();
                     ScalarField scalarField1 = ScalarField.newBuilder().setLongData(longArray).build();
                     ScalarField scalarField1 = ScalarField.newBuilder().setLongData(longArray).build();
                     return builder.setFieldName(fieldName).setType(dataType).setScalars(scalarField1).build();
                     return builder.setFieldName(fieldName).setType(dataType).setScalars(scalarField1).build();
+                case Int32:
+                case Int16:
                 case Int8:
                 case Int8:
                     List<Integer> integers = objects.stream().map(p -> (Integer) p).collect(Collectors.toList());
                     List<Integer> integers = objects.stream().map(p -> (Integer) p).collect(Collectors.toList());
                     IntArray intArray = IntArray.newBuilder().addAllData(integers).build();
                     IntArray intArray = IntArray.newBuilder().addAllData(integers).build();
@@ -331,6 +331,47 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         });
         });
     }
     }
 
 
+    private R<Boolean> waitForIndex(String collectionName, String fieldName, long waitingInterval, long timeout) {
+        // This method use getIndexState() to check index state.
+        // If all index state become Finished, then we say the sync index action is finished.
+        // If waiting time exceed timeout, exist the circle
+        long tsBegin = System.currentTimeMillis();
+        while (true) {
+            long tsNow = System.currentTimeMillis();
+            if ((tsNow - tsBegin) >= timeout*1000) {
+                String msg = "Waiting index thread is timeout, index process may not be finished";
+                logWarning(msg);
+                return R.failed(R.Status.Success, msg);
+            }
+
+            GetIndexStateRequest request = GetIndexStateRequest.newBuilder()
+                    .setCollectionName(collectionName)
+                    .setFieldName(fieldName)
+                    .build();
+
+            GetIndexStateResponse response = blockingStub().getIndexState(request);
+            if (response.getState() == IndexState.Finished) {
+                break;
+            } else if (response.getState() == IndexState.Failed) {
+                String msg = "Index failed: " + response.getFailReason();
+                logError(msg);
+                return R.failed(R.Status.UnexpectedError, msg);
+            }
+
+            try {
+                String msg = "Waiting index, interval: " + waitingInterval + "ms. ";
+                logInfo(msg);
+                TimeUnit.MILLISECONDS.sleep(waitingInterval);
+            } catch (InterruptedException e) {
+                String msg = "Waiting index thread is interrupted, index process may not be finished";
+                logWarning(msg);
+                return R.failed(R.Status.Success, msg);
+            }
+        }
+
+        return R.failed(R.Status.Success, "Waiting index thread exist");
+    }
+
     ///////////////////// API implementation //////////////////////
     ///////////////////// API implementation //////////////////////
     @Override
     @Override
     public R<Boolean> hasCollection(@NonNull HasCollectionParam requestParam) {
     public R<Boolean> hasCollection(@NonNull HasCollectionParam requestParam) {
@@ -1067,15 +1108,25 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
             Status response = blockingStub().createIndex(createIndexRequest);
             Status response = blockingStub().createIndex(createIndexRequest);
 
 
-            if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("CreateIndexRequest successfully! Collection name:{}",
-                        requestParam.getCollectionName());
-                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
-            } else {
-                logError("CreateIndexRequest failed! Collection name:{}\n{}",
-                        requestParam.getCollectionName(), response.getReason());
+            if (response.getErrorCode() != ErrorCode.Success) {
+                logError("CreateIndexRequest failed! Collection name:{} Field name:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getFieldName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
             }
+
+            if (requestParam.isSyncMode()) {
+                R<Boolean> res = waitForIndex(requestParam.getCollectionName(), requestParam.getFieldName(),
+                        requestParam.getSyncWaitingInterval(), requestParam.getSyncWaitingTimeout());
+                if (res.getStatus() != R.Status.Success.getCode()) {
+                    logError("CreateIndexRequest failed in sync mode! Collection name:{} Field name:{}\n{}",
+                            requestParam.getCollectionName(), requestParam.getFieldName(), response.getReason());
+                    return R.failed(R.Status.valueOf(res.getStatus()), res.getMessage());
+                }
+            }
+
+            logInfo("CreateIndexRequest successfully! Collection name:{} Field name:{}",
+                    requestParam.getCollectionName(), requestParam.getFieldName());
+            return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
         } catch (StatusRuntimeException e) {
         } catch (StatusRuntimeException e) {
             logError("CreateIndexRequest RPC failed! Collection name:{}\n{}",
             logError("CreateIndexRequest RPC failed! Collection name:{}\n{}",
                     requestParam.getCollectionName(), e.getStatus().toString());
                     requestParam.getCollectionName(), e.getStatus().toString());

+ 3 - 0
src/main/java/io/milvus/param/Constant.java

@@ -44,4 +44,7 @@ public class Constant {
 
 
     // max value for waiting flushing collection/partition timeout,  unit: second
     // max value for waiting flushing collection/partition timeout,  unit: second
     public static final Long MAX_WAITING_FLUSHING_TIMEOUT = 300L;
     public static final Long MAX_WAITING_FLUSHING_TIMEOUT = 300L;
+
+    // max value for waiting create index interval, unit: millisecond
+    public static final Long MAX_WAITING_INDEX_INTERVAL = 2000L;
 }
 }

+ 38 - 1
src/main/java/io/milvus/param/dml/InsertParam.java

@@ -164,7 +164,7 @@ public class InsertParam {
                 } else if (field.getType() == DataType.BinaryVector) {
                 } else if (field.getType() == DataType.BinaryVector) {
                     for (Object obj : values) {
                     for (Object obj : values) {
                         if (!(obj instanceof ByteBuffer)) {
                         if (!(obj instanceof ByteBuffer)) {
-                            throw new ParamException("Binary vector's type must be ByteBuffer");
+                            throw new ParamException("Binary vector field's type must be ByteBuffer");
                         }
                         }
                     }
                     }
 
 
@@ -176,6 +176,43 @@ public class InsertParam {
                             throw new ParamException("Vector dimension must be equal");
                             throw new ParamException("Vector dimension must be equal");
                         }
                         }
                     }
                     }
+                } else if (field.getType() == DataType.Int64) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof Long)) {
+                            throw new ParamException("Int64 field value type must be Long");
+                        }
+                    }
+                } else if (field.getType() == DataType.Int32 || field.getType() == DataType.Int16
+                        || field.getType() == DataType.Int8 ) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof Integer)) {
+                            throw new ParamException("Int32/Int16/Int8 field value type must be Integer");
+                        }
+                    }
+                } else if (field.getType() == DataType.Float) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof Float)) {
+                            throw new ParamException("Float field value type must be Float");
+                        }
+                    }
+                } else if (field.getType() == DataType.Double) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof Double)) {
+                            throw new ParamException("Double field value type must be Double");
+                        }
+                    }
+                } else if (field.getType() == DataType.Bool) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof Boolean)) {
+                            throw new ParamException("Bool field value type must be Boolean");
+                        }
+                    }
+                } else if (field.getType() == DataType.String) {
+                    for (Object obj : values) {
+                        if (!(obj instanceof String)) {
+                            throw new ParamException("String field value type must be String");
+                        }
+                    }
                 }
                 }
             }
             }
 
 

+ 74 - 0
src/main/java/io/milvus/param/index/CreateIndexParam.java

@@ -25,6 +25,7 @@ import io.milvus.param.IndexType;
 import io.milvus.param.MetricType;
 import io.milvus.param.MetricType;
 import io.milvus.param.ParamUtils;
 import io.milvus.param.ParamUtils;
 
 
+import io.milvus.param.collection.LoadCollectionParam;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.NonNull;
 import lombok.NonNull;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -38,6 +39,9 @@ public class CreateIndexParam {
     private final String collectionName;
     private final String collectionName;
     private final String fieldName;
     private final String fieldName;
     private final Map<String, String> extraParam = new HashMap<>();
     private final Map<String, String> extraParam = new HashMap<>();
+    private final boolean syncMode;
+    private final long syncWaitingInterval;
+    private final long syncWaitingTimeout;
 
 
     private CreateIndexParam(@NonNull Builder builder) {
     private CreateIndexParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.collectionName = builder.collectionName;
@@ -45,6 +49,9 @@ public class CreateIndexParam {
         this.extraParam.put(Constant.INDEX_TYPE, builder.indexType.name());
         this.extraParam.put(Constant.INDEX_TYPE, builder.indexType.name());
         this.extraParam.put(Constant.METRIC_TYPE, builder.metricType.name());
         this.extraParam.put(Constant.METRIC_TYPE, builder.metricType.name());
         this.extraParam.put(Constant.PARAMS, builder.extraParam);
         this.extraParam.put(Constant.PARAMS, builder.extraParam);
+        this.syncMode = builder.syncMode;
+        this.syncWaitingInterval = builder.syncWaitingInterval;
+        this.syncWaitingTimeout = builder.syncWaitingTimeout;
     }
     }
 
 
     public static Builder newBuilder() {
     public static Builder newBuilder() {
@@ -61,6 +68,20 @@ public class CreateIndexParam {
         private MetricType metricType;
         private MetricType metricType;
         private String extraParam;
         private String extraParam;
 
 
+        // syncMode:
+        //   Default behavior is sync mode, createIndex() return after the index successfully created.
+        private Boolean syncMode = Boolean.TRUE;
+
+        // syncWaitingDuration:
+        //   When syncMode is ture, createIndex() return after the index successfully created.
+        //   this value control the waiting interval. Unit: millisecond. Default value: 500 milliseconds.
+        private Long syncWaitingInterval = 500L;
+
+        // syncWaitingTimeout:
+        //   When syncMode is ture, createIndex() return after the index successfully created.
+        //   this value control the waiting timeout. Unit: second. Default value: 600 seconds.
+        private Long syncWaitingTimeout = 600L;
+
         private Builder() {
         private Builder() {
         }
         }
 
 
@@ -122,6 +143,46 @@ public class CreateIndexParam {
             return this;
             return this;
         }
         }
 
 
+        /**
+         * Set to sync mode.
+         * With sync mode, the client side will keep waiting until all segments of the collection successfully indexed.
+         *
+         * If not sync mode, client will return at once after the createIndex() is called.
+         *
+         * @param syncMode <code>Boolean.TRUE</code> is sync mode, Bollean.FALSE is not
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncMode(@NonNull Boolean syncMode) {
+            this.syncMode = syncMode;
+            return this;
+        }
+
+        /**
+         * Set waiting interval in sync mode. In sync mode, the client will constantly check index state by interval.
+         * Interval must be larger than zero, and cannot be larger than Constant.MAX_WAITING_INDEX_INTERVAL.
+         * @see Constant
+         *
+         * @param milliseconds interval
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncWaitingInterval(@NonNull Long milliseconds) {
+            this.syncWaitingInterval = milliseconds;
+            return this;
+        }
+
+        /**
+         * Set time out value for sync mode.
+         * Time out value must be larger than zero. No upper limit. Default value is 600 seconds.
+         * @see Constant
+         *
+         * @param seconds time out value for sync mode
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncWaitingTimeout(@NonNull Long seconds) {
+            this.syncWaitingTimeout = seconds;
+            return this;
+        }
+
         /**
         /**
          * Verify parameters and create a new <code>CreateIndexParam</code> instance.
          * Verify parameters and create a new <code>CreateIndexParam</code> instance.
          *
          *
@@ -139,6 +200,19 @@ public class CreateIndexParam {
                 throw new ParamException("Metric type is required");
                 throw new ParamException("Metric type is required");
             }
             }
 
 
+            if (syncMode == Boolean.TRUE) {
+                if (syncWaitingInterval <= 0) {
+                    throw new ParamException("Sync index waiting interval must be larger than zero");
+                } else if (syncWaitingInterval > Constant.MAX_WAITING_LOADING_INTERVAL) {
+                    throw new ParamException("Sync index waiting interval cannot be larger than "
+                            + Constant.MAX_WAITING_LOADING_INTERVAL.toString() + " milliseconds");
+                }
+
+                if (syncWaitingTimeout <= 0) {
+                    throw new ParamException("Sync index waiting timeout must be larger than zero");
+                }
+            }
+
             ParamUtils.CheckNullEmptyString(extraParam, "Index extra param");
             ParamUtils.CheckNullEmptyString(extraParam, "Index extra param");
 
 
             return new CreateIndexParam(this);
             return new CreateIndexParam(this);

+ 33 - 9
src/test/java/io/milvus/client/MilvusClientDockerTest.java

@@ -21,15 +21,13 @@ package io.milvus.client;
 
 
 import io.milvus.Response.*;
 import io.milvus.Response.*;
 import io.milvus.grpc.*;
 import io.milvus.grpc.*;
-import io.milvus.param.ConnectParam;
-import io.milvus.param.MetricType;
-import io.milvus.param.R;
-import io.milvus.param.RpcStatus;
+import io.milvus.param.*;
 import io.milvus.param.collection.*;
 import io.milvus.param.collection.*;
 import io.milvus.param.dml.InsertParam;
 import io.milvus.param.dml.InsertParam;
 import io.milvus.param.dml.QueryParam;
 import io.milvus.param.dml.QueryParam;
 import io.milvus.param.dml.SearchParam;
 import io.milvus.param.dml.SearchParam;
 
 
+import io.milvus.param.index.CreateIndexParam;
 import org.apache.commons.text.RandomStringGenerator;
 import org.apache.commons.text.RandomStringGenerator;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.Logger;
@@ -171,10 +169,11 @@ public class MilvusClientDockerTest {
         String randomCollectionName = generator.generate(10);
         String randomCollectionName = generator.generate(10);
 
 
         // collection schema
         // collection schema
-        String field1Name = "int_field";
+        String field1Name = "long_field";
         String field2Name = "vec_field";
         String field2Name = "vec_field";
         String field3Name = "bool_field";
         String field3Name = "bool_field";
         String field4Name = "double_field";
         String field4Name = "double_field";
+        String field5Name = "int_field";
         List<FieldType> fieldsSchema = new ArrayList<>();
         List<FieldType> fieldsSchema = new ArrayList<>();
         fieldsSchema.add(FieldType.newBuilder()
         fieldsSchema.add(FieldType.newBuilder()
                 .withPrimaryKey(true)
                 .withPrimaryKey(true)
@@ -203,6 +202,12 @@ public class MilvusClientDockerTest {
                 .withDescription("weight")
                 .withDescription("weight")
                 .build());
                 .build());
 
 
+        fieldsSchema.add(FieldType.newBuilder()
+                .withDataType(DataType.Int32)
+                .withName(field5Name)
+                .withDescription("age")
+                .build());
+
         // create collection
         // create collection
         CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
         CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withCollectionName(randomCollectionName)
@@ -218,10 +223,12 @@ public class MilvusClientDockerTest {
         List<Long> ids = new ArrayList<>();
         List<Long> ids = new ArrayList<>();
         List<Boolean> genders = new ArrayList<>();
         List<Boolean> genders = new ArrayList<>();
         List<Double> weights = new ArrayList<>();
         List<Double> weights = new ArrayList<>();
+        List<Integer> ages = new ArrayList<>();
         for (long i = 0L; i < rowCount; ++i) {
         for (long i = 0L; i < rowCount; ++i) {
             ids.add(i);
             ids.add(i);
             genders.add(i%3 == 0 ? Boolean.TRUE : Boolean.FALSE);
             genders.add(i%3 == 0 ? Boolean.TRUE : Boolean.FALSE);
             weights.add((double) (i / 100));
             weights.add((double) (i / 100));
+            ages.add((int)i%99);
         }
         }
         List<List<Float>> vectors = generateFloatVectors(rowCount);
         List<List<Float>> vectors = generateFloatVectors(rowCount);
 
 
@@ -230,6 +237,7 @@ public class MilvusClientDockerTest {
         fieldsInsert.add(new InsertParam.Field(field2Name, DataType.FloatVector, vectors));
         fieldsInsert.add(new InsertParam.Field(field2Name, DataType.FloatVector, vectors));
         fieldsInsert.add(new InsertParam.Field(field3Name, DataType.Bool, genders));
         fieldsInsert.add(new InsertParam.Field(field3Name, DataType.Bool, genders));
         fieldsInsert.add(new InsertParam.Field(field4Name, DataType.Double, weights));
         fieldsInsert.add(new InsertParam.Field(field4Name, DataType.Double, weights));
+        fieldsInsert.add(new InsertParam.Field(field5Name, DataType.Int8, ages));
 
 
         InsertParam insertParam = InsertParam.newBuilder()
         InsertParam insertParam = InsertParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withCollectionName(randomCollectionName)
@@ -251,6 +259,21 @@ public class MilvusClientDockerTest {
         GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(statR.getData());
         GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(statR.getData());
         System.out.println("Collection row count: " + stat.GetRowCount());
         System.out.println("Collection row count: " + stat.GetRowCount());
 
 
+        // create index
+        CreateIndexParam param = CreateIndexParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFieldName(field2Name)
+                .withIndexType(IndexType.IVF_FLAT)
+                .withMetricType(MetricType.L2)
+                .withExtraParam("{\"nlist\":64}")
+                .withSyncMode(Boolean.TRUE)
+                .withSyncWaitingInterval(500L)
+                .withSyncWaitingTimeout(30L)
+                .build();
+
+        R<RpcStatus> createIndexR = client.createIndex(param);
+        assertEquals(createIndexR.getStatus().intValue(), R.Status.Success.getCode());
+
         // load collection
         // load collection
         R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.newBuilder()
         R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withCollectionName(randomCollectionName)
@@ -270,7 +293,7 @@ public class MilvusClientDockerTest {
             compareWeights.add(weights.get(randomIndex));
             compareWeights.add(weights.get(randomIndex));
         }
         }
         String expr = field1Name + " in " + queryIDs.toString();
         String expr = field1Name + " in " + queryIDs.toString();
-        List<String> outputFields = Arrays.asList(field1Name, field2Name, field3Name, field4Name);
+        List<String> outputFields = Arrays.asList(field1Name, field2Name, field3Name, field4Name, field4Name);
         QueryParam queryParam = QueryParam.newBuilder()
         QueryParam queryParam = QueryParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withCollectionName(randomCollectionName)
                 .withExpr(expr)
                 .withExpr(expr)
@@ -278,14 +301,14 @@ public class MilvusClientDockerTest {
                 .build();
                 .build();
 
 
         R<QueryResults> queryR= client.query(queryParam);
         R<QueryResults> queryR= client.query(queryParam);
-//        System.out.println(queryR);
         assertEquals(queryR.getStatus().intValue(), R.Status.Success.getCode());
         assertEquals(queryR.getStatus().intValue(), R.Status.Success.getCode());
 
 
         // verify query result
         // verify query result
         QueryResultsWrapper queryResultsWrapper = new QueryResultsWrapper(queryR.getData());
         QueryResultsWrapper queryResultsWrapper = new QueryResultsWrapper(queryR.getData());
         for (String fieldName : outputFields) {
         for (String fieldName : outputFields) {
-            System.out.println("Query data of " + fieldName);
-            System.out.println(queryResultsWrapper.getFieldWrapper(fieldName).getFieldData());
+            FieldDataWrapper wrapper = queryResultsWrapper.getFieldWrapper(fieldName);
+            System.out.println("Query data of " + fieldName + ", row count: " + wrapper.getRowCount());
+            System.out.println(wrapper.getFieldData());
         }
         }
 
 
         if (outputFields.contains(field1Name)) {
         if (outputFields.contains(field1Name)) {
@@ -340,6 +363,7 @@ public class MilvusClientDockerTest {
                 .withTopK(topK)
                 .withTopK(topK)
                 .withVectors(targetVectors)
                 .withVectors(targetVectors)
                 .withVectorFieldName(field2Name)
                 .withVectorFieldName(field2Name)
+                .withParams("{\"nprobe\":8}")
                 .build();
                 .build();
 
 
         R<SearchResults> searchR = client.search(searchParam);
         R<SearchResults> searchR = client.search(searchParam);

+ 82 - 494
src/test/java/io/milvus/client/MilvusServiceClientTest.java

@@ -36,6 +36,8 @@ import io.milvus.server.MockMilvusServer;
 import io.milvus.server.MockMilvusServerImpl;
 import io.milvus.server.MockMilvusServerImpl;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Test;
 
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
@@ -65,6 +67,35 @@ class MilvusServiceClientTest {
         return new MilvusServiceClient(connectParam);
         return new MilvusServiceClient(connectParam);
     }
     }
 
 
+    private <T, P> void testFuncByName(String funcName, T param) {
+        try {
+            Class<?> clientClass = MilvusServiceClient.class;
+            Method testFunc = clientClass.getMethod(funcName, param.getClass());
+
+            // start mock server
+            MockMilvusServer server = startServer();
+            MilvusServiceClient client = startClient();
+
+            // test return ok with correct input
+            R<P> resp = (R<P>) testFunc.invoke(client, param);
+            assertEquals(R.Status.Success.getCode(), resp.getStatus());
+
+            // stop mock server
+            server.stop();
+
+            // test return error without server
+            resp = (R<P>) testFunc.invoke(client, param);
+            assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
+
+            // test return error when client channel is shutdown
+            client.close();
+            resp = (R<P>) testFunc.invoke(client, param);
+            assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+            System.out.println(e.toString());
+        }
+    }
+
     @Test
     @Test
     void connectParam() {
     void connectParam() {
         System.out.println(System.getProperty("os.name"));
         System.out.println(System.getProperty("os.name"));
@@ -180,25 +211,7 @@ class MilvusServiceClientTest {
                 .addFieldType(fieldType1)
                 .addFieldType(fieldType1)
                 .build();
                 .build();
 
 
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
-        // test return ok with correct input
-        R<RpcStatus> resp = client.createCollection(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.createCollection(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.createCollection(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("createCollection", param);
     }
     }
 
 
     @Test
     @Test
@@ -213,29 +226,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void describeCollection() {
     void describeCollection() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DescribeCollectionParam param = DescribeCollectionParam.newBuilder()
         DescribeCollectionParam param = DescribeCollectionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<DescribeCollectionResponse> resp = client.describeCollection(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.describeCollection(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.describeCollection(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("describeCollection", param);
     }
     }
 
 
     @Test
     @Test
@@ -250,29 +245,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void dropCollection() {
     void dropCollection() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DropCollectionParam param = DropCollectionParam.newBuilder()
         DropCollectionParam param = DropCollectionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.dropCollection(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.dropCollection(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.dropCollection(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("dropCollection", param);
     }
     }
 
 
     @Test
     @Test
@@ -298,7 +275,7 @@ class MilvusServiceClientTest {
                 .build();
                 .build();
 
 
         // test return ok with correct input
         // test return ok with correct input
-        final Long segmentID = 2021L;
+        final long segmentID = 2021L;
         mockServerImpl.setFlushResponse(FlushResponse.newBuilder()
         mockServerImpl.setFlushResponse(FlushResponse.newBuilder()
                 .putCollSegIDs(collectionName, LongArray.newBuilder().addData(segmentID).build())
                 .putCollSegIDs(collectionName, LongArray.newBuilder().addData(segmentID).build())
                 .build());
                 .build());
@@ -311,8 +288,9 @@ class MilvusServiceClientTest {
 
 
         new Thread(() -> {
         new Thread(() -> {
             try {
             try {
-                    TimeUnit.SECONDS.sleep(1);
+                TimeUnit.SECONDS.sleep(1);
             } catch (InterruptedException e) {
             } catch (InterruptedException e) {
+                System.out.println(e.toString());
             }
             }
             mockServerImpl.setGetPersistentSegmentInfoResponse(GetPersistentSegmentInfoResponse.newBuilder()
             mockServerImpl.setGetPersistentSegmentInfoResponse(GetPersistentSegmentInfoResponse.newBuilder()
                     .addInfos(PersistentSegmentInfo.newBuilder()
                     .addInfos(PersistentSegmentInfo.newBuilder()
@@ -350,29 +328,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void hasCollection() {
     void hasCollection() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         HasCollectionParam param = HasCollectionParam.newBuilder()
         HasCollectionParam param = HasCollectionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<Boolean> resp = client.hasCollection(param);
-        assertFalse(resp.getData());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.hasCollection(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.hasCollection(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("hasCollection", param);
     }
     }
 
 
     @Test
     @Test
@@ -505,29 +465,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void releaseCollection() {
     void releaseCollection() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         ReleaseCollectionParam param = ReleaseCollectionParam.newBuilder()
         ReleaseCollectionParam param = ReleaseCollectionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.releaseCollection(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.releaseCollection(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.releaseCollection(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("releaseCollection", param);
     }
     }
 
 
     @Test
     @Test
@@ -560,30 +502,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void showCollections() {
     void showCollections() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         ShowCollectionsParam param = ShowCollectionsParam.newBuilder()
         ShowCollectionsParam param = ShowCollectionsParam.newBuilder()
                 .addCollectionName("collection1")
                 .addCollectionName("collection1")
                 .addCollectionName("collection2")
                 .addCollectionName("collection2")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<ShowCollectionsResponse> resp = client.showCollections(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.showCollections(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.showCollections(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("showCollections", param);
     }
     }
 
 
     @Test
     @Test
@@ -655,30 +579,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void createPartition() {
     void createPartition() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         CreatePartitionParam param = CreatePartitionParam.newBuilder()
         CreatePartitionParam param = CreatePartitionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withPartitionName("partition1")
                 .withPartitionName("partition1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.createPartition(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.createPartition(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.createPartition(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("createPartition", param);
     }
     }
 
 
     @Test
     @Test
@@ -699,30 +605,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void dropPartition() {
     void dropPartition() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DropPartitionParam param = DropPartitionParam.newBuilder()
         DropPartitionParam param = DropPartitionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withPartitionName("partition1")
                 .withPartitionName("partition1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.dropPartition(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.dropPartition(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.dropPartition(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("dropPartition", param);
     }
     }
 
 
     @Test
     @Test
@@ -743,30 +631,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void hasPartition() {
     void hasPartition() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         HasPartitionParam param = HasPartitionParam.newBuilder()
         HasPartitionParam param = HasPartitionParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withPartitionName("partition1")
                 .withPartitionName("partition1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<Boolean> resp = client.hasPartition(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.hasPartition(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.hasPartition(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("hasPartition", param);
     }
     }
 
 
     @Test
     @Test
@@ -930,30 +800,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void releasePartitions() {
     void releasePartitions() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         ReleasePartitionsParam param = ReleasePartitionsParam.newBuilder()
         ReleasePartitionsParam param = ReleasePartitionsParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .addPartitionName("partition1")
                 .addPartitionName("partition1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.releasePartitions(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.releasePartitions(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.releasePartitions(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("releasePartitions", param);
     }
     }
 
 
     @Test
     @Test
@@ -974,30 +826,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getPartitionStatistics() {
     void getPartitionStatistics() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetPartitionStatisticsParam param = GetPartitionStatisticsParam.newBuilder()
         GetPartitionStatisticsParam param = GetPartitionStatisticsParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withPartitionName("partition1")
                 .withPartitionName("partition1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetPartitionStatisticsResponse> resp = client.getPartitionStatistics(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getPartitionStatistics(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getPartitionStatistics(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getPartitionStatistics", param);
     }
     }
 
 
     @Test
     @Test
@@ -1038,29 +872,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void showPartitions() {
     void showPartitions() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         ShowPartitionsParam param = ShowPartitionsParam.newBuilder()
         ShowPartitionsParam param = ShowPartitionsParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<ShowPartitionsResponse> resp = client.showPartitions(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.showPartitions(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.showPartitions(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("showPartitions", param);
     }
     }
 
 
     @Test
     @Test
@@ -1081,30 +897,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void createAlias() {
     void createAlias() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         CreateAliasParam param = CreateAliasParam.newBuilder()
         CreateAliasParam param = CreateAliasParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withAlias("alias1")
                 .withAlias("alias1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.createAlias(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.createAlias(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.createAlias(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("createAlias", param);
     }
     }
 
 
     @Test
     @Test
@@ -1118,29 +916,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void dropAlias() {
     void dropAlias() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DropAliasParam param = DropAliasParam.newBuilder()
         DropAliasParam param = DropAliasParam.newBuilder()
                 .withAlias("alias1")
                 .withAlias("alias1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.dropAlias(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.dropAlias(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.dropAlias(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("dropAlias", param);
     }
     }
 
 
     @Test
     @Test
@@ -1161,30 +941,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void alterAlias() {
     void alterAlias() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         AlterAliasParam param = AlterAliasParam.newBuilder()
         AlterAliasParam param = AlterAliasParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withAlias("alias1")
                 .withAlias("alias1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<RpcStatus> resp = client.alterAlias(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.alterAlias(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.alterAlias(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("alterAlias", param);
     }
     }
 
 
     @Test
     @Test
@@ -1242,12 +1004,33 @@ class MilvusServiceClientTest {
         MockMilvusServer server = startServer();
         MockMilvusServer server = startServer();
         MilvusServiceClient client = startClient();
         MilvusServiceClient client = startClient();
 
 
+        // test return ok for sync mode loading
+        mockServerImpl.setGetIndexStateResponse(GetIndexStateResponse.newBuilder()
+                .setState(IndexState.InProgress)
+                .build());
+
+        new Thread(() -> {
+            try {
+                TimeUnit.SECONDS.sleep(1);
+                mockServerImpl.setGetIndexStateResponse(GetIndexStateResponse.newBuilder()
+                        .setState(IndexState.Finished)
+                        .build());
+            } catch (InterruptedException e) {
+                mockServerImpl.setGetIndexStateResponse(GetIndexStateResponse.newBuilder()
+                        .setState(IndexState.Finished)
+                        .build());
+            }
+        }, "RefreshIndexState").start();
+
         CreateIndexParam param = CreateIndexParam.newBuilder()
         CreateIndexParam param = CreateIndexParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withFieldName("field1")
                 .withFieldName("field1")
                 .withIndexType(IndexType.IVF_FLAT)
                 .withIndexType(IndexType.IVF_FLAT)
                 .withMetricType(MetricType.L2)
                 .withMetricType(MetricType.L2)
                 .withExtraParam("dummy")
                 .withExtraParam("dummy")
+                .withSyncMode(Boolean.TRUE)
+                .withSyncWaitingInterval(500L)
+                .withSyncWaitingTimeout(2L)
                 .build();
                 .build();
 
 
         // test return ok with correct input
         // test return ok with correct input
@@ -1285,30 +1068,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void describeIndex() {
     void describeIndex() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DescribeIndexParam param = DescribeIndexParam.newBuilder()
         DescribeIndexParam param = DescribeIndexParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withFieldName("field1")
                 .withFieldName("field1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<DescribeIndexResponse> resp = client.describeIndex(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.describeIndex(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.describeIndex(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("describeIndex", param);
     }
     }
 
 
     @Test
     @Test
@@ -1329,30 +1094,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getIndexState() {
     void getIndexState() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetIndexStateParam param = GetIndexStateParam.newBuilder()
         GetIndexStateParam param = GetIndexStateParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withFieldName("field1")
                 .withFieldName("field1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetIndexStateResponse> resp = client.getIndexState(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getIndexState(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getIndexState(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getIndexState", param);
     }
     }
 
 
     @Test
     @Test
@@ -1366,29 +1113,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getIndexBuildProgress() {
     void getIndexBuildProgress() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetIndexBuildProgressParam param = GetIndexBuildProgressParam.newBuilder()
         GetIndexBuildProgressParam param = GetIndexBuildProgressParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetIndexBuildProgressResponse> resp = client.getIndexBuildProgress(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getIndexBuildProgress(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getIndexBuildProgress(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getIndexBuildProgress", param);
     }
     }
 
 
     @Test
     @Test
@@ -1409,29 +1138,12 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void dropIndex() {
     void dropIndex() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DropIndexParam param = DropIndexParam.newBuilder()
         DropIndexParam param = DropIndexParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withFieldName("field1")
                 .withFieldName("field1")
                 .build();
                 .build();
-        // test return ok with correct input
-        R<RpcStatus> resp = client.dropIndex(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
 
 
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.dropIndex(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.dropIndex(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("dropIndex", param);
     }
     }
 
 
     @Test
     @Test
@@ -1549,10 +1261,6 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void insert() {
     void insert() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         List<InsertParam.Field> fields = new ArrayList<>();
         List<InsertParam.Field> fields = new ArrayList<>();
         List<Long> ids = new ArrayList<>();
         List<Long> ids = new ArrayList<>();
         List<Integer> nVal = new ArrayList<>();
         List<Integer> nVal = new ArrayList<>();
@@ -1590,21 +1298,7 @@ class MilvusServiceClientTest {
                 .withFields(fields)
                 .withFields(fields)
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<MutationResult> resp = client.insert(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.insert(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.insert(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("insert", param);
     }
     }
 
 
     @Test
     @Test
@@ -1625,31 +1319,13 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void delete() {
     void delete() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         DeleteParam param = DeleteParam.newBuilder()
         DeleteParam param = DeleteParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .withPartitionName("partition1")
                 .withPartitionName("partition1")
                 .withExpr("dummy")
                 .withExpr("dummy")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<MutationResult> resp = client.delete(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.delete(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.delete(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("delete", param);
     }
     }
 
 
     @Test
     @Test
@@ -1833,6 +1509,7 @@ class MilvusServiceClientTest {
                 .withVectors(bVectors)
                 .withVectors(bVectors)
                 .withExpr("dummy")
                 .withExpr("dummy")
                 .build();
                 .build();
+
         resp = client.search(param);
         resp = client.search(param);
         assertEquals(R.Status.Success.getCode(), resp.getStatus());
         assertEquals(R.Status.Success.getCode(), resp.getStatus());
 
 
@@ -1873,10 +1550,6 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void query() {
     void query() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         List<String> partitions = Collections.singletonList("partition1");
         List<String> partitions = Collections.singletonList("partition1");
         List<String> outputFields = Collections.singletonList("field2");
         List<String> outputFields = Collections.singletonList("field2");
         QueryParam param = QueryParam.newBuilder()
         QueryParam param = QueryParam.newBuilder()
@@ -1885,21 +1558,8 @@ class MilvusServiceClientTest {
                 .withOutFields(outputFields)
                 .withOutFields(outputFields)
                 .withExpr("dummy")
                 .withExpr("dummy")
                 .build();
                 .build();
-        // test return ok with correct input
-        R<QueryResults> resp = client.query(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.query(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
 
 
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.query(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("query", param);
     }
     }
 
 
     @Test
     @Test
@@ -1961,10 +1621,6 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void calcDistance() {
     void calcDistance() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         List<List<Float>> vectorsLeft = new ArrayList<>();
         List<List<Float>> vectorsLeft = new ArrayList<>();
         List<List<Float>> vectorsRight = new ArrayList<>();
         List<List<Float>> vectorsRight = new ArrayList<>();
         List<Float> vector1 = Collections.singletonList(0.1F);
         List<Float> vector1 = Collections.singletonList(0.1F);
@@ -1977,21 +1633,7 @@ class MilvusServiceClientTest {
                 .withMetricType(MetricType.L2)
                 .withMetricType(MetricType.L2)
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<CalcDistanceResults> resp = client.calcDistance(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.calcDistance(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.calcDistance(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("calcDistance", param);
     }
     }
 
 
     @Test
     @Test
@@ -2005,29 +1647,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getMetrics() {
     void getMetrics() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetMetricsParam param = GetMetricsParam.newBuilder()
         GetMetricsParam param = GetMetricsParam.newBuilder()
                 .withRequest("{}")
                 .withRequest("{}")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetMetricsResponse> resp = client.getMetrics(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getMetrics(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getMetrics(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getMetrics", param);
     }
     }
 
 
     @Test
     @Test
@@ -2041,29 +1665,11 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getPersistentSegmentInfo() {
     void getPersistentSegmentInfo() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetPersistentSegmentInfoParam param = GetPersistentSegmentInfoParam.newBuilder()
         GetPersistentSegmentInfoParam param = GetPersistentSegmentInfoParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetPersistentSegmentInfoResponse> resp = client.getPersistentSegmentInfo(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getPersistentSegmentInfo(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getPersistentSegmentInfo(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getPersistentSegmentInfo", param);
     }
     }
 
 
     @Test
     @Test
@@ -2078,28 +1684,10 @@ class MilvusServiceClientTest {
 
 
     @Test
     @Test
     void getQuerySegmentInfo() {
     void getQuerySegmentInfo() {
-        // start mock server
-        MockMilvusServer server = startServer();
-        MilvusServiceClient client = startClient();
-
         GetQuerySegmentInfoParam param = GetQuerySegmentInfoParam.newBuilder()
         GetQuerySegmentInfoParam param = GetQuerySegmentInfoParam.newBuilder()
                 .withCollectionName("collection1")
                 .withCollectionName("collection1")
                 .build();
                 .build();
 
 
-        // test return ok with correct input
-        R<GetQuerySegmentInfoResponse> resp = client.getQuerySegmentInfo(param);
-        assertEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // stop mock server
-        server.stop();
-
-        // test return error without server
-        resp = client.getQuerySegmentInfo(param);
-        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
-
-        // test return error when client channel is shutdown
-        client.close();
-        resp = client.getQuerySegmentInfo(param);
-        assertEquals(R.Status.ClientNotConnected.getCode(), resp.getStatus());
+        testFuncByName("getQuerySegmentInfo", param);
     }
     }
 }
 }