فهرست منبع

Implement load and flush (#221)

* Implement load and flush

Signed-off-by: yhmo <yihua.mo@zilliz.com>

* Add for java doc

Signed-off-by: yhmo <yihua.mo@zilliz.com>
groot 3 سال پیش
والد
کامیت
61b40311d0
61فایلهای تغییر یافته به همراه4982 افزوده شده و 2235 حذف شده
  1. 18 0
      .github/mergify.yml
  2. 26 0
      .github/workflows/maven.yml
  3. 9 0
      OWNERS
  4. 120 158
      examples/main/io/milvus/GeneralExample.java
  5. 7 1
      pom.xml
  6. 104 0
      src/main/java/io/milvus/Response/FieldDataWrapper.java
  7. 36 0
      src/main/java/io/milvus/Response/GetCollStatResponseWrapper.java
  8. 57 0
      src/main/java/io/milvus/Response/InsertResultWrapper.java
  9. 40 0
      src/main/java/io/milvus/Response/QueryResultsWrapper.java
  10. 114 0
      src/main/java/io/milvus/Response/SearchResultsWrapper.java
  11. 1072 553
      src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java
  12. 111 45
      src/main/java/io/milvus/client/MilvusClient.java
  13. 5 10
      src/main/java/io/milvus/client/MilvusServiceClient.java
  14. 3 0
      src/main/java/io/milvus/exception/ClientNotConnectedException.java
  15. 33 0
      src/main/java/io/milvus/exception/IllegalResponseException.java
  16. 3 0
      src/main/java/io/milvus/exception/MilvusException.java
  17. 3 0
      src/main/java/io/milvus/exception/ParamException.java
  18. 99 32
      src/main/java/io/milvus/param/ConnectParam.java
  19. 16 5
      src/main/java/io/milvus/param/Constant.java
  20. 0 65
      src/main/java/io/milvus/param/Control/GetMetricsRequestParam.java
  21. 5 1
      src/main/java/io/milvus/param/IndexType.java
  22. 5 1
      src/main/java/io/milvus/param/MetricType.java
  23. 10 0
      src/main/java/io/milvus/param/ParamUtils.java
  24. 42 2
      src/main/java/io/milvus/param/R.java
  25. 6 3
      src/main/java/io/milvus/param/RpcStatus.java
  26. 44 14
      src/main/java/io/milvus/param/alias/AlterAliasParam.java
  27. 44 14
      src/main/java/io/milvus/param/alias/CreateAliasParam.java
  28. 36 9
      src/main/java/io/milvus/param/alias/DropAliasParam.java
  29. 66 31
      src/main/java/io/milvus/param/collection/CreateCollectionParam.java
  30. 33 12
      src/main/java/io/milvus/param/collection/DescribeCollectionParam.java
  31. 33 12
      src/main/java/io/milvus/param/collection/DropCollectionParam.java
  32. 72 51
      src/main/java/io/milvus/param/collection/FieldType.java
  33. 166 0
      src/main/java/io/milvus/param/collection/FlushParam.java
  34. 51 11
      src/main/java/io/milvus/param/collection/GetCollectionStatisticsParam.java
  35. 34 13
      src/main/java/io/milvus/param/collection/HasCollectionParam.java
  36. 112 11
      src/main/java/io/milvus/param/collection/LoadCollectionParam.java
  37. 33 12
      src/main/java/io/milvus/param/collection/ReleaseCollectionParam.java
  38. 50 18
      src/main/java/io/milvus/param/collection/ShowCollectionsParam.java
  39. 89 0
      src/main/java/io/milvus/param/control/GetMetricsParam.java
  40. 35 12
      src/main/java/io/milvus/param/control/GetPersistentSegmentInfoParam.java
  41. 35 12
      src/main/java/io/milvus/param/control/GetQuerySegmentInfoParam.java
  42. 66 34
      src/main/java/io/milvus/param/dml/CalcDistanceParam.java
  43. 53 26
      src/main/java/io/milvus/param/dml/DeleteParam.java
  44. 83 42
      src/main/java/io/milvus/param/dml/InsertParam.java
  45. 61 32
      src/main/java/io/milvus/param/dml/QueryParam.java
  46. 120 65
      src/main/java/io/milvus/param/dml/SearchParam.java
  47. 67 24
      src/main/java/io/milvus/param/index/CreateIndexParam.java
  48. 42 15
      src/main/java/io/milvus/param/index/DescribeIndexParam.java
  49. 42 15
      src/main/java/io/milvus/param/index/DropIndexParam.java
  50. 34 10
      src/main/java/io/milvus/param/index/GetIndexBuildProgressParam.java
  51. 42 15
      src/main/java/io/milvus/param/index/GetIndexStateParam.java
  52. 42 17
      src/main/java/io/milvus/param/partition/CreatePartitionParam.java
  53. 42 17
      src/main/java/io/milvus/param/partition/DropPartitionParam.java
  54. 42 17
      src/main/java/io/milvus/param/partition/GetPartitionStatisticsParam.java
  55. 42 17
      src/main/java/io/milvus/param/partition/HasPartitionParam.java
  56. 138 20
      src/main/java/io/milvus/param/partition/LoadPartitionsParam.java
  57. 58 20
      src/main/java/io/milvus/param/partition/ReleasePartitionsParam.java
  58. 77 11
      src/main/java/io/milvus/param/partition/ShowPartitionsParam.java
  59. 294 57
      src/test/java/io/milvus/client/MilvusClientDockerTest.java
  60. 756 664
      src/test/java/io/milvus/client/MilvusServiceClientTest.java
  61. 4 9
      src/test/java/io/milvus/server/MockMilvusServer.java

+ 18 - 0
.github/mergify.yml

@@ -0,0 +1,18 @@
+pull_request_rules:
+  - name: Test passed for code changed
+    conditions:
+      - base=master
+      - "status-success=Java CI with Maven / build (pull_request)"
+    actions:
+      label:
+        add:
+          - ci-passed
+
+  - name: Remove ci-passed when code check failed
+    conditions:
+      - base=master
+      - "check-failure=Java CI with Maven / build (pull_request)"
+    actions:
+      label:
+        remove:
+          - ci-passed

+ 26 - 0
.github/workflows/maven.yml

@@ -0,0 +1,26 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
+
+name: Java CI with Maven
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+      - name: Setup JDK 11
+        uses: actions/setup-java@v2
+        with:
+          java-version: '11'
+          distribution: 'adopt'
+          cache: maven
+      - name: Build with Maven
+        run: mvn --batch-mode --update-snapshots verify

+ 9 - 0
OWNERS

@@ -0,0 +1,9 @@
+filters:
+  ".*":
+    reviewers:
+      - xiaofan-luan
+      - yhmo
+    approvers:
+      - xiaofan-luan
+      - yhmo
+

+ 120 - 158
examples/main/io/milvus/GeneralExample.java

@@ -26,38 +26,36 @@ import io.milvus.param.collection.*;
 import io.milvus.param.dml.*;
 import io.milvus.param.index.*;
 import io.milvus.param.partition.*;
+import io.milvus.Response.*;
 
 import java.util.*;
-import java.util.Random;
 
 public class GeneralExample {
-
-    public static MilvusServiceClient milvusClient;
+    private static MilvusServiceClient milvusClient;
 
     static {
-        ConnectParam connectParam = ConnectParam.Builder.newBuilder()
+        ConnectParam connectParam = ConnectParam.newBuilder()
                 .withHost("localhost")
                 .withPort(19530)
                 .build();
         milvusClient = new MilvusServiceClient(connectParam);
     }
 
-    public static final String COLLECTION_NAME = "TEST";
-    public static final String ID_FIELD = "userID";
-    public static final String VECTOR_FIELD = "userFace";
-    public static final Integer VECTOR_DIM = 64;
+    private static final String COLLECTION_NAME = "TEST";
+    private static final String ID_FIELD = "userID";
+    private static final String VECTOR_FIELD = "userFace";
+    private static final Integer VECTOR_DIM = 64;
 
-    public static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
-    public static final String INDEX_PARAM = "{\"nlist\":128}";
-    public static final MetricType METRIC_TYPE = MetricType.IP;
+    private static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
+    private static final String INDEX_PARAM = "{\"nlist\":128}";
+    private static final MetricType METRIC_TYPE = MetricType.IP;
 
-    public static final Integer SEARCH_K = 5;
-    public static final String SEARCH_PARAM = "{\"nprobe\":10}";
+    private static final Integer SEARCH_K = 5;
+    private static final String SEARCH_PARAM = "{\"nprobe\":10}";
 
-    public R<RpcStatus> createCollection() {
+    private R<RpcStatus> createCollection() {
         System.out.println("========== createCollection() ==========");
-        FieldType[] fieldTypes = new FieldType[2];
-        FieldType fieldType1 = FieldType.Builder.newBuilder()
+        FieldType fieldType1 = FieldType.newBuilder()
                 .withName(ID_FIELD)
                 .withDescription("user identification")
                 .withDataType(DataType.Int64)
@@ -65,20 +63,19 @@ public class GeneralExample {
                 .withPrimaryKey(true)
                 .build();
 
-        FieldType fieldType2 = FieldType.Builder.newBuilder()
+        FieldType fieldType2 = FieldType.newBuilder()
                 .withName(VECTOR_FIELD)
                 .withDescription("face embedding")
                 .withDataType(DataType.FloatVector)
                 .withDimension(VECTOR_DIM)
                 .build();
-        fieldTypes[0] = fieldType1;
-        fieldTypes[1] = fieldType2;
 
-        CreateCollectionParam createCollectionReq = CreateCollectionParam.Builder.newBuilder()
+        CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withDescription("customer info")
                 .withShardsNum(2)
-                .withFieldTypes(fieldTypes)
+                .addFieldType(fieldType1)
+                .addFieldType(fieldType2)
                 .build();
         R<RpcStatus> response = milvusClient.createCollection(createCollectionReq);
 
@@ -86,20 +83,18 @@ public class GeneralExample {
         return response;
     }
 
-    public R<RpcStatus> dropCollection() {
+    private R<RpcStatus> dropCollection() {
         System.out.println("========== dropCollection() ==========");
-        R<RpcStatus> response = milvusClient.dropCollection(DropCollectionParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.dropCollection(DropCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<Boolean> hasCollection() {
+    private R<Boolean> hasCollection() {
         System.out.println("========== hasCollection() ==========");
-        R<Boolean> response = milvusClient.hasCollection(HasCollectionParam.Builder
-                .newBuilder()
+        R<Boolean> response = milvusClient.hasCollection(HasCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
 
@@ -107,61 +102,55 @@ public class GeneralExample {
         return response;
     }
 
-    public R<RpcStatus> loadCollection() {
+    private R<RpcStatus> loadCollection() {
         System.out.println("========== loadCollection() ==========");
-        R<RpcStatus> response = milvusClient.loadCollection(LoadCollectionParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.loadCollection(LoadCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<RpcStatus> releaseCollection() {
+    private R<RpcStatus> releaseCollection() {
         System.out.println("========== releaseCollection() ==========");
-        R<RpcStatus> response = milvusClient.releaseCollection(ReleaseCollectionParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.releaseCollection(ReleaseCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<DescribeCollectionResponse> describeCollection() {
+    private R<DescribeCollectionResponse> describeCollection() {
         System.out.println("========== describeCollection() ==========");
-        R<DescribeCollectionResponse> response = milvusClient.describeCollection(DescribeCollectionParam.Builder
-                .newBuilder()
+        R<DescribeCollectionResponse> response = milvusClient.describeCollection(DescribeCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<GetCollectionStatisticsResponse> getCollectionStatistics() {
+    private R<GetCollectionStatisticsResponse> getCollectionStatistics() {
         System.out.println("========== getCollectionStatistics() ==========");
-        R<GetCollectionStatisticsResponse> response = milvusClient.getCollectionStatistics(GetCollectionStatisticsParam.Builder
-                .newBuilder()
-                .withCollectionName(COLLECTION_NAME)
-                .build());
-        System.out.println(response);
+        R<GetCollectionStatisticsResponse> response = milvusClient.getCollectionStatistics(
+                GetCollectionStatisticsParam.newBuilder()
+                        .withCollectionName(COLLECTION_NAME)
+                        .build());
+        GetCollStatResponseWrapper wrapper = new GetCollStatResponseWrapper(response.getData());
+        System.out.println("Collection row count: " + wrapper.GetRowCount());
         return response;
     }
 
-    public R<ShowCollectionsResponse> showCollections() {
+    private R<ShowCollectionsResponse> showCollections() {
         System.out.println("========== showCollections() ==========");
-        String[] collectionNames = new String[]{COLLECTION_NAME};
-        R<ShowCollectionsResponse> response = milvusClient.showCollections(ShowCollectionsParam.Builder
-                .newBuilder()
-                .withCollectionNames(collectionNames)
+        R<ShowCollectionsResponse> response = milvusClient.showCollections(ShowCollectionsParam.newBuilder()
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<RpcStatus> createPartition(String partitionName) {
+    private R<RpcStatus> createPartition(String partitionName) {
         System.out.println("========== createPartition() ==========");
-        R<RpcStatus> response = milvusClient.createPartition(CreatePartitionParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.createPartition(CreatePartitionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withPartitionName(partitionName)
                 .build());
@@ -170,10 +159,9 @@ public class GeneralExample {
         return response;
     }
 
-    public R<RpcStatus> dropPartition(String partitionName) {
+    private R<RpcStatus> dropPartition(String partitionName) {
         System.out.println("========== dropPartition() ==========");
-        R<RpcStatus> response = milvusClient.dropPartition(DropPartitionParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.dropPartition(DropPartitionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withPartitionName(partitionName)
                 .build());
@@ -182,10 +170,9 @@ public class GeneralExample {
         return response;
     }
 
-    public R<Boolean> hasPartition(String partitionName) {
+    private R<Boolean> hasPartition(String partitionName) {
         System.out.println("========== hasPartition() ==========");
-        R<Boolean> response = milvusClient.hasPartition(HasPartitionParam.Builder
-                .newBuilder()
+        R<Boolean> response = milvusClient.hasPartition(HasPartitionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withPartitionName(partitionName)
                 .build());
@@ -194,58 +181,29 @@ public class GeneralExample {
         return response;
     }
 
-    public R<RpcStatus> loadPartition(String partitionName) {
-        System.out.println("========== loadPartition() ==========");
-        String[] partitionNames = new String[]{partitionName};
-        R<RpcStatus> response = milvusClient.loadPartitions(LoadPartitionsParam.Builder
-                .newBuilder()
-                .withCollectionName(COLLECTION_NAME)
-                .withPartitionNames(partitionNames)
-                .build());
-
-        System.out.println(response);
-        return response;
-    }
-
-    public R<RpcStatus> releasePartition(String partitionName) {
+    private R<RpcStatus> releasePartition(String partitionName) {
         System.out.println("========== releasePartition() ==========");
-        String[] releaseNames = new String[]{partitionName};
-        R<RpcStatus> response = milvusClient.releasePartitions(ReleasePartitionsParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.releasePartitions(ReleasePartitionsParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
-                .withPartitionNames(releaseNames)
+                .addPartitionName(partitionName)
                 .build());
 
         System.out.println(response);
         return response;
     }
 
-    public R<GetPartitionStatisticsResponse> getPartitionStatistics(String partitionName) {
-        System.out.println("========== getPartitionStatistics() ==========");
-        R<GetPartitionStatisticsResponse> response = milvusClient.getPartitionStatistics(GetPartitionStatisticsParam.Builder
-                .newBuilder()
-                .withCollectionName(COLLECTION_NAME)
-                .withPartitionName(partitionName)
-                .build());
-
-        System.out.println(response);
-        return response;
-    }
-
-    public R<ShowPartitionsResponse> showPartitions() {
+    private R<ShowPartitionsResponse> showPartitions() {
         System.out.println("========== showPartitions() ==========");
-        R<ShowPartitionsResponse> response = milvusClient.showPartitions(ShowPartitionsParam.Builder
-                .newBuilder()
+        R<ShowPartitionsResponse> response = milvusClient.showPartitions(ShowPartitionsParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .build());
         System.out.println(response);
         return response;
     }
 
-    public R<RpcStatus> createIndex() {
+    private R<RpcStatus> createIndex() {
         System.out.println("========== createIndex() ==========");
-        R<RpcStatus> response = milvusClient.createIndex(CreateIndexParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.createIndex(CreateIndexParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withFieldName(VECTOR_FIELD)
                 .withIndexType(INDEX_TYPE)
@@ -256,10 +214,9 @@ public class GeneralExample {
         return response;
     }
 
-    public R<RpcStatus> dropIndex() {
+    private R<RpcStatus> dropIndex() {
         System.out.println("========== dropIndex() ==========");
-        R<RpcStatus> response = milvusClient.dropIndex(DropIndexParam.Builder
-                .newBuilder()
+        R<RpcStatus> response = milvusClient.dropIndex(DropIndexParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withFieldName(VECTOR_FIELD)
                 .build());
@@ -267,10 +224,9 @@ public class GeneralExample {
         return response;
     }
 
-    public R<DescribeIndexResponse> describeIndex() {
+    private R<DescribeIndexResponse> describeIndex() {
         System.out.println("========== describeIndex() ==========");
-        R<DescribeIndexResponse> response = milvusClient.describeIndex(DescribeIndexParam.Builder
-                .newBuilder()
+        R<DescribeIndexResponse> response = milvusClient.describeIndex(DescribeIndexParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withFieldName(VECTOR_FIELD)
                 .build());
@@ -278,10 +234,9 @@ public class GeneralExample {
         return response;
     }
 
-    public R<GetIndexStateResponse> getIndexState() {
+    private R<GetIndexStateResponse> getIndexState() {
         System.out.println("========== getIndexState() ==========");
-        R<GetIndexStateResponse> response = milvusClient.getIndexState(GetIndexStateParam.Builder
-                .newBuilder()
+        R<GetIndexStateResponse> response = milvusClient.getIndexState(GetIndexStateParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withFieldName(VECTOR_FIELD)
                 .build());
@@ -289,54 +244,19 @@ public class GeneralExample {
         return response;
     }
 
-    public R<GetIndexBuildProgressResponse> getIndexBuildProgress() {
+    private R<GetIndexBuildProgressResponse> getIndexBuildProgress() {
         System.out.println("========== getIndexBuildProgress() ==========");
-        R<GetIndexBuildProgressResponse> response = milvusClient.getIndexBuildProgress(GetIndexBuildProgressParam.Builder
-                .newBuilder()
-                .withCollectionName(COLLECTION_NAME)
-                .build());
+        R<GetIndexBuildProgressResponse> response = milvusClient.getIndexBuildProgress(
+                GetIndexBuildProgressParam.newBuilder()
+                        .withCollectionName(COLLECTION_NAME)
+                        .build());
         System.out.println(response);
         return response;
     }
 
-    public R<MutationResult> insert(String partitionName, Long count) {
-        System.out.println("========== insert() ==========");
-        List<Long> ids = new ArrayList<>();
-        List<List<Float>> vectors = new ArrayList<>();
-
-        Random ran=new Random();
-        for (Long i = 0L; i < count; ++i) {
-            ids.add(i + 100L);
-            List<Float> vector = new ArrayList<>();
-            for (int d = 0; d < VECTOR_DIM; ++d) {
-                vector.add(ran.nextFloat());
-            }
-            vectors.add(vector);
-        }
-
-        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));
-
-        InsertParam insertParam = InsertParam.Builder
-                .newBuilder()
-                .withCollectionName(COLLECTION_NAME)
-                .withPartitionName(partitionName)
-                .withFields(fields)
-                .build();
-
-        R<MutationResult> response = milvusClient.insert(insertParam);
-        System.out.println(response);
-
-        R<FlushResponse> ret = milvusClient.flush(COLLECTION_NAME);
-        System.out.println(ret.getData());
-
-        return response;
-    }
-
-    public R<MutationResult> delete(String partitionName, String expr) {
+    private R<MutationResult> delete(String partitionName, String expr) {
         System.out.println("========== delete() ==========");
-        DeleteParam build = DeleteParam.Builder.newBuilder()
+        DeleteParam build = DeleteParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withPartitionName(partitionName)
                 .withExpr(expr)
@@ -346,22 +266,27 @@ public class GeneralExample {
         return response;
     }
 
-    public R<SearchResults> search(String expr) {
+    private R<SearchResults> search(String expr) {
         System.out.println("========== search() ==========");
         List<String> outFields = Collections.singletonList(ID_FIELD);
 
         Random ran=new Random();
-        List<Float> vector = new ArrayList<>();
-        for (int d = 0; d < VECTOR_DIM; ++d) {
-            vector.add(ran.nextFloat());
+        int nq = 5;
+        List<List<Float>> vectors = new ArrayList<>();
+        for (int i = 0; i < nq; ++i) {
+            List<Float> vector = new ArrayList<>();
+            for (int d = 0; d < VECTOR_DIM; ++d) {
+                vector.add(ran.nextFloat());
+            }
+            vectors.add(vector);
         }
 
-        SearchParam searchParam = SearchParam.Builder.newBuilder()
+        SearchParam searchParam = SearchParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withMetricType(MetricType.L2)
                 .withOutFields(outFields)
                 .withTopK(SEARCH_K)
-                .withVectors(Collections.singletonList(vector))
+                .withVectors(vectors)
                 .withVectorFieldName(VECTOR_FIELD)
                 .withExpr(expr)
                 .withParams(SEARCH_PARAM)
@@ -369,11 +294,18 @@ public class GeneralExample {
 
 
         R<SearchResults> response = milvusClient.search(searchParam);
-        System.out.println(response);
+
+        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData());
+        for (int i = 0; i < vectors.size(); ++i) {
+            System.out.println("Search result of No." + i);
+            List<SearchResultsWrapper.IDScore> scores = wrapper.GetIDScore(i);
+            System.out.println(scores);
+        }
+
         return response;
     }
 
-    public R<CalcDistanceResults> calDistance() {
+    private R<CalcDistanceResults> calDistance() {
         System.out.println("========== calDistance() ==========");
         Random ran=new Random();
         List<Float> vector1 = new ArrayList<>();
@@ -383,7 +315,7 @@ public class GeneralExample {
             vector2.add(ran.nextFloat());
         }
 
-        CalcDistanceParam calcDistanceParam = CalcDistanceParam.Builder.newBuilder()
+        CalcDistanceParam calcDistanceParam = CalcDistanceParam.newBuilder()
                 .withVectorsLeft(Collections.singletonList(vector1))
                 .withVectorsRight(Collections.singletonList(vector2))
                 .withMetricType(MetricType.L2)
@@ -393,10 +325,10 @@ public class GeneralExample {
         return response;
     }
 
-    public R<QueryResults> query(String expr) {
+    private R<QueryResults> query(String expr) {
         System.out.println("========== query() ==========");
         List<String> fields = Arrays.asList(ID_FIELD, VECTOR_FIELD);
-        QueryParam test = QueryParam.Builder.newBuilder()
+        QueryParam test = QueryParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withExpr(expr)
                 .withOutFields(fields)
@@ -406,7 +338,38 @@ public class GeneralExample {
         return response;
     }
 
-    public static void main(String[] args) throws InterruptedException {
+    private R<MutationResult> insert(String partitionName, Long count) {
+        System.out.println("========== insert() ==========");
+        List<Long> ids = new ArrayList<>();
+        List<List<Float>> vectors = new ArrayList<>();
+
+        Random ran=new Random();
+        for (long i = 0L; i < count; ++i) {
+            ids.add(i + 100L);
+            List<Float> vector = new ArrayList<>();
+            for (int d = 0; d < VECTOR_DIM; ++d) {
+                vector.add(ran.nextFloat());
+            }
+            vectors.add(vector);
+        }
+
+        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));
+
+        InsertParam insertParam = InsertParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withPartitionName(partitionName)
+                .withFields(fields)
+                .build();
+
+        R<MutationResult> response = milvusClient.insert(insertParam);
+//        System.out.println(response);
+
+        return response;
+    }
+
+    public static void main(String[] args) {
         GeneralExample example = new GeneralExample();
 
         example.dropCollection();
@@ -415,17 +378,16 @@ public class GeneralExample {
         example.describeCollection();
         example.showCollections();
         example.loadCollection();
-        example.getCollectionStatistics();
 
         final String partitionName = "p1";
         example.createPartition(partitionName);
         example.hasPartition(partitionName);
         example.showPartitions();
-        example.loadPartition(partitionName);
-        example.getPartitionStatistics(partitionName);
 
         final Long row_count = 10000L;
         example.insert(partitionName, row_count);
+        example.getCollectionStatistics();
+
         example.createIndex();
         example.describeIndex();
         example.getIndexBuildProgress();

+ 7 - 1
pom.xml

@@ -159,6 +159,12 @@
             <version>5.7.0</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.22</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <profiles>
@@ -295,7 +301,7 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.19.1</version>
                 <configuration>
-                    <skipTests>true</skipTests>
+                    <skipTests>false</skipTests>
                 </configuration>
                 <dependencies>
                     <dependency>

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

@@ -0,0 +1,104 @@
+package io.milvus.Response;
+
+import io.milvus.grpc.DataType;
+import io.milvus.grpc.FieldData;
+import io.milvus.exception.IllegalResponseException;
+
+import io.milvus.param.Constant;
+import lombok.NonNull;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.protobuf.ByteString;
+
+/**
+ * Util class to wrap response of <code>query/search</code> interface.
+ */
+public class FieldDataWrapper {
+    private final FieldData fieldData;
+
+    public FieldDataWrapper(@NonNull FieldData fieldData) {
+        this.fieldData = fieldData;
+    }
+
+    public boolean isVectorField() {
+        return fieldData.getType() == DataType.FloatVector || fieldData.getType() == DataType.BinaryVector;
+    }
+
+    /**
+     * Get dimension of a vector field.
+     * Throw {@link IllegalResponseException} if the field is not a vector filed.
+     *
+     * @return <code>int</code> dimension of the vector field
+     */
+    public int getDim() throws IllegalResponseException {
+        if (!isVectorField()) {
+            throw new IllegalResponseException("Not a vector field");
+        }
+        return (int) fieldData.getVectors().getDim();
+    }
+
+    /**
+     * Return field data according to its type:
+     *      float vector field return List<List<Float>>
+     *      binary vector field return List<ByteBuffer>
+     *      int64 field return List<Long>
+     *      boolean field return List<Boolean>
+     *
+     * Throws {@link IllegalResponseException} if the field type is illegal.
+     *
+     * @return <code>List<?></?></code>
+     */
+    public List<?> getFieldData() 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");
+                }
+
+                List<List<Float>> packData = new ArrayList<>();
+                int count = data.size() / dim;
+                for (int i = 0; i < count; ++i) {
+                    packData.add(data.subList(i * dim, (i + 1) * dim));
+                }
+                return packData;
+            }
+            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");
+                }
+
+                List<ByteBuffer> packData = new ArrayList<>();
+                int count = data.size() / dim;
+                for (int i = 0; i < count; ++i) {
+                    packData.add(data.substring(i * dim, (i + 1) * dim).asReadOnlyByteBuffer());
+                }
+                return packData;
+            }
+            case Int64:
+            case Int32:
+            case Int16:
+                return fieldData.getScalars().getLongData().getDataList();
+            case Int8:
+                return fieldData.getScalars().getIntData().getDataList();
+            case Bool:
+                return fieldData.getScalars().getBoolData().getDataList();
+            case Float:
+                return fieldData.getScalars().getFloatData().getDataList();
+            case Double:
+                return fieldData.getScalars().getDoubleData().getDataList();
+            case String:
+                return fieldData.getScalars().getStringData().getDataList();
+            default:
+                throw new IllegalResponseException("Unsupported data type returned by FieldData");
+        }
+    }
+}

+ 36 - 0
src/main/java/io/milvus/Response/GetCollStatResponseWrapper.java

@@ -0,0 +1,36 @@
+package io.milvus.Response;
+
+import io.milvus.exception.IllegalResponseException;
+import io.milvus.grpc.GetCollectionStatisticsResponse;
+import io.milvus.grpc.KeyValuePair;
+import lombok.NonNull;
+
+import java.util.List;
+
+/**
+ * Util class to wrap response of <code>getCollectionStatistics</code> interface.
+ */
+public class GetCollStatResponseWrapper {
+    private final GetCollectionStatisticsResponse stat;
+
+    public GetCollStatResponseWrapper(@NonNull GetCollectionStatisticsResponse stat) {
+        this.stat = stat;
+    }
+
+    /**
+     * Get row count of this field.
+     * Throw {@link NumberFormatException} if the row count is not a number.
+     *
+     * @return <code>int</code> dimension of the vector field
+     */
+    public long GetRowCount() throws NumberFormatException {
+        List<KeyValuePair> stats = stat.getStatsList();
+        for (KeyValuePair kv : stats) {
+            if (kv.getKey().compareTo("row_count") == 0) {
+                return Long.parseLong(kv.getValue());
+            }
+        }
+
+        return 0;
+    }
+}

+ 57 - 0
src/main/java/io/milvus/Response/InsertResultWrapper.java

@@ -0,0 +1,57 @@
+package io.milvus.Response;
+
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.MutationResult;
+
+import java.util.List;
+
+import lombok.NonNull;
+
+/**
+ * Util class to wrap response of <code>insert</code> interface.
+ */
+public class InsertResultWrapper {
+    private final MutationResult result;
+
+    public InsertResultWrapper(@NonNull MutationResult result) {
+        this.result = result;
+    }
+
+    /**
+     * Get inserted count.
+     *
+     * @return <code>int</code> inserted count
+     */
+    public long getInsertCount() {
+        return result.getInsertCnt();
+    }
+
+    /**
+     * Get long id array returned by insert interface.
+     * Throw {@link ParamException} if the primary key type is not int64 type.
+     *
+     * @return <code>List<Long></code> id array returned by insert interface
+     */
+    public List<Long> getLongIDs() throws ParamException {
+        if (result.getIDs().hasIntId()) {
+            return result.getIDs().getIntId().getDataList();
+        } else {
+            throw new ParamException("The primary key is not long type, please try getStringIDs()");
+        }
+    }
+
+    /**
+     * Get string id array returned by insert interface.
+     * Throw {@link ParamException} if the primary key type is not string type.
+     * Note that currently Milvus doesn't support string type field, this method is reserved.
+     *
+     * @return <code>List<String></code> id array returned by insert interface
+     */
+    public List<String> getStringIDs() throws ParamException {
+        if (result.getIDs().hasStrId()) {
+            return result.getIDs().getStrId().getDataList();
+        } else {
+            throw new ParamException("The primary key is not string type, please try getLongIDs()");
+        }
+    }
+}

+ 40 - 0
src/main/java/io/milvus/Response/QueryResultsWrapper.java

@@ -0,0 +1,40 @@
+package io.milvus.Response;
+
+import io.milvus.exception.IllegalResponseException;
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.*;
+import io.milvus.param.dml.InsertParam;
+
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Util class to wrap response of <code>query</code> interface.
+ */
+public class QueryResultsWrapper {
+    private final QueryResults results;
+
+    public QueryResultsWrapper(@NonNull QueryResults results) {
+        this.results = results;
+    }
+
+    /**
+     * Get {@link FieldDataWrapper} for a field.
+     * Throws {@link ParamException} if the field doesn't exist.
+     *
+     * @return <code>FieldDataWrapper</code>
+     */
+    public FieldDataWrapper getFieldWrapper(@NonNull String fieldName) throws ParamException {
+        List<FieldData> fields = results.getFieldsDataList();
+        for (FieldData field : fields) {
+            if (fieldName.compareTo(field.getFieldName()) == 0) {
+                return new FieldDataWrapper(field);
+            }
+        }
+
+        throw new ParamException("The field name doesn't exist");
+    }
+}

+ 114 - 0
src/main/java/io/milvus/Response/SearchResultsWrapper.java

@@ -0,0 +1,114 @@
+package io.milvus.Response;
+
+import io.milvus.exception.IllegalResponseException;
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.*;
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Util class to wrap response of <code>search</code> interface.
+ */
+public class SearchResultsWrapper {
+    private final SearchResultData results;
+
+    public SearchResultsWrapper(@NonNull SearchResultData results) {
+        this.results = results;
+    }
+
+    /**
+     * Get {@link FieldDataWrapper} for a field.
+     * Throws {@link ParamException} if the field doesn't exist.
+     *
+     * @return <code>FieldDataWrapper</code>
+     */
+    public FieldDataWrapper GetFieldData(@NonNull String fieldName) {
+        for (int i = 0; i < results.getFieldsDataCount(); ++i) {
+            FieldData data = results.getFieldsData(i);
+            if (fieldName.compareTo(data.getFieldName()) == 0) {
+                return new FieldDataWrapper(data);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get id-score pairs returned by search interface.
+     * Throw {@link ParamException} if the indexOfTarget is illegal.
+     * Throw {@link IllegalResponseException} if the returned results is illegal.
+     *
+     * @return <code>List<IDScore></code> id-score pairs returned by search interface
+     */
+    public List<IDScore> GetIDScore(int indexOfTarget) throws ParamException, IllegalResponseException {
+        List<Long> kList = results.getTopksList();
+        if (indexOfTarget < 0 || indexOfTarget >= kList.size()) {
+            throw new ParamException("Illegal index of target: " + indexOfTarget);
+        }
+
+        int offset = 0;
+        for (int i = 0; i < indexOfTarget; ++i) {
+            offset += kList.get(i);
+        }
+
+        long k = kList.get(indexOfTarget);
+        if (offset + k > results.getScoresCount()) {
+            throw new IllegalResponseException("Result scores count is wrong");
+        }
+
+        List<IDScore> idScore = new ArrayList<>();
+
+        IDs ids = results.getIds();
+        if (ids.hasIntId()) {
+            LongArray longIDs = ids.getIntId();
+            if (offset + k > longIDs.getDataCount()) {
+                throw new IllegalResponseException("Result ids count is wrong");
+            }
+
+            for (int n = 0; n < k; ++n) {
+                idScore.add(new IDScore("", longIDs.getData(offset + n), results.getScores(offset + n)));
+            }
+        } else if (ids.hasStrId()) {
+            StringArray strIDs = ids.getStrId();
+            if (offset + k >= strIDs.getDataCount()) {
+                throw new IllegalResponseException("Result ids count is wrong");
+            }
+
+            for (int n = 0; n < k; ++n) {
+                idScore.add(new IDScore(strIDs.getData(offset + n), 0, results.getScores(offset + n)));
+            }
+        } else {
+            throw new IllegalResponseException("Result ids is illegal");
+        }
+
+        return idScore;
+    }
+
+    /**
+     * Internal use class to wrap response of <code>search</code> interface.
+     */
+    @Getter
+    public static final class IDScore {
+        private final String strID;
+        private final long longID;
+        private final float score;
+
+        public IDScore(String strID, long longID, float score) {
+            this.strID = strID;
+            this.longID = longID;
+            this.score = score;
+        }
+
+        @Override
+        public String toString() {
+            if (strID.isEmpty()) {
+                return "(ID: " + longID + " Score: " + score + ")";
+            } else {
+                return "(ID: '" + strID + "' Score: " + score + ")";
+            }
+        }
+    }
+}

+ 1072 - 553
src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java

@@ -22,28 +22,32 @@ package io.milvus.client;
 import com.google.protobuf.ByteString;
 import io.grpc.StatusRuntimeException;
 import io.milvus.exception.ClientNotConnectedException;
+import io.milvus.exception.IllegalResponseException;
 import io.milvus.exception.ParamException;
 import io.milvus.grpc.*;
 import io.milvus.param.Constant;
-import io.milvus.param.ParamUtils;
 import io.milvus.param.R;
 import io.milvus.param.RpcStatus;
 import io.milvus.param.alias.AlterAliasParam;
 import io.milvus.param.alias.CreateAliasParam;
 import io.milvus.param.alias.DropAliasParam;
 import io.milvus.param.collection.*;
+import io.milvus.param.control.GetMetricsParam;
+import io.milvus.param.control.GetPersistentSegmentInfoParam;
+import io.milvus.param.control.GetQuerySegmentInfoParam;
 import io.milvus.param.dml.*;
 import io.milvus.param.index.*;
 import io.milvus.param.partition.*;
+import lombok.NonNull;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 public abstract class AbstractMilvusGrpcClient implements MilvusClient {
@@ -56,279 +60,21 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
     protected abstract boolean clientIsReady();
 
-    @Override
-    public R<FlushResponse> flush(String collectionName, String dbName) {
-        return flush(Collections.singletonList(collectionName), dbName);
-    }
-
-    @Override
-    public R<FlushResponse> flush(String collectionName) {
-        return flush(Collections.singletonList(collectionName), "");
-    }
-
-    @Override
-    public R<FlushResponse> flush(List<String> collectionNames) {
-        return flush(collectionNames, "");
-    }
-
-    @Override
-    public R<FlushResponse> flush(List<String> collectionNames, String dbName) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        for (String collectionName : collectionNames) {
-            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
-        }
-
-        MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Flush).build();
-        FlushRequest.Builder builder = FlushRequest.newBuilder().setBase(msgBase).setDbName(dbName);
-        collectionNames.forEach(builder::addCollectionNames);
-        FlushRequest flushRequest = builder.build();
-        FlushResponse flush = null;
-        try {
-            flush = blockingStub().flush(flushRequest);
-        } catch (Exception e) {
-            logger.error("[milvus] flush rpc request error:{}", e.getMessage());
-            return R.failed(e);
-        }
-        return R.success(flush);
-    }
-
-    @Override
-    public R<MutationResult> delete(DeleteParam deleteParam) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        DeleteRequest deleteRequest = DeleteRequest.newBuilder()
-                .setBase(MsgBase.newBuilder().setMsgType(MsgType.Delete).build())
-                .setCollectionName(deleteParam.getCollectionName())
-                .setPartitionName(deleteParam.getPartitionName())
-                .build();
-
-        MutationResult delete = null;
-        try {
-            delete = blockingStub().delete(deleteRequest);
-        } catch (Exception e) {
-            logger.error("[milvus] delete rpc request error:{}", e.getMessage());
-            return R.failed(e);
-        }
-        return R.success(delete);
-    }
-
-    @Override
-    public R<MutationResult> insert(InsertParam insertParam) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        String collectionName = insertParam.getCollectionName();
-        String partitionName = insertParam.getPartitionName();
-        List<InsertParam.Field> fields = insertParam.getFields();
-
-        //1. gen insert request
-        MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Insert).build();
-        InsertRequest.Builder insertBuilder = InsertRequest.newBuilder()
-                .setCollectionName(collectionName)
-                .setPartitionName(partitionName)
-                .setBase(msgBase)
-                .setNumRows(insertParam.getRowCount());
-
-        //2. gen fieldData
-        // TODO: check field type(use DescribeCollection get schema to compare)
-        for (InsertParam.Field field : fields) {
-            insertBuilder.addFieldsData(genFieldData(field.getName(), field.getType(), field.getValues()));
-        }
-
-        //3. call insert
-        InsertRequest insertRequest = insertBuilder.build();
-        MutationResult insert = null;
-        try {
-            insert = blockingStub().insert(insertRequest);
-        } catch (Exception e) {
-            logger.error("[milvus] insert rpc request error:{}", e.getMessage());
-            return R.failed(e);
-        }
-        return R.success(insert);
-    }
-
-    @Override
-    public R<SearchResults> search(SearchParam searchParam) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        SearchRequest.Builder builder = SearchRequest.newBuilder()
-                .setDbName(searchParam.getDbName())
-                .setCollectionName(searchParam.getCollectionName());
-        if (!searchParam.getPartitionNames().isEmpty()) {
-            searchParam.getPartitionNames().forEach(builder::addPartitionNames);
-        }
-
-        // prepare target vectors
-        // TODO: check target vector dimension(use DescribeColection get schema to compare)
-        List<?> vectors = searchParam.getVectors();
-        List<ByteString> byteStrings = vectors.stream().map(vector -> {
-            if (vector instanceof ByteBuffer) {
-                return ByteString.copyFrom((ByteBuffer) vector);
-            }
-
-            if (vector instanceof List) {
-                List list = (List) vector;
-                if (list.get(0) instanceof Float) {
-                    ByteBuffer buf = ByteBuffer.allocate(Float.BYTES * list.size());
-                    list.forEach(v -> buf.putFloat((Float) v));
-
-                    byte[] array = buf.array();
-                    return ByteString.copyFrom(array);
-                }
-            }
-
-            logger.error("Search target vector type is illegal(Only allow List<Float> or ByteBuffer)");
-            return null;
-        }).collect(Collectors.toList());
-
-        PlaceholderValue.Builder pldBuilder = PlaceholderValue.newBuilder()
-                .setTag(Constant.VECTOR_TAG)
-                .setType(PlaceholderType.FloatVector);
-        byteStrings.forEach(pldBuilder::addValues);
-
-        PlaceholderGroup placeholderGroup = PlaceholderGroup.newBuilder()
-                .addPlaceholders(pldBuilder.build())
-                .build();
-
-        builder.setPlaceholderGroup(placeholderGroup.toByteString());
-
-        // search parameters
-        builder.addSearchParams(
-                KeyValuePair.newBuilder()
-                        .setKey(Constant.VECTOR_FIELD)
-                        .setValue(searchParam.getVectorFieldName())
-                        .build())
-                .addSearchParams(
-                        KeyValuePair.newBuilder()
-                                .setKey(Constant.TOP_K)
-                                .setValue(String.valueOf(searchParam.getTopK()))
-                                .build())
-                .addSearchParams(
-                        KeyValuePair.newBuilder()
-                                .setKey(Constant.METRIC_TYPE)
-                                .setValue(searchParam.getMetricType())
-                                .build());
-
-        if (null != searchParam.getParams() && !searchParam.getParams().isEmpty()) {
-            builder.addSearchParams(
-                    KeyValuePair.newBuilder()
-                            .setKey(Constant.PARAMS)
-                            .setValue(searchParam.getParams())
-                            .build());
-        }
-
-        if (!searchParam.getOutFields().isEmpty()) {
-            searchParam.getOutFields().forEach(builder::addOutputFields);
-        }
-
-        // always use expression since dsl is discarded
-        builder.setDslType(DslType.BoolExprV1);
-        if (searchParam.getExpr() != null && !searchParam.getExpr().isEmpty()) {
-            builder.setDsl(searchParam.getExpr());
-        }
-
-        SearchRequest searchRequest = builder.build();
-        SearchResults search;
-        try {
-            search = this.blockingStub().search(searchRequest);
-        } catch (Exception e) {
-            logger.error("[milvus] search rpc request error:{}", e.getMessage());
-            return R.failed(e);
-        }
-
-        return R.success(search);
-    }
-
-    @Override
-    public R<QueryResults> query(QueryParam queryParam) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        QueryRequest queryRequest = QueryRequest.newBuilder()
-                .setDbName(queryParam.getDbName())
-                .setCollectionName(queryParam.getCollectionName())
-                .addAllPartitionNames(queryParam.getPartitionNames())
-                .addAllOutputFields(queryParam.getOutFields())
-                .setExpr(queryParam.getExpr())
-                .build();
-
-        QueryResults query;
-        try {
-            query = this.blockingStub().query(queryRequest);
-        } catch (Exception e) {
-//            e.printStackTrace();
-            logger.error("[milvus] query rpc request error:{}", e.getMessage());
-            return R.failed(e);
-        }
-        return R.success(query);
-    }
-
-    @Override
-    public R<CalcDistanceResults> calcDistance(CalcDistanceParam calcDistanceParam) {
-        if (!clientIsReady()) {
-            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
-        }
-
-        List<List<Float>> vectors_left = calcDistanceParam.getVectorsLeft();
-        List<List<Float>> vectors_right = calcDistanceParam.getVectorsRight();
-
-        FloatArray.Builder left_float_array = FloatArray.newBuilder();
-        for (List<Float> vector : vectors_left) {
-            left_float_array.addAllData(vector);
-        }
-
-        FloatArray.Builder right_float_array = FloatArray.newBuilder();
-        for (List<Float> vector : vectors_right) {
-            right_float_array.addAllData(vector);
-        }
-
-        CalcDistanceRequest calcDistanceRequest = CalcDistanceRequest.newBuilder()
-                .setOpLeft(
-                        VectorsArray.newBuilder()
-                                .setDataArray(
-                                        VectorField.newBuilder()
-                                                .setFloatVector(left_float_array.build())
-                                                .setDim(vectors_left.get(0).size())
-                                                .build()
-                                )
-                                .build()
-                )
-                .setOpRight(
-                        VectorsArray.newBuilder()
-                                .setDataArray(
-                                        VectorField.newBuilder()
-                                                .setFloatVector(right_float_array.build())
-                                                .setDim(vectors_right.get(0).size())
-                                                .build()
-                                )
-                                .build()
-                )
-                .addParams(
-                        KeyValuePair.newBuilder()
-                                .setKey("metric")
-                                .setValue(calcDistanceParam.getMetricType())
-                                .build()
-                )
-                .build();
-        CalcDistanceResults calcDistanceResults;
-        try {
-            calcDistanceResults = blockingStub().calcDistance(calcDistanceRequest);
-        } catch (Exception e) {
-            logger.error("[milvus] calDistance rpc request error:{}", e.getMessage());
-            return R.failed(e);
+    ///////////////////// Internal Functions//////////////////////
+    private List<KeyValuePair> assembleKvPair(Map<String, String> sourceMap) {
+        List<KeyValuePair> result = new ArrayList<>();
+        if (MapUtils.isNotEmpty(sourceMap)) {
+            sourceMap.forEach((key, value) -> {
+                KeyValuePair kv = KeyValuePair.newBuilder()
+                        .setKey(key)
+                        .setValue(value).build();
+                result.add(kv);
+            });
         }
-        return R.success(calcDistanceResults);
+        return result;
     }
 
+    @SuppressWarnings("unchecked")
     private FieldData genFieldData(String fieldName, DataType dataType, List<?> objects) {
         if (objects == null) {
             throw new ParamException("Cannot generate FieldData from null object");
@@ -340,7 +86,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 // each object is List<Float>
                 for (Object object : objects) {
                     if (object instanceof List) {
-                        List list = (List) object;
+                        List<Float> list = (List<Float>) object;
                         floats.addAll(list);
                     } else {
                         throw new ParamException("The type of FloatVector must be List<Float>");
@@ -352,20 +98,21 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 VectorField vectorField = VectorField.newBuilder().setDim(dim).setFloatVector(floatArray).build();
                 return builder.setFieldName(fieldName).setType(DataType.FloatVector).setVectors(vectorField).build();
             } else if (dataType == DataType.BinaryVector) {
-                ByteString byteString = null;
+                ByteBuffer totalBuf = null;
                 int dim = 0;
                 // each object is ByteBuffer
                 for (Object object : objects) {
                     ByteBuffer buf = (ByteBuffer) object;
-                    ByteString tempStr = ByteString.copyFrom((ByteBuffer) buf);
-                    if (byteString == null){
-                        byteString = tempStr;
+                    if (totalBuf == null){
+                        totalBuf = ByteBuffer.allocate(buf.position() * objects.size());
+                        totalBuf.put(buf.array());
                         dim = buf.position() * 8;
                     } else {
-                        byteString.concat(tempStr);
+                        totalBuf.put(buf.array());
                     }
                 }
 
+                ByteString byteString = ByteString.copyFrom(totalBuf.array());
                 VectorField vectorField = VectorField.newBuilder().setDim(dim).setBinaryVector(byteString).build();
                 return builder.setFieldName(fieldName).setType(DataType.BinaryVector).setVectors(vectorField).build();
             }
@@ -417,704 +164,1476 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         add(DataType.BinaryVector);
     }};
 
+    private void waitForLoadingCollection(String collectionName, List<String> partitionNames,
+                                          long waitingInterval, long timeout) throws IllegalResponseException {
+        long tsBegin = System.currentTimeMillis();
+        if (partitionNames == null || partitionNames.isEmpty()) {
+            ShowCollectionsRequest showCollectionRequest = ShowCollectionsRequest.newBuilder()
+                    .addCollectionNames(collectionName)
+                    .setType(ShowType.InMemory)
+                    .build();
+
+            // Use showCollection() to check loading percentages of the collection.
+            // If the inMemory percentage is 100, that means the collection has finished loading.
+            // Otherwise, this thread will sleep a small interval and check again.
+            // If waiting time exceed timeout, exist the circle
+            while (true) {
+                long tsNow = System.currentTimeMillis();
+                if ((tsNow - tsBegin) >= timeout*1000) {
+                    logWarning("Waiting load thread is timeout, loading process may not be finished");
+                    break;
+                }
+
+                ShowCollectionsResponse response = blockingStub().showCollections(showCollectionRequest);
+                int namesCount = response.getCollectionNamesCount();
+                int percentagesCount = response.getInMemoryPercentagesCount();
+                if (namesCount != 1) {
+                    throw new IllegalResponseException("ShowCollectionsResponse is illegal. Collection count: "
+                            + namesCount);
+                }
+
+                if (namesCount != percentagesCount) {
+                    String msg = "ShowCollectionsResponse is illegal. Collection count: " + namesCount
+                            + " memory percentages count: " + percentagesCount;
+                    throw new IllegalResponseException(msg);
+                }
+
+                long percentage = response.getInMemoryPercentages(0);
+                String responseCollection = response.getCollectionNames(0);
+                if (responseCollection.compareTo(collectionName) == 0 && percentage >= 100) {
+                    break;
+                }
+
+                try {
+                    logInfo("Waiting load, interval: {} ms, percentage: {}%", waitingInterval, percentage);
+                    TimeUnit.MILLISECONDS.sleep(waitingInterval);
+                } catch (InterruptedException e) {
+                    logWarning("Waiting load thread is interrupted, loading process may not be finished");
+                    break;
+                }
+            }
+
+        } else {
+            ShowPartitionsRequest showPartitionsRequest = ShowPartitionsRequest.newBuilder()
+                    .setCollectionName(collectionName)
+                    .addAllPartitionNames(partitionNames)
+                    .setType(ShowType.InMemory).build();
+
+            // Use showPartitions() to check loading percentages of all the partitions.
+            // If each partition's  inMemory percentage is 100, that means all the partitions have finished loading.
+            // Otherwise, this thread will sleep a small interval and check again.
+            // If waiting time exceed timeout, exist the circle
+            while(true) {
+                long tsNow = System.currentTimeMillis();
+                if ((tsNow - tsBegin) >= timeout*1000) {
+                    logWarning("Waiting load thread is timeout, loading process may not be finished");
+                    break;
+                }
+
+                ShowPartitionsResponse response = blockingStub().showPartitions(showPartitionsRequest);
+                int namesCount = response.getPartitionNamesCount();
+                int percentagesCount = response.getInMemoryPercentagesCount();
+                if (namesCount != percentagesCount) {
+                    String msg = "ShowPartitionsResponse is illegal. Partition count: " + namesCount
+                            + " memory percentages count: " + percentagesCount;
+                    throw new IllegalResponseException(msg);
+                }
+
+                // construct a hash map to check each partition's inMemory percentage by name
+                Map<String, Long> percentages = new HashMap<>();
+                for (int i = 0; i < response.getInMemoryPercentagesCount(); ++i) {
+                    percentages.put(response.getPartitionNames(i), response.getInMemoryPercentages(i));
+                }
+
+                String partitionNoMemState = "";
+                String partitionNotFullyLoad = "";
+                boolean allLoaded = true;
+                for (String name : partitionNames) {
+                    if (!percentages.containsKey(name)) {
+                        allLoaded = false;
+                        partitionNoMemState = name;
+                        break;
+                    }
+                    if (percentages.get(name) < 100L) {
+                        allLoaded = false;
+                        partitionNotFullyLoad = name;
+                        break;
+                    }
+                }
+
+                if (allLoaded) {
+                    break;
+                }
+
+                try {
+                    String msg = "Waiting load, interval: " + waitingInterval + "ms.";
+                    if (!partitionNoMemState.isEmpty()) {
+                        msg += ("Partition " + partitionNoMemState + " has no memory state.");
+                    }
+                    if (!partitionNotFullyLoad.isEmpty()) {
+                        msg += ("Partition " + partitionNotFullyLoad + " has not fully loaded.");
+                    }
+                    logInfo(msg);
+                    TimeUnit.MILLISECONDS.sleep(waitingInterval);
+                } catch (InterruptedException e) {
+                    logWarning("Waiting load thread is interrupted, load process may not be finished");
+                    break;
+                }
+            }
+        }
+    }
+
+    private void waitForFlush(FlushResponse flushResponse, long waitingInterval, long timeout) {
+        // The rpc api flush() return FlushResponse, but the returned segment ids maybe not yet persisted.
+        // This method use getPersistentSegmentInfo() to check segment state.
+        // If all segments state become Flushed, then we say the sync flush action is finished.
+        // If waiting time exceed timeout, exist the circle
+        long tsBegin = System.currentTimeMillis();
+        Map<String, LongArray> collectionSegIDs = flushResponse.getCollSegIDsMap();
+        collectionSegIDs.forEach((collectionName, segmentIDs) -> {
+            while (segmentIDs.getDataCount() > 0) {
+                long tsNow = System.currentTimeMillis();
+                if ((tsNow - tsBegin) >= timeout*1000) {
+                    logWarning("Waiting flush thread is timeout, flush process may not be finished");
+                    break;
+                }
+
+                GetPersistentSegmentInfoRequest getSegInfoRequest = GetPersistentSegmentInfoRequest.newBuilder()
+                        .setCollectionName(collectionName)
+                        .build();
+                GetPersistentSegmentInfoResponse response = blockingStub().getPersistentSegmentInfo(getSegInfoRequest);
+                List<PersistentSegmentInfo> segmentInfoArray = response.getInfosList();
+                int flushedCount = 0;
+                for (int i = 0; i < segmentIDs.getDataCount(); ++i) {
+                    for (PersistentSegmentInfo info : segmentInfoArray) {
+                        if (info.getSegmentID() == segmentIDs.getData(i) && info.getState() == SegmentState.Flushed) {
+                            flushedCount++;
+                            break;
+                        }
+                    }
+                }
+
+                // if all segment of this collection has been flushed, break this circle and check next collection
+                if (flushedCount == segmentIDs.getDataCount()) {
+                    break;
+                }
+
+                try {
+                    String msg = "Waiting flush, interval: " + waitingInterval + "ms. " + flushedCount +
+                            " of " + segmentIDs.getDataCount() + " segments flushed.";
+                    logInfo(msg);
+                    TimeUnit.MILLISECONDS.sleep(waitingInterval);
+                } catch (InterruptedException e) {
+                    logWarning("Waiting flush thread is interrupted, flush process may not be finished");
+                    break;
+                }
+            }
+        });
+    }
+
+    ///////////////////// API implementation //////////////////////
     @Override
-    public R<Boolean> hasCollection(HasCollectionParam requestParam) {
+    public R<Boolean> hasCollection(@NonNull HasCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        HasCollectionRequest hasCollectionRequest = HasCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        BoolResponse response;
         try {
-            response = blockingStub().hasCollection(hasCollectionRequest);
+            HasCollectionRequest hasCollectionRequest = HasCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            BoolResponse response = blockingStub().hasCollection(hasCollectionRequest);
+
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Has collection check successfully!\n{}", requestParam.toString());
+                logInfo("HasCollectionRequest successfully!");
                 Boolean value = Optional.of(response)
                         .map(BoolResponse::getValue)
                         .orElse(false);
                 return R.success(value);
             } else {
+                logError("HasCollectionRequest failed!\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logger.error("[milvus] hasCollection:{} request error: {}", requestParam.getCollectionName(), e.getMessage());
+            logError("HasCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("HasCollectionRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
-
     @Override
-    public R<RpcStatus> createCollection(CreateCollectionParam requestParam) {
+    public R<RpcStatus> createCollection(@NonNull CreateCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        // Check whether the parameters are correct or not
-        if (requestParam == null) {
-            return R.failed(new ParamException("Request param can not be null"));
-        }
-
-        // Construct CollectionSchema Params
-        CollectionSchema.Builder collectionSchemaBuilder = CollectionSchema.newBuilder();
-        collectionSchemaBuilder.setName(requestParam.getCollectionName())
-                .setDescription(requestParam.getDescription());
+        logInfo(requestParam.toString());
 
-        for (FieldType fieldType : requestParam.getFieldTypes()) {
-            FieldSchema.Builder fieldSchemaBuilder = FieldSchema.newBuilder()
-                    .setFieldID(fieldType.getFieldID())
-                    .setName(fieldType.getName())
-                    .setIsPrimaryKey(fieldType.isPrimaryKey())
-                    .setDescription(fieldType.getDescription())
-                    .setDataType(fieldType.getDataType())
-                    .setAutoID(fieldType.isAutoID());
+        try {
+            // Construct CollectionSchema Params
+            CollectionSchema.Builder collectionSchemaBuilder = CollectionSchema.newBuilder();
+            collectionSchemaBuilder.setName(requestParam.getCollectionName())
+                    .setDescription(requestParam.getDescription());
+
+            long fieldID = 0;
+            for (FieldType fieldType : requestParam.getFieldTypes()) {
+                FieldSchema.Builder fieldSchemaBuilder = FieldSchema.newBuilder()
+                        .setFieldID(fieldID)
+                        .setName(fieldType.getName())
+                        .setIsPrimaryKey(fieldType.isPrimaryKey())
+                        .setDescription(fieldType.getDescription())
+                        .setDataType(fieldType.getDataType())
+                        .setAutoID(fieldType.isAutoID());
+
+                // assemble typeParams for CollectionSchema
+                List<KeyValuePair> typeParamsList = assembleKvPair(fieldType.getTypeParams());
+                if (CollectionUtils.isNotEmpty(typeParamsList)) {
+                    typeParamsList.forEach(fieldSchemaBuilder::addTypeParams);
+                }
 
-            // assemble typeParams for CollectionSchema
-            List<KeyValuePair> typeParamsList = assembleKvPair(fieldType.getTypeParams());
-            if (CollectionUtils.isNotEmpty(typeParamsList)) {
-                typeParamsList.forEach(fieldSchemaBuilder::addTypeParams);
+                collectionSchemaBuilder.addFields(fieldSchemaBuilder.build());
+                fieldID++;
             }
 
-            collectionSchemaBuilder.addFields(fieldSchemaBuilder.build());
-        }
-
-        // Construct CreateCollectionRequest
-        CreateCollectionRequest createCollectionRequest = CreateCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setShardsNum(requestParam.getShardsNum())
-                .setSchema(collectionSchemaBuilder.build().toByteString())
-                .build();
+            // Construct CreateCollectionRequest
+            CreateCollectionRequest createCollectionRequest = CreateCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setShardsNum(requestParam.getShardsNum())
+                    .setSchema(collectionSchemaBuilder.build().toByteString())
+                    .build();
 
-        Status response;
-        try {
-            response = blockingStub().createCollection(createCollectionRequest);
+            Status response = blockingStub().createCollection(createCollectionRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Created collection " + requestParam.getCollectionName() + " successfully!\n{}",
-                        requestParam.toString());
+                logInfo("CreateCollectionRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
-                logInfo("Created collection " + requestParam.getCollectionName() + " failed!\n{}", response);
+                logError("CreateCollectionRequest failed!\n{}", response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("createCollection " + requestParam.getCollectionName() + " RPC failed:\n{}",
-                    e.getStatus().toString());
+            logError("CreateCollectionRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CreateCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> dropCollection(DropCollectionParam requestParam) {
+    public R<RpcStatus> dropCollection(@NonNull DropCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DropCollectionRequest dropCollectionRequest = DropCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().dropCollection(dropCollectionRequest);
+            DropCollectionRequest dropCollectionRequest = DropCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            Status response = blockingStub().dropCollection(dropCollectionRequest);
+
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Drop collection successfully!\n{}", requestParam.toString());
+                logInfo("DropCollectionRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("DropCollectionRequest failed! Collection name:{}\n{}",
+                        requestParam.getCollectionName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("dropCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("DropCollectionRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DropCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
-
     @Override
-    public R<RpcStatus> loadCollection(LoadCollectionParam requestParam) {
+    public R<RpcStatus> loadCollection(@NonNull LoadCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        LoadCollectionRequest loadCollectionRequest = LoadCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().loadCollection(loadCollectionRequest);
+            LoadCollectionRequest loadCollectionRequest = LoadCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
 
-            if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Load collection successfully!\n{}", requestParam.toString());
-                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
-            } else {
+            Status response = blockingStub().loadCollection(loadCollectionRequest);
+
+            if (response.getErrorCode() != ErrorCode.Success) {
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
-        } catch (StatusRuntimeException e) {
-            logError("loadCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+
+            // sync load, wait until collection finish loading
+            if (requestParam.isSyncLoad()) {
+                waitForLoadingCollection(requestParam.getCollectionName(), null,
+                        requestParam.getSyncLoadWaitingInterval(), requestParam.getSyncLoadWaitingTimeout());
+            }
+
+            logInfo("LoadCollectionRequest successfully! Collection name:{}",
+                    requestParam.getCollectionName());
+            return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
+        } catch (StatusRuntimeException e) { // gRPC could throw this exception
+            logError("LoadCollectionRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (IllegalResponseException e) { // milvus exception for illegal response
+            logError("LoadCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("LoadCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> releaseCollection(ReleaseCollectionParam requestParam) {
+    public R<RpcStatus> releaseCollection(@NonNull ReleaseCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        ReleaseCollectionRequest releaseCollectionRequest = ReleaseCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().releaseCollection(releaseCollectionRequest);
+            ReleaseCollectionRequest releaseCollectionRequest = ReleaseCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            Status response = blockingStub().releaseCollection(releaseCollectionRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Release collection successfully!\n{}", requestParam.toString());
+                logInfo("ReleaseCollectionRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("ReleaseCollectionRequest failed! Collection name:{}\n{}",
+                        requestParam.getCollectionName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("releaseCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("ReleaseCollectionRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("ReleaseCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
-
     }
 
     @Override
-    public R<DescribeCollectionResponse> describeCollection(DescribeCollectionParam requestParam) {
+    public R<DescribeCollectionResponse> describeCollection(@NonNull DescribeCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DescribeCollectionRequest describeCollectionRequest = DescribeCollectionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        DescribeCollectionResponse response;
         try {
-            response = blockingStub().describeCollection(describeCollectionRequest);
+            DescribeCollectionRequest describeCollectionRequest = DescribeCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            DescribeCollectionResponse response = blockingStub().describeCollection(describeCollectionRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Describe collection successfully!\n{}", requestParam.toString());
+                logInfo("DescribeCollectionRequest successfully!");
                 return R.success(response);
             } else {
+                logError("DescribeCollectionRequest failed!\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("describeCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("DescribeCollectionRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DescribeCollectionRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<GetCollectionStatisticsResponse> getCollectionStatistics(GetCollectionStatisticsParam requestParam) {
+    public R<GetCollectionStatisticsResponse> getCollectionStatistics(@NonNull GetCollectionStatisticsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        GetCollectionStatisticsRequest getCollectionStatisticsRequest = GetCollectionStatisticsRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        GetCollectionStatisticsResponse response;
         try {
-            response = blockingStub().getCollectionStatistics(getCollectionStatisticsRequest);
+            // flush collection if client command to do it(some times user may want to know the newest row count)
+            if (requestParam.isFlushCollection()) {
+                R<FlushResponse> response = flush(FlushParam.newBuilder()
+                        .addCollectionName(requestParam.getCollectionName())
+                        .withSyncFlush(Boolean.TRUE)
+                        .build());
+                if (response.getStatus() != R.Status.Success.getCode()) {
+                    return R.failed(R.Status.valueOf(response.getStatus()), response.getMessage());
+                }
+            }
+
+            GetCollectionStatisticsRequest getCollectionStatisticsRequest = GetCollectionStatisticsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            GetCollectionStatisticsResponse response = blockingStub().getCollectionStatistics(getCollectionStatisticsRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Get collection statistics successfully!\n{}", requestParam.toString());
+                logInfo("GetCollectionStatisticsRequest successfully!");
                 return R.success(response);
             } else {
+                logError("GetCollectionStatisticsRequest failed!\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("getCollectionStatisticsRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("GetCollectionStatisticsRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetCollectionStatisticsRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<ShowCollectionsResponse> showCollections(ShowCollectionsParam requestParam) {
+    public R<ShowCollectionsResponse> showCollections(@NonNull ShowCollectionsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        ShowCollectionsRequest.Builder showCollectionRequestBuilder = ShowCollectionsRequest.newBuilder();
+        logInfo(requestParam.toString());
 
-        String[] collectionNames = requestParam.getCollectionNames();
-        if (collectionNames == null || collectionNames.length <= 0) {
-            return R.failed(R.Status.ParamError, "Collection name not specified");
-        }
-
-        // add collectionNames
-        Arrays.stream(collectionNames).forEach(showCollectionRequestBuilder::addCollectionNames);
-
-        ShowCollectionsRequest showCollectionsRequest = showCollectionRequestBuilder
-                .setType(requestParam.getShowType()).build();
-
-        ShowCollectionsResponse response;
         try {
-            response = blockingStub().showCollections(showCollectionsRequest);
+            ShowCollectionsRequest showCollectionsRequest = ShowCollectionsRequest.newBuilder()
+                    .addAllCollectionNames(requestParam.getCollectionNames())
+                    .setType(requestParam.getShowType()).build();
+
+            ShowCollectionsResponse response = blockingStub().showCollections(showCollectionsRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Show collection successfully!\n{}", requestParam.toString());
+                logInfo("ShowCollectionsRequest successfully!");
                 return R.success(response);
             } else {
+                logError("ShowCollectionsRequest failed!\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("showCollectionsRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("ShowCollectionsRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("ShowCollectionsRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    /**
+     * Currently we don't allow client call this method since server side has no compaction function
+     * Now this method is only internally used by getCollectionStatistics()
+     */
+//    @Override
+    private R<FlushResponse> flush(@NonNull FlushParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Flush).build();
+            FlushRequest flushRequest = FlushRequest.newBuilder()
+                    .setBase(msgBase)
+                    .addAllCollectionNames(requestParam.getCollectionNames())
+                    .build();
+            FlushResponse response = blockingStub().flush(flushRequest);
+
+            if (requestParam.getSyncFlush() == Boolean.TRUE) {
+                waitForFlush(response, requestParam.getSyncFlushWaitingInterval(),
+                        requestParam.getSyncFlushWaitingTimeout());
+            }
+
+            logInfo("FlushRequest successfully! Collection names:{}", requestParam.getCollectionNames());
+            return R.success(response);
+        } catch (StatusRuntimeException e) {
+            logError("FlushRequest RPC failed! Collection names:{}\n{}",
+                    requestParam.getCollectionNames(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("FlushRequest failed! Collection names:{}\n{}",
+                    requestParam.getCollectionNames(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> createPartition(CreatePartitionParam requestParam) {
+    public R<RpcStatus> createPartition(@NonNull CreatePartitionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        CreatePartitionRequest createPartitionRequest = CreatePartitionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setPartitionName(requestParam.getPartitionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().createPartition(createPartitionRequest);
+            CreatePartitionRequest createPartitionRequest = CreatePartitionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setPartitionName(requestParam.getPartitionName())
+                    .build();
+
+            Status response = blockingStub().createPartition(createPartitionRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Create partition successfully!\n{}", requestParam.toString());
+                logInfo("CreatePartitionRequest successfully! Collection name:{}, partition name:{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("CreatePartitionRequest failed! Collection name:{}, partition name:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("createPartitionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("CreatePartitionRequest RPC failed! Collection name:{}, partition name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CreatePartitionRequest failed! Collection name:{}, partition name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> dropPartition(DropPartitionParam requestParam) {
+    public R<RpcStatus> dropPartition(@NonNull DropPartitionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DropPartitionRequest dropPartitionRequest = DropPartitionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setPartitionName(requestParam.getPartitionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().dropPartition(dropPartitionRequest);
+            DropPartitionRequest dropPartitionRequest = DropPartitionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setPartitionName(requestParam.getPartitionName())
+                    .build();
+
+            Status response = blockingStub().dropPartition(dropPartitionRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Drop partition successfully!\n{}", requestParam.toString());
+                logInfo("DropPartitionRequest successfully! Collection name:{}, partition name:{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("DropPartitionRequest failed! Collection name:{}, partition name:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("createPartitionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("DropPartitionRequest RPC failed! Collection name:{}, partition name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DropPartitionRequest failed! Collection name:{}, partition name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<Boolean> hasPartition(HasPartitionParam requestParam) {
+    public R<Boolean> hasPartition(@NonNull HasPartitionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        HasPartitionRequest hasPartitionRequest = HasPartitionRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setPartitionName(requestParam.getPartitionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        BoolResponse response;
         try {
-            response = blockingStub().hasPartition(hasPartitionRequest);
+            HasPartitionRequest hasPartitionRequest = HasPartitionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setPartitionName(requestParam.getPartitionName())
+                    .build();
 
-            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("HasPartition call successfully!\n{}", requestParam.toString());
-
-                Boolean result = Optional.ofNullable(response)
-                        .map(BoolResponse::getValue)
-                        .orElse(false);
+            BoolResponse response = blockingStub().hasPartition(hasPartitionRequest);
 
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("HasPartitionRequest successfully!");
+                Boolean result = response.getValue();
                 return R.success(result);
             } else {
+                logError("HasPartitionRequest failed!\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("hasPartitionRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("HasPartitionRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("HasPartitionRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> loadPartitions(LoadPartitionsParam requestParam) {
+    public R<RpcStatus> loadPartitions(@NonNull LoadPartitionsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        LoadPartitionsRequest.Builder loadPartitionsRequestBuilder = LoadPartitionsRequest.newBuilder();
+        logInfo(requestParam.toString());
 
-        String[] partitionNames = requestParam.getPartitionNames();
-        // add partitionNames
-        Arrays.stream(partitionNames).forEach(loadPartitionsRequestBuilder::addPartitionNames);
-
-        LoadPartitionsRequest loadPartitionsRequest = loadPartitionsRequestBuilder
-                .setCollectionName(requestParam.getCollectionName()).build();
-
-        Status response;
         try {
-            response = blockingStub().loadPartitions(loadPartitionsRequest);
+            LoadPartitionsRequest loadPartitionsRequest = LoadPartitionsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .addAllPartitionNames(requestParam.getPartitionNames())
+                    .build();
 
-            if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Load partition successfully!\n{}", requestParam.toString());
-                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
-            } else {
+            Status response = blockingStub().loadPartitions(loadPartitionsRequest);
+
+            if (response.getErrorCode() != ErrorCode.Success) {
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
-        } catch (StatusRuntimeException e) {
-            logError("loadPartitionsRequest RPC failed:\n{}", e.getStatus().toString());
+
+            // sync load, wait until all partitions finish loading
+            if (requestParam.isSyncLoad()) {
+                waitForLoadingCollection(requestParam.getCollectionName(), requestParam.getPartitionNames(),
+                        requestParam.getSyncLoadWaitingInterval(), requestParam.getSyncLoadWaitingTimeout());
+            }
+
+            logInfo("LoadPartitionsRequest successfully! Collection name:{}, partition names:{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames());
+            return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
+        } catch (StatusRuntimeException e) { // gRPC could throw this exception
+            logError("LoadPartitionsRequest RPC failed! Collection name:{}, partition names:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (IllegalResponseException e) { // milvus exception for illegal response
+            logError("LoadPartitionsRequest failed! Collection name:{}, partition names:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("LoadPartitionsRequest failed! Collection name:{}, partition names:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> releasePartitions(ReleasePartitionsParam requestParam) {
+    public R<RpcStatus> releasePartitions(@NonNull ReleasePartitionsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        ReleasePartitionsRequest.Builder releasePartitionsRequestBuilder = ReleasePartitionsRequest.newBuilder();
-
-        String[] partitionNames = requestParam.getPartitionNames();
-        // add partitionNames
-        Arrays.stream(partitionNames).forEach(releasePartitionsRequestBuilder::addPartitionNames);
-
-        ReleasePartitionsRequest releasePartitionsRequest = releasePartitionsRequestBuilder
-                .setCollectionName(requestParam.getCollectionName()).build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().releasePartitions(releasePartitionsRequest);
+            ReleasePartitionsRequest releasePartitionsRequest = ReleasePartitionsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .addAllPartitionNames(requestParam.getPartitionNames())
+                    .build();
+
+            Status response = blockingStub().releasePartitions(releasePartitionsRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Release partition successfully!\n{}", requestParam.toString());
+                logInfo("ReleasePartitionsRequest successfully! Collection name:{}, partition names:{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionNames());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("ReleasePartitionsRequest failed! Collection name:{}, partition names:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getPartitionNames(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("releasePartitionsRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("ReleasePartitionsRequest RPC failed! Collection name:{}, partition names:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("ReleasePartitionsRequest failed! Collection name:{}, partition names:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getPartitionNames(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<GetPartitionStatisticsResponse> getPartitionStatistics(GetPartitionStatisticsParam requestParam) {
+    public R<GetPartitionStatisticsResponse> getPartitionStatistics(@NonNull GetPartitionStatisticsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        GetPartitionStatisticsRequest getPartitionStatisticsRequest = GetPartitionStatisticsRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setPartitionName(requestParam.getPartitionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        GetPartitionStatisticsResponse response;
         try {
-            response = blockingStub().getPartitionStatistics(getPartitionStatisticsRequest);
+            GetPartitionStatisticsRequest getPartitionStatisticsRequest = GetPartitionStatisticsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setPartitionName(requestParam.getPartitionName())
+                    .build();
+
+            GetPartitionStatisticsResponse response =
+                    blockingStub().getPartitionStatistics(getPartitionStatisticsRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Get partition statistics successfully!\n{}", requestParam.toString());
+                logInfo("GetPartitionStatisticsRequest successfully!");
                 return R.success(response);
             } else {
+                logError("ReleasePartitionsRequest failed:\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("getPartitionStatisticsRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("GetPartitionStatisticsRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetQuerySegmentInfoRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<ShowPartitionsResponse> showPartitions(ShowPartitionsParam requestParam) {
+    public R<ShowPartitionsResponse> showPartitions(@NonNull ShowPartitionsParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        ShowPartitionsRequest showPartitionsRequest = ShowPartitionsRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        ShowPartitionsResponse response;
         try {
-            response = blockingStub().showPartitions(showPartitionsRequest);
+            ShowPartitionsRequest showPartitionsRequest = ShowPartitionsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .addAllPartitionNames(requestParam.getPartitionNames())
+                    .build();
+
+            ShowPartitionsResponse response = blockingStub().showPartitions(showPartitionsRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Show partition successfully!\n{}", requestParam.toString());
+                logInfo("ShowPartitionsRequest successfully!");
                 return R.success(response);
             } else {
+                logError("ShowPartitionsRequest failed:\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("showPartitionsRequest RPC failed:\n{}", e.getStatus().toString());
+            logError("ShowPartitionsRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("ShowPartitionsRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> createAlias(CreateAliasParam requestParam) {
+    public R<RpcStatus> createAlias(@NonNull CreateAliasParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        CreateAliasRequest createAliasRequest = CreateAliasRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setAlias(requestParam.getAlias())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().createAlias(createAliasRequest);
+            CreateAliasRequest createAliasRequest = CreateAliasRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setAlias(requestParam.getAlias())
+                    .build();
+
+            Status response = blockingStub().createAlias(createAliasRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Create alias successfully!\n{}", requestParam.toString());
+                logInfo("CreateAliasRequest successfully! Collection name:{}, alias name:{}",
+                        requestParam.getCollectionName(), requestParam.getAlias());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("CreateAliasRequest failed! Collection name:{}, alias name:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getAlias(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("createAlias RPC failed:\n{}", e.getStatus().toString());
+            logError("CreateAliasRequest RPC failed! Collection name:{}, alias name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getAlias(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CreateAliasRequest failed! Collection name:{}, alias name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getAlias(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> dropAlias(DropAliasParam requestParam) {
+    public R<RpcStatus> dropAlias(@NonNull DropAliasParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DropAliasRequest dropAliasRequest = DropAliasRequest.newBuilder()
-                .setAlias(requestParam.getAlias())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().dropAlias(dropAliasRequest);
+            DropAliasRequest dropAliasRequest = DropAliasRequest.newBuilder()
+                    .setAlias(requestParam.getAlias())
+                    .build();
+
+            Status response = blockingStub().dropAlias(dropAliasRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Drop alias successfully!\n{}", requestParam.toString());
+                logInfo("DropAliasRequest successfully! Alias name:{}", requestParam.getAlias());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("DropAliasRequest failed! Alias name:{}\n{}",
+                        requestParam.getAlias(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("dropAlias RPC failed:\n{}", e.getStatus().toString());
+            logError("DropAliasRequest RPC failed! Alias name:{}\n{}",
+                    requestParam.getAlias(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DropAliasRequest failed! Alias name:{}\n{}",
+                    requestParam.getAlias(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> alterAlias(AlterAliasParam requestParam) {
+    public R<RpcStatus> alterAlias(@NonNull AlterAliasParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        AlterAliasRequest alterAliasRequest = AlterAliasRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setAlias(requestParam.getAlias())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().alterAlias(alterAliasRequest);
+            AlterAliasRequest alterAliasRequest = AlterAliasRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setAlias(requestParam.getAlias())
+                    .build();
+
+            Status response = blockingStub().alterAlias(alterAliasRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Alter alias successfully!\n{}", requestParam.toString());
+                logInfo("AlterAliasRequest successfully! Collection name:{}, alias name:{}",
+                        requestParam.getCollectionName(), requestParam.getAlias());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("AlterAliasRequest failed! Collection name:{}, alias name:{}\n{}",
+                        requestParam.getCollectionName(), requestParam.getAlias(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("alterAlias RPC failed:\n{}", e.getStatus().toString());
+            logError("AlterAliasRequest RPC failed! Collection name:{}, alias name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getAlias(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("AlterAliasRequest failed! Collection name:{}, alias name:{}\n{}",
+                    requestParam.getCollectionName(), requestParam.getAlias(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> createIndex(CreateIndexParam requestParam) {
+    public R<RpcStatus> createIndex(@NonNull CreateIndexParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        CreateIndexRequest.Builder createIndexRequestBuilder = CreateIndexRequest.newBuilder();
-        List<KeyValuePair> extraParamList = assembleKvPair(requestParam.getExtraParam());
+        logInfo(requestParam.toString());
 
-        if (CollectionUtils.isNotEmpty(extraParamList)) {
-            extraParamList.forEach(createIndexRequestBuilder::addExtraParams);
-        }
+        try {
+            CreateIndexRequest.Builder createIndexRequestBuilder = CreateIndexRequest.newBuilder();
+            List<KeyValuePair> extraParamList = assembleKvPair(requestParam.getExtraParam());
 
-        CreateIndexRequest createIndexRequest = createIndexRequestBuilder.setCollectionName(requestParam.getCollectionName())
-                .setFieldName(requestParam.getFieldName()).build();
+            if (CollectionUtils.isNotEmpty(extraParamList)) {
+                extraParamList.forEach(createIndexRequestBuilder::addExtraParams);
+            }
 
-        Status response;
-        try {
-            response = blockingStub().createIndex(createIndexRequest);
+            CreateIndexRequest createIndexRequest = createIndexRequestBuilder.setCollectionName(requestParam.getCollectionName())
+                    .setFieldName(requestParam.getFieldName()).build();
+
+            Status response = blockingStub().createIndex(createIndexRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Create index successfully!\n{}", requestParam.toString());
+                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());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("createIndex RPC failed:\n{}", e.getStatus().toString());
+            logError("CreateIndexRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CreateIndexRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<RpcStatus> dropIndex(DropIndexParam requestParam) {
+    public R<RpcStatus> dropIndex(@NonNull DropIndexParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DropIndexRequest dropIndexRequest = DropIndexRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setFieldName(requestParam.getFieldName())
-                .build();
+        logInfo(requestParam.toString());
 
-        Status response;
         try {
-            response = blockingStub().dropIndex(dropIndexRequest);
+            DropIndexRequest dropIndexRequest = DropIndexRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setFieldName(requestParam.getFieldName())
+                    .build();
+
+            Status response = blockingStub().dropIndex(dropIndexRequest);
 
             if (response.getErrorCode() == ErrorCode.Success) {
-                logInfo("Drop index successfully!\n{}", requestParam.toString());
+                logInfo("DropIndexRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
                 return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
             } else {
+                logError("DropIndexRequest failed! Collection name:{}\n{}",
+                        requestParam.getCollectionName(), response.getReason());
                 return R.failed(R.Status.valueOf(response.getErrorCode().getNumber()), response.getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("dropIndex RPC failed:\n{}", e.getStatus().toString());
+            logError("DropIndexRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DropIndexRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<DescribeIndexResponse> describeIndex(DescribeIndexParam requestParam) {
+    public R<DescribeIndexResponse> describeIndex(@NonNull DescribeIndexParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        DescribeIndexRequest describeIndexRequest = DescribeIndexRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setFieldName(requestParam.getFieldName())
-                .build();
+        logInfo(requestParam.toString());
 
-        DescribeIndexResponse response;
         try {
-            response = blockingStub().describeIndex(describeIndexRequest);
+            DescribeIndexRequest describeIndexRequest = DescribeIndexRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setFieldName(requestParam.getFieldName())
+                    .build();
+
+            DescribeIndexResponse response = blockingStub().describeIndex(describeIndexRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Describe index successfully!\n{}", requestParam.toString());
+                logInfo("DescribeIndexRequest successfully!");
                 return R.success(response);
             } else {
+                logError("DescribeIndexRequest failed:\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("describeIndex RPC failed:\n{}", e.getStatus().toString());
+            logError("DescribeIndexRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DescribeIndexRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<GetIndexStateResponse> getIndexState(GetIndexStateParam requestParam) {
+    public R<GetIndexStateResponse> getIndexState(@NonNull GetIndexStateParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        GetIndexStateRequest getIndexStateRequest = GetIndexStateRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .setFieldName(requestParam.getFieldName())
-                .build();
+        logInfo(requestParam.toString());
+
+        try {
+            GetIndexStateRequest getIndexStateRequest = GetIndexStateRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setFieldName(requestParam.getFieldName())
+                    .build();
 
-        GetIndexStateResponse response;
+            GetIndexStateResponse response = blockingStub().getIndexState(getIndexStateRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("GetIndexStateRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("GetIndexStateRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("GetIndexStateRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetIndexStateRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<GetIndexBuildProgressResponse> getIndexBuildProgress(@NonNull GetIndexBuildProgressParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
 
         try {
-            response = blockingStub().getIndexState(getIndexStateRequest);
+            GetIndexBuildProgressRequest getIndexBuildProgressRequest = GetIndexBuildProgressRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            GetIndexBuildProgressResponse response = blockingStub().getIndexBuildProgress(getIndexBuildProgressRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Get index state successfully!\n{}", requestParam.toString());
+                logInfo("GetIndexBuildProgressRequest successfully!");
                 return R.success(response);
             } else {
+                logError("GetIndexBuildProgressRequest failed:\n{}", response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("getIndexState RPC failed:\n{}", e.getStatus().toString());
+            logError("GetIndexBuildProgressRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetIndexBuildProgressRequest failed:\n{}", e.getMessage());
             return R.failed(e);
         }
     }
 
     @Override
-    public R<GetIndexBuildProgressResponse> getIndexBuildProgress(GetIndexBuildProgressParam requestParam) {
+    public R<MutationResult> delete(@NonNull DeleteParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
 
-        GetIndexBuildProgressRequest getIndexBuildProgressRequest = GetIndexBuildProgressRequest.newBuilder()
-                .setCollectionName(requestParam.getCollectionName())
-                .build();
+        logInfo(requestParam.toString());
 
-        GetIndexBuildProgressResponse response;
+        try {
+            DeleteRequest deleteRequest = DeleteRequest.newBuilder()
+                    .setBase(MsgBase.newBuilder().setMsgType(MsgType.Delete).build())
+                    .setCollectionName(requestParam.getCollectionName())
+                    .setPartitionName(requestParam.getPartitionName())
+                    .setExpr(requestParam.getExpr())
+                    .build();
+
+            MutationResult response = blockingStub().delete(deleteRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("DeleteRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
+                return R.success(response);
+            } else {
+                logError("DeleteRequest failed! Collection name:{}\n{}",
+                        requestParam.getCollectionName(), response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("DeleteRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DeleteRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<MutationResult> insert(@NonNull InsertParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
 
         try {
-            response = blockingStub().getIndexBuildProgress(getIndexBuildProgressRequest);
+            String collectionName = requestParam.getCollectionName();
+            String partitionName = requestParam.getPartitionName();
+            List<InsertParam.Field> fields = requestParam.getFields();
+
+            //1. gen insert request
+            MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Insert).build();
+            InsertRequest.Builder insertBuilder = InsertRequest.newBuilder()
+                    .setCollectionName(collectionName)
+                    .setPartitionName(partitionName)
+                    .setBase(msgBase)
+                    .setNumRows(requestParam.getRowCount());
+
+            //2. gen fieldData
+            // TODO: check field type(use DescribeCollection get schema to compare)
+            for (InsertParam.Field field : fields) {
+                insertBuilder.addFieldsData(genFieldData(field.getName(), field.getType(), field.getValues()));
+            }
+
+            //3. call insert
+            InsertRequest insertRequest = insertBuilder.build();
+            MutationResult response = blockingStub().insert(insertRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
-                logInfo("Get index build progress successfully!\n{}", requestParam.toString());
+                logInfo("InsertRequest successfully! Collection name:{}",
+                        requestParam.getCollectionName());
                 return R.success(response);
             } else {
+                logError("InsertRequest failed! Collection name:{}\n{}",
+                        requestParam.getCollectionName(), response.getStatus().getReason());
                 return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
                         response.getStatus().getReason());
             }
         } catch (StatusRuntimeException e) {
-            logError("getIndexBuildProgress RPC failed:\n{}", e.getStatus().toString());
+            logError("InsertRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("InsertRequest failed! Collection name:{}\n{}",
+                    requestParam.getCollectionName(), e.getMessage());
             return R.failed(e);
         }
     }
 
-    private List<KeyValuePair> assembleKvPair(Map<String, String> sourceMap) {
-        List<KeyValuePair> result = new ArrayList<>();
-        if (MapUtils.isNotEmpty(sourceMap)) {
-            sourceMap.forEach((key, value) -> {
-                KeyValuePair kv = KeyValuePair.newBuilder()
-                        .setKey(key)
-                        .setValue(value).build();
-                result.add(kv);
-            });
+    @Override
+    @SuppressWarnings("unchecked")
+    public R<SearchResults> search(@NonNull SearchParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            SearchRequest.Builder builder = SearchRequest.newBuilder()
+                    .setDbName("")
+                    .setCollectionName(requestParam.getCollectionName());
+            if (!requestParam.getPartitionNames().isEmpty()) {
+                requestParam.getPartitionNames().forEach(builder::addPartitionNames);
+            }
+
+            // prepare target vectors
+            // TODO: check target vector dimension(use DescribeCollection get schema to compare)
+            PlaceholderType plType = PlaceholderType.None;
+            List<?> vectors = requestParam.getVectors();
+            List<ByteString> byteStrings = new ArrayList<>();
+            for (Object vector : vectors) {
+                if (vector instanceof List) {
+                    plType = PlaceholderType.FloatVector;
+                    List<Float> list = (List<Float>) vector;
+                    ByteBuffer buf = ByteBuffer.allocate(Float.BYTES * list.size());
+                    buf.order(ByteOrder.LITTLE_ENDIAN);
+                    list.forEach(buf::putFloat);
+
+                    byte[] array = buf.array();
+                    ByteString bs = ByteString.copyFrom(array);
+                    byteStrings.add(bs);
+                } else if (vector instanceof ByteBuffer) {
+                    plType = PlaceholderType.BinaryVector;
+                    ByteBuffer buf = (ByteBuffer) vector;
+                    byte[] array = buf.array();
+                    ByteString bs = ByteString.copyFrom(array);
+                    byteStrings.add(bs);
+                } else {
+                    String msg = "Search target vector type is illegal(Only allow List<Float> or ByteBuffer)";
+                    logError(msg);
+                    return R.failed(R.Status.UnexpectedError, msg);
+                }
+            }
+
+            PlaceholderValue.Builder pldBuilder = PlaceholderValue.newBuilder()
+                    .setTag(Constant.VECTOR_TAG)
+                    .setType(plType);
+            byteStrings.forEach(pldBuilder::addValues);
+
+            PlaceholderValue plv = pldBuilder.build();
+            PlaceholderGroup placeholderGroup = PlaceholderGroup.newBuilder()
+                    .addPlaceholders(plv)
+                    .build();
+
+            ByteString byteStr = placeholderGroup.toByteString();
+            builder.setPlaceholderGroup(byteStr);
+
+            // search parameters
+            builder.addSearchParams(
+                    KeyValuePair.newBuilder()
+                            .setKey(Constant.VECTOR_FIELD)
+                            .setValue(requestParam.getVectorFieldName())
+                            .build())
+                    .addSearchParams(
+                            KeyValuePair.newBuilder()
+                                    .setKey(Constant.TOP_K)
+                                    .setValue(String.valueOf(requestParam.getTopK()))
+                                    .build())
+                    .addSearchParams(
+                            KeyValuePair.newBuilder()
+                                    .setKey(Constant.METRIC_TYPE)
+                                    .setValue(requestParam.getMetricType())
+                                    .build())
+                    .addSearchParams(
+                            KeyValuePair.newBuilder()
+                                    .setKey(Constant.ROUND_DECIMAL)
+                                    .setValue(String.valueOf(requestParam.getRoundDecimal()))
+                                    .build());
+
+            if (null != requestParam.getParams() && !requestParam.getParams().isEmpty()) {
+                builder.addSearchParams(
+                        KeyValuePair.newBuilder()
+                                .setKey(Constant.PARAMS)
+                                .setValue(requestParam.getParams())
+                                .build());
+            }
+
+            if (!requestParam.getOutFields().isEmpty()) {
+                requestParam.getOutFields().forEach(builder::addOutputFields);
+            }
+
+            // always use expression since dsl is discarded
+            builder.setDslType(DslType.BoolExprV1);
+            if (requestParam.getExpr() != null && !requestParam.getExpr().isEmpty()) {
+                builder.setDsl(requestParam.getExpr());
+            }
+
+            SearchRequest searchRequest = builder.build();
+            SearchResults response = this.blockingStub().search(searchRequest);
+
+            //TODO: truncate distance value by round decimal
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("SearchRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("SearchRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("SearchRequest RPC failed:{}", e.getMessage());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("SearchRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<QueryResults> query(@NonNull QueryParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            QueryRequest queryRequest = QueryRequest.newBuilder()
+                    .setDbName("")
+                    .setCollectionName(requestParam.getCollectionName())
+                    .addAllPartitionNames(requestParam.getPartitionNames())
+                    .addAllOutputFields(requestParam.getOutFields())
+                    .setExpr(requestParam.getExpr())
+                    .build();
+
+            QueryResults response = this.blockingStub().query(queryRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("QueryRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("QueryRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+//            e.printStackTrace();
+            logError("QueryRequest RPC failed:{}", e.getMessage());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("QueryRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<CalcDistanceResults> calcDistance(@NonNull CalcDistanceParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            List<List<Float>> vectors_left = requestParam.getVectorsLeft();
+            List<List<Float>> vectors_right = requestParam.getVectorsRight();
+
+            FloatArray.Builder left_float_array = FloatArray.newBuilder();
+            for (List<Float> vector : vectors_left) {
+                left_float_array.addAllData(vector);
+            }
+
+            FloatArray.Builder right_float_array = FloatArray.newBuilder();
+            for (List<Float> vector : vectors_right) {
+                right_float_array.addAllData(vector);
+            }
+
+            CalcDistanceRequest calcDistanceRequest = CalcDistanceRequest.newBuilder()
+                    .setOpLeft(
+                            VectorsArray.newBuilder()
+                                    .setDataArray(
+                                            VectorField.newBuilder()
+                                                    .setFloatVector(left_float_array.build())
+                                                    .setDim(vectors_left.get(0).size())
+                                                    .build()
+                                    )
+                                    .build()
+                    )
+                    .setOpRight(
+                            VectorsArray.newBuilder()
+                                    .setDataArray(
+                                            VectorField.newBuilder()
+                                                    .setFloatVector(right_float_array.build())
+                                                    .setDim(vectors_right.get(0).size())
+                                                    .build()
+                                    )
+                                    .build()
+                    )
+                    .addParams(
+                            KeyValuePair.newBuilder()
+                                    .setKey("metric")
+                                    .setValue(requestParam.getMetricType())
+                                    .build()
+                    )
+                    .build();
+
+            CalcDistanceResults response = blockingStub().calcDistance(calcDistanceRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("CalcDistanceRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("CalcDistanceRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("CalcDistanceRequest RPC failed:{}", e.getMessage());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CalcDistanceRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<GetMetricsResponse> getMetrics(@NonNull GetMetricsParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            GetMetricsRequest getMetricsRequest = GetMetricsRequest.newBuilder()
+                    .setRequest(requestParam.getRequest())
+                    .build();
+
+            GetMetricsResponse response = blockingStub().getMetrics(getMetricsRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("GetMetricsRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("GetMetricsRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("GetMetricsRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetMetricsRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<GetPersistentSegmentInfoResponse> getPersistentSegmentInfo(@NonNull GetPersistentSegmentInfoParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            GetPersistentSegmentInfoRequest getSegmentInfoRequest = GetPersistentSegmentInfoRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            GetPersistentSegmentInfoResponse response = blockingStub().getPersistentSegmentInfo(getSegmentInfoRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("GetPersistentSegmentInfoRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("GetPersistentSegmentInfoRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("GetPersistentSegmentInfoRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetPersistentSegmentInfoRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<GetQuerySegmentInfoResponse> getQuerySegmentInfo(@NonNull GetQuerySegmentInfoParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            GetQuerySegmentInfoRequest getSegmentInfoRequest = GetQuerySegmentInfoRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName())
+                    .build();
+
+            GetQuerySegmentInfoResponse response = blockingStub().getQuerySegmentInfo(getSegmentInfoRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logInfo("GetQuerySegmentInfoRequest successfully!");
+                return R.success(response);
+            } else {
+                logError("GetQuerySegmentInfoRequest failed:\n{}", response.getStatus().getReason());
+                return R.failed(R.Status.valueOf(response.getStatus().getErrorCode().getNumber()),
+                        response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("GetQuerySegmentInfoRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("GetQuerySegmentInfoRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
         }
-        return result;
     }
 
     ///////////////////// Log Functions//////////////////////

+ 111 - 45
src/main/java/io/milvus/client/MilvusClient.java

@@ -26,45 +26,37 @@ import io.milvus.param.alias.AlterAliasParam;
 import io.milvus.param.alias.CreateAliasParam;
 import io.milvus.param.alias.DropAliasParam;
 import io.milvus.param.collection.*;
+import io.milvus.param.control.*;
 import io.milvus.param.dml.*;
 import io.milvus.param.index.*;
 import io.milvus.param.partition.*;
 
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /** The Milvus Client Interface */
 public interface MilvusClient {
 
+    /** Close Milvus client channel, timeout: 1 minute */
     default void close() {
-        close(TimeUnit.MINUTES.toSeconds(1));
+        try {
+            close(TimeUnit.MINUTES.toSeconds(1));
+        } catch (InterruptedException e) {
+            System.out.println("Interrupted during shutdown Milvus client!");
+        }
     }
 
-    void close(long maxWaitSeconds);
-
-    R<MutationResult> insert(InsertParam insertParam);
-
-    R<FlushResponse> flush(String collectionName, String dbName);
-
-    R<FlushResponse> flush(String collectionName);
-
-    R<FlushResponse> flush(List<String> collectionNames);
-
-    R<FlushResponse> flush(List<String> collectionNames, String dbName);
-
-    R<MutationResult> delete(DeleteParam deleteParam);
-
-    R<SearchResults> search(SearchParam searchParam);
-
-    R<QueryResults> query(QueryParam queryParam);
-
-    R<CalcDistanceResults> calcDistance(CalcDistanceParam calcDistanceParam);
+    /**
+     * Close Milvus client channel.
+     *
+     * @param maxWaitSeconds timeout unit: second
+     */
+    void close(long maxWaitSeconds) throws InterruptedException;
 
     /**
      * Check if a collection exists.
      *
      * @param requestParam {@link HasCollectionParam}
-     * @return {status:result code,data: boolean, whether if has collection or not}
+     * @return {status:result code, data: boolean, whether if has collection or not}
      */
     R<Boolean> hasCollection(HasCollectionParam requestParam);
 
@@ -72,7 +64,7 @@ public interface MilvusClient {
      * Create a collection in Milvus.
      *
      * @param requestParam {@link CreateCollectionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> createCollection(CreateCollectionParam requestParam);
 
@@ -80,15 +72,15 @@ public interface MilvusClient {
      * Drop a collection. Note that this drops all data in the collection.
      *
      * @param requestParam {@link DropCollectionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> dropCollection(DropCollectionParam requestParam);
 
     /**
-     * Load collection to cache before search.
+     * Load collection to cache before search/query.
      *
      * @param requestParam {@link LoadCollectionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> loadCollection(LoadCollectionParam requestParam);
 
@@ -97,7 +89,7 @@ public interface MilvusClient {
      * search while the corresponding collection is unloaded.
      *
      * @param requestParam {@link ReleaseCollectionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> releaseCollection(ReleaseCollectionParam requestParam);
 
@@ -105,7 +97,7 @@ public interface MilvusClient {
      * Show the details of a collection, e.g. name, schema.
      *
      * @param requestParam {@link DescribeCollectionParam}
-     * @return {status:result code,data:DescribeCollectionResponse{schema,collectionID}}
+     * @return {status:result code, data:DescribeCollectionResponse{schema,collectionID}}
      */
     R<DescribeCollectionResponse> describeCollection(DescribeCollectionParam requestParam);
 
@@ -125,11 +117,20 @@ public interface MilvusClient {
      */
     R<ShowCollectionsResponse> showCollections(ShowCollectionsParam requestParam);
 
+//    /**
+//     * Flush collections.
+//     * Currently we don't allow client call this method since server side has no compaction function
+//     *
+//     * @param requestParam {@link FlushParam}
+//     * @return {status:result code,data: FlushResponse{flush segment ids}}
+//     */
+//    R<FlushResponse> flush(FlushParam requestParam);
+
     /**
      * Create a partition in a collection.
      *
      * @param requestParam {@link CreatePartitionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> createPartition(CreatePartitionParam requestParam);
 
@@ -137,7 +138,7 @@ public interface MilvusClient {
      * To drop a partition will drop all data in this partition and the _default partition cannot be dropped.
      *
      * @param requestParam {@link DropPartitionParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> dropPartition(DropPartitionParam requestParam);
 
@@ -145,7 +146,7 @@ public interface MilvusClient {
      * Check if a partition exists in a collection.
      *
      * @param requestParam {@link HasPartitionParam}
-     * @return {status:result code,data: boolean, whether if has collection or not}
+     * @return {status:result code, data: boolean, whether if has collection or not}
      */
     R<Boolean> hasPartition(HasPartitionParam requestParam);
 
@@ -153,7 +154,7 @@ public interface MilvusClient {
      * Load a partition into cache.
      *
      * @param requestParam {@link LoadPartitionsParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> loadPartitions(LoadPartitionsParam requestParam);
 
@@ -161,7 +162,7 @@ public interface MilvusClient {
      * Release a partition from cache.
      *
      * @param requestParam {@link ReleasePartitionsParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> releasePartitions(ReleasePartitionsParam requestParam);
 
@@ -177,15 +178,16 @@ public interface MilvusClient {
      * Show all partitions in a collection.
      *
      * @param requestParam {@link ShowPartitionsParam}
-     * @return {status:result code,data:ShowPartitionsResponse{partition_names,partitionIDs,created_timestamps,created_utc_timestamps}}
+     * @return {status:result code, data:ShowPartitionsResponse{partition_names,partitionIDs,created_timestamps,created_utc_timestamps}}
      */
     R<ShowPartitionsResponse> showPartitions(ShowPartitionsParam requestParam);
 
     /**
-     * Create alias for a collection.
+     * Create an alias for a collection.
+     * Alias can be used in search/query to replace collection name
      *
      * @param requestParam {@link CreateAliasParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> createAlias(CreateAliasParam requestParam);
 
@@ -193,7 +195,7 @@ public interface MilvusClient {
      * Drop an alias.
      *
      * @param requestParam {@link DropAliasParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> dropAlias(DropAliasParam requestParam);
 
@@ -201,7 +203,7 @@ public interface MilvusClient {
      * Alter alias from a collection to another.
      *
      * @param requestParam {@link AlterAliasParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> alterAlias(AlterAliasParam requestParam);
 
@@ -209,7 +211,7 @@ public interface MilvusClient {
      * Create an index on a vector field. Note that index building is an async progress.
      *
      * @param requestParam {@link CreateIndexParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> createIndex(CreateIndexParam requestParam);
 
@@ -217,7 +219,7 @@ public interface MilvusClient {
      * Drop an index.
      *
      * @param requestParam {@link DropIndexParam}
-     * @return {status:result code,data:RpcStatus{msg: result message}}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
      */
     R<RpcStatus> dropIndex(DropIndexParam requestParam);
 
@@ -225,23 +227,87 @@ public interface MilvusClient {
      * Show index information. Current release of Milvus only supports showing latest built index.
      *
      * @param requestParam {@link DescribeIndexParam}
-     * @return {status:result code,data:DescribeIndexResponse{status,index_descriptions}}
+     * @return {status:result code, data:DescribeIndexResponse{status,index_descriptions}}
      */
     R<DescribeIndexResponse> describeIndex(DescribeIndexParam requestParam);
 
     /**
-     * Show index building state.
+     * Show index building state(in-progress/finished/failed), failed reason.
      *
      * @param requestParam {@link GetIndexStateParam}
-     * @return {status:result code,data:GetIndexStateResponse{status,state}}
+     * @return {status:result code, data:GetIndexStateResponse{status,state}}
      */
     R<GetIndexStateResponse> getIndexState(GetIndexStateParam requestParam);
 
     /**
-     * Show index building progress.
+     * Show index building progress, such as how many rows are indexed.
      *
      * @param requestParam {@link GetIndexBuildProgressParam}
-     * @return {status:result code,data:GetIndexStateResponse{status,indexed_rows}}
+     * @return {status:result code, data:GetIndexBuildProgressResponse{status,indexed_rows}}
      */
     R<GetIndexBuildProgressResponse> getIndexBuildProgress(GetIndexBuildProgressParam requestParam);
+
+    /**
+     * Insert entities into collection. Note that you don't need to input values for auto-id field.
+     *
+     * @param requestParam {@link InsertParam}
+     * @return {status:result code, data: MutationResult{insert results}}
+     */
+    R<MutationResult> insert(InsertParam requestParam);
+
+    /**
+     * Delete entities by expression. Currently release of Milvus only support expression like "xxx in [1, 2, ...]"
+     *
+     * @param requestParam {@link DeleteParam}
+     * @return {status:result code, data: MutationResult{delete results}}
+     */
+    R<MutationResult> delete(DeleteParam requestParam);
+
+    /**
+     * Do ANN search base on a vector field. Use expression to do filtering before search.
+     *
+     * @param requestParam {@link SearchParam}
+     * @return {status:result code, data: SearchResults{topK results}}
+     */
+    R<SearchResults> search(SearchParam requestParam);
+
+    /**
+     * Query entities by expression. Note that the returned entities sequence cannot be guaranteed.
+     *
+     * @param requestParam {@link QueryParam}
+     * @return {status:result code,data: QueryResults{filter results}}
+     */
+    R<QueryResults> query(QueryParam requestParam);
+
+    /**
+     * Calculate distance between specified vectors.
+     *
+     * @param requestParam {@link CalcDistanceParam}
+     * @return {status:result code, data: CalcDistanceResults{distances}}
+     */
+    R<CalcDistanceResults> calcDistance(CalcDistanceParam requestParam);
+
+    /**
+     * Get runtime metrics information of Milvus, return in json format.
+     *
+     * @param requestParam {@link GetMetricsParam}
+     * @return {status:result code, data:GetMetricsResponse{status,metrics}}
+     */
+    R<GetMetricsResponse> getMetrics(GetMetricsParam requestParam);
+
+    /**
+     * Get information of persistent segments, including row count, persist state(growing or flushed), etc.
+     *
+     * @param requestParam {@link GetPersistentSegmentInfoParam}
+     * @return {status:result code, data:GetPersistentSegmentInfoResponse{status,info}}
+     */
+    R<GetPersistentSegmentInfoResponse> getPersistentSegmentInfo(GetPersistentSegmentInfoParam requestParam);
+
+    /**
+     * Get query information of segments in a collection, including row count, mem size, index name, etc.
+     *
+     * @param requestParam {@link GetQuerySegmentInfoParam}
+     * @return {status:result code, data:GetQuerySegmentInfoResponse{status,info}}
+     */
+    R<GetQuerySegmentInfoResponse> getQuerySegmentInfo(GetQuerySegmentInfoParam requestParam);
 }

+ 5 - 10
src/main/java/io/milvus/client/MilvusServiceClient.java

@@ -22,11 +22,10 @@ package io.milvus.client;
 import io.grpc.ConnectivityState;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
-import io.milvus.exception.ClientNotConnectedException;
 import io.milvus.grpc.MilvusServiceGrpc;
 import io.milvus.param.ConnectParam;
-import io.milvus.param.R;
 
+import lombok.NonNull;
 import java.util.concurrent.TimeUnit;
 
 public class MilvusServiceClient extends AbstractMilvusGrpcClient {
@@ -35,7 +34,7 @@ public class MilvusServiceClient extends AbstractMilvusGrpcClient {
     private final MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub;
     private final MilvusServiceGrpc.MilvusServiceFutureStub futureStub;
 
-    public MilvusServiceClient(ConnectParam connectParam) {
+    public MilvusServiceClient(@NonNull ConnectParam connectParam) {
         channel = ManagedChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort())
                 .usePlaintext()
                 .maxInboundMessageSize(Integer.MAX_VALUE)
@@ -61,17 +60,13 @@ public class MilvusServiceClient extends AbstractMilvusGrpcClient {
     @Override
     protected boolean clientIsReady() {
         ConnectivityState state = channel.getState(false);
-        switch (state) {
-            case SHUTDOWN:
-                return false;
-            default:
-                return true;
-        }
+        return state != ConnectivityState.SHUTDOWN;
     }
 
     @Override
-    public void close(long maxWaitSeconds) {
+    public void close(long maxWaitSeconds) throws InterruptedException {
         channel.shutdownNow();
+        channel.awaitTermination(maxWaitSeconds, TimeUnit.SECONDS);
     }
 }
 

+ 3 - 0
src/main/java/io/milvus/exception/ClientNotConnectedException.java

@@ -21,6 +21,9 @@ package io.milvus.exception;
 
 import io.milvus.param.R;
 
+/**
+ * Milvus client api throws this exception when client channel is closed.
+ */
 public class ClientNotConnectedException extends MilvusException {
     public ClientNotConnectedException(String msg) {
         super(msg, R.Status.ClientNotConnected.getCode());

+ 33 - 0
src/main/java/io/milvus/exception/IllegalResponseException.java

@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+package io.milvus.exception;
+
+import io.milvus.param.R;
+
+/**
+ * Some interfaces including <code>search</code>/<code>search</code>/<code>loadCollection</code> can throw this exception
+ * when server return illegal response, this may indicate a bug of server.
+ */
+public class IllegalResponseException extends MilvusException {
+    public IllegalResponseException(String msg) {
+        super(msg, R.Status.IllegalResponse.getCode());
+    }
+}

+ 3 - 0
src/main/java/io/milvus/exception/MilvusException.java

@@ -19,6 +19,9 @@
 
 package io.milvus.exception;
 
+/**
+ * Base class of Milvus exceptions.
+ */
 public abstract class MilvusException extends RuntimeException {
     protected Integer status;
 

+ 3 - 0
src/main/java/io/milvus/exception/ParamException.java

@@ -21,6 +21,9 @@ package io.milvus.exception;
 
 import io.milvus.param.R;
 
+/**
+ * Exception for caller input illegal parameters.
+ */
 public class ParamException extends MilvusException {
     public ParamException(String msg) {
         super(msg, R.Status.ParamError.getCode());

+ 99 - 32
src/main/java/io/milvus/param/ConnectParam.java

@@ -21,11 +21,11 @@ package io.milvus.param;
 
 import io.milvus.exception.ParamException;
 
-import javax.annotation.Nonnull;
+import lombok.NonNull;
 import java.util.concurrent.TimeUnit;
 
 /**
- * connectParam, timeUnit:ms
+ * Parameters for client connection.
  */
 public class ConnectParam {
     private final String host;
@@ -36,7 +36,7 @@ public class ConnectParam {
     private final boolean keepAliveWithoutCalls;
     private final long idleTimeoutMs;
 
-    private ConnectParam(@Nonnull Builder builder) {
+    private ConnectParam(@NonNull Builder builder) {
         this.host = builder.host;
         this.port = builder.port;
         this.connectTimeoutMs = builder.connectTimeoutMs;
@@ -74,6 +74,10 @@ public class ConnectParam {
         return idleTimeoutMs;
     }
 
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
     /**
      * Builder for <code>ConnectParam</code>
      */
@@ -89,66 +93,129 @@ public class ConnectParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withHost(@Nonnull String host) {
+        /**
+         * Set host name/address.
+         *
+         * @param host host name/address
+         * @return <code>Builder</code>
+         */
+        public Builder withHost(@NonNull String host) {
             this.host = host;
             return this;
         }
 
-        public Builder withPort(int port) throws IllegalArgumentException {
-            if (port < 0 || port > 0xFFFF) {
-                throw new IllegalArgumentException("Port is out of range!");
-            }
+        /**
+         * Set connection port. Port value must be larger than zero and less than 65536.
+         *
+         * @param port port value
+         * @return <code>Builder</code>
+         */
+        public Builder withPort(int port)  {
             this.port = port;
             return this;
         }
 
-        public Builder withConnectTimeout(long connectTimeout, @Nonnull TimeUnit timeUnit)
-                throws IllegalArgumentException {
-            if (connectTimeout <= 0L) {
-                throw new IllegalArgumentException("Connect timeout must be positive!");
-            }
+        /**
+         * Set connect time out value of client channel. The time out value must be larger than zero.
+         *
+         * @param connectTimeout time out value
+         * @param timeUnit time out unit
+         * @return <code>Builder</code>
+         */
+        public Builder withConnectTimeout(long connectTimeout, @NonNull TimeUnit timeUnit) {
             this.connectTimeoutMs = timeUnit.toMillis(connectTimeout);
             return this;
         }
 
-        public Builder withKeepAliveTime(long keepAliveTime, @Nonnull TimeUnit timeUnit)
-                throws IllegalArgumentException {
-            if (keepAliveTime <= 0L) {
-                throw new IllegalArgumentException("Keepalive time must be positive!");
-            }
+        /**
+         * Set keep alive time value of client channel. The time out value must be larger than zero.
+         *
+         * @param keepAliveTime time out value
+         * @param timeUnit time out unit
+         * @return <code>Builder</code>
+         */
+        public Builder withKeepAliveTime(long keepAliveTime, @NonNull TimeUnit timeUnit) {
             this.keepAliveTimeMs = timeUnit.toMillis(keepAliveTime);
             return this;
         }
 
-        public Builder withKeepAliveTimeout(long keepAliveTimeout, @Nonnull TimeUnit timeUnit)
-                throws IllegalArgumentException {
-            if (keepAliveTimeout <= 0L) {
-                throw new IllegalArgumentException("Keepalive timeout must be positive!");
-            }
+        /**
+         * Set keep alive time out value of client channel. The time out value must be larger than zero.
+         *
+         * @param keepAliveTimeout time out value
+         * @param timeUnit time out unit
+         * @return <code>Builder</code>
+         */
+        public Builder withKeepAliveTimeout(long keepAliveTimeout, @NonNull TimeUnit timeUnit) {
             this.keepAliveTimeoutMs = timeUnit.toNanos(keepAliveTimeout);
             return this;
         }
 
+        /**
+         * Set client channel keep alive.
+         *
+         * @param enable true keep alive
+         * @return <code>Builder</code>
+         */
         public Builder keepAliveWithoutCalls(boolean enable) {
             keepAliveWithoutCalls = enable;
             return this;
         }
 
-        public Builder withIdleTimeout(long idleTimeout, @Nonnull TimeUnit timeUnit)
-                throws IllegalArgumentException {
-            if (idleTimeout <= 0L) {
-                throw new IllegalArgumentException("Idle timeout must be positive!");
-            }
+        /**
+         * Set idle time out value of client channel. The time out value must be larger than zero.
+         *
+         * @param idleTimeout time out value
+         * @param timeUnit time out unit
+         * @return <code>Builder</code>
+         */
+        public Builder withIdleTimeout(long idleTimeout, @NonNull TimeUnit timeUnit) {
             this.idleTimeoutMs = timeUnit.toMillis(idleTimeout);
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>ConnectParam</code> instance.
+         *
+         * @return <code>ShowCollectionsParam</code>
+         */
         public ConnectParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(host, "Host name");
+
+            if (port < 0 || port > 0xFFFF) {
+                throw new ParamException("Port is out of range!");
+            }
+
+            if (keepAliveTimeMs <= 0L) {
+                throw new IllegalArgumentException("Keep alive time must be positive!");
+            }
+
+            if (connectTimeoutMs <= 0L) {
+                throw new IllegalArgumentException("Connect timeout must be positive!");
+            }
+
+            if (keepAliveTimeoutMs <= 0L) {
+                throw new ParamException("Keep alive timeout must be positive!");
+            }
+
+            if (idleTimeoutMs <= 0L) {
+                throw new ParamException("Idle timeout must be positive!");
+            }
+
             return new ConnectParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>ConnectParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "ConnectParam{" +
+                "host='" + host + '\'' +
+                ", port='" + port +
+                '}';
+    }
 }

+ 16 - 5
src/main/java/io/milvus/param/Constant.java

@@ -19,11 +19,9 @@
 
 package io.milvus.param;
 
-import io.milvus.grpc.DataType;
-
-import java.util.HashSet;
-import java.util.Set;
-
+/**
+ * Constant/static values for internal usage.
+ */
 public class Constant {
     // default value for search key
     public static final String VECTOR_TAG = "$0";
@@ -32,5 +30,18 @@ public class Constant {
     public static final String TOP_K = "topk";
     public static final String INDEX_TYPE = "index_type";
     public static final String METRIC_TYPE = "metric_type";
+    public static final String ROUND_DECIMAL = "round_decimal";
     public static final String PARAMS = "params";
+
+    // max value for waiting loading collection/partition interval, unit: millisecond
+    public static final Long MAX_WAITING_LOADING_INTERVAL = 2000L;
+
+    // max value for waiting loading collection/partition timeout,  unit: second
+    public static final Long MAX_WAITING_LOADING_TIMEOUT = 300L;
+
+    // max value for waiting flushing collection/partition interval, unit: millisecond
+    public static final Long MAX_WAITING_FLUSHING_INTERVAL = 2000L;
+
+    // max value for waiting flushing collection/partition timeout,  unit: second
+    public static final Long MAX_WAITING_FLUSHING_TIMEOUT = 300L;
 }

+ 0 - 65
src/main/java/io/milvus/param/Control/GetMetricsRequestParam.java

@@ -1,65 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.param.Control;
-
-import io.milvus.exception.ParamException;
-import io.milvus.param.ParamUtils;
-
-import javax.annotation.Nonnull;
-
-/**
- * @author:weilongzhao
- * @time:2021/9/4 23:15
- */
-public class GetMetricsRequestParam {
-    private final String request;
-
-    private GetMetricsRequestParam(@Nonnull Builder builder) {
-        this.request = builder.request;
-    }
-
-    public String getRequest() {
-        return request;
-    }
-
-    public static final class Builder {
-        private String request;
-
-        private Builder() {
-        }
-
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String request) {
-            this.request = request;
-            return this;
-        }
-
-        public GetMetricsRequestParam build() throws ParamException {
-            ParamUtils.CheckNullEmptyString(request, "Request string");
-
-            // TODO: check the request string is json format
-
-            return new GetMetricsRequestParam(this);
-        }
-    }
-}

+ 5 - 1
src/main/java/io/milvus/param/IndexType.java

@@ -19,6 +19,10 @@
 
 package io.milvus.param;
 
+/**
+ * Represents available index types.
+ * For more information: @see <a href="https://milvus.io/docs/v2.0.0/index_selection.md">Index Types</a>
+ */
 public enum IndexType {
     INVALID,
     IVF_FLAT,
@@ -29,7 +33,7 @@ public enum IndexType {
     RHNSW_PQ,
     RHNSW_SQ,
     ANNOY,
-    //Only supported for byte vectors
+    //Only supported for binary vectors
     BIN_IVF_FLAT,
     ;
 }

+ 5 - 1
src/main/java/io/milvus/param/MetricType.java

@@ -19,11 +19,15 @@
 
 package io.milvus.param;
 
+/**
+ * Represents available metric types.
+ * For more information: @see <a href="https://milvus.io/docs/v2.0.0/metric.md">Similarity Metrics</a>
+ */
 public enum MetricType {
     INVALID,
     L2,
     IP,
-    //Only supported for byte vectors
+    // Only supported for binary vectors
     HAMMING,
     JACCARD,
     TANIMOTO,

+ 10 - 0
src/main/java/io/milvus/param/ParamUtils.java

@@ -3,7 +3,17 @@ package io.milvus.param;
 import io.milvus.exception.ParamException;
 import org.apache.commons.lang3.StringUtils;
 
+/**
+ * Util functions for param classes
+ */
 public class ParamUtils {
+    /**
+     * Check a string is empty or null.
+     * Throws {@link ParamException} if the string is empty of null.
+     *
+     * @param target target string
+     * @param name a name to describe this string
+     */
     public static void CheckNullEmptyString(String target, String name) throws ParamException {
         if (target == null || StringUtils.isBlank(target)) {
             throw new ParamException(name + " cannot be null or empty");

+ 42 - 2
src/main/java/io/milvus/param/R.java

@@ -26,6 +26,9 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
 import java.util.Arrays;
 import java.util.Optional;
 
+/**
+ * Util class to wrap gpc response and exceptions.
+ */
 public class R<T> {
     private Exception exception;
     private Integer status;
@@ -57,6 +60,12 @@ public class R<T> {
         this.data = data;
     }
 
+    /**
+     * Wrap an exception for failure.
+     *
+     * @param exception exception object
+     * @return <code>R</code>
+     */
     public static <T> R<T> failed(Exception exception) {
         R<T> r = new R<>();
         if (exception instanceof MilvusException) {
@@ -64,11 +73,19 @@ public class R<T> {
             r.setStatus(e.getStatus());
         } else {
             r.setStatus(Status.Unknown.getCode());
+            r.exception = exception;
         }
         r.setException(exception);
         return r;
     }
 
+    /**
+     * Wrap an error code and error message for failure.
+     *
+     * @param errorCode rpc error code
+     * @param msg error message
+     * @return <code>R</code>
+     */
     public static <T> R<T> failed(ErrorCode errorCode, String msg) {
         R<T> r = new R<>();
         r.setStatus(errorCode.ordinal());
@@ -76,6 +93,13 @@ public class R<T> {
         return r;
     }
 
+    /**
+     * Wrap a status code and error message for failure.
+     *
+     * @param statusCode status code
+     * @param msg error message
+     * @return <code>R</code>
+     */
     public static <T> R<T> failed(Status statusCode, String msg) {
         R<T> r = new R<>();
         r.setStatus(statusCode.getCode());
@@ -83,13 +107,23 @@ public class R<T> {
         return r;
     }
 
+    /**
+     * Direct return a succeed status.
+     *
+     * @return <code>R</code>
+     */
     public static <T> R<T> success() {
         R<T> r = new R<>();
         r.setStatus(Status.Success.getCode());
         return r;
     }
 
-
+    /**
+     * Wrap a succeed rpc response object.
+     *
+     * @param data rpc response object
+     * @return <code>R</code>
+     */
     public static <T> R<T> success(T data) {
         R<T> r = new R<>();
         r.setStatus(Status.Success.getCode());
@@ -137,7 +171,8 @@ public class R<T> {
         ClientNotConnected(-2),
         Unknown(-3),
         VersionMismatch(-4),
-        ParamError(-5);
+        ParamError(-5),
+        IllegalResponse(-6);
 
         private final int code;
 
@@ -156,6 +191,11 @@ public class R<T> {
         }
     }
 
+    /**
+     * Construct a <code>String</code> by <code>R</code> instance.
+     *
+     * @return <code>String</code>
+     */
     @Override
     public String toString() {
         if (exception != null) {

+ 6 - 3
src/main/java/io/milvus/param/RpcStatus.java

@@ -20,9 +20,7 @@
 package io.milvus.param;
 
 /**
- * Extend partial DDL rpc invoke result
- *
- * @author changzechuan
+ * Util class to wrap a message.
  */
 public class RpcStatus {
     public static final String SUCCESS_MSG = "Success";
@@ -37,6 +35,11 @@ public class RpcStatus {
         this.msg = msg;
     }
 
+    /**
+     * Construct a <code>String</code> by <code>RpcStatus</code> instance.
+     *
+     * @return <code>String</code>
+     */
     @Override
     public String toString() {
         return "RpcStatus{" +

+ 44 - 14
src/main/java/io/milvus/param/alias/AlterAliasParam.java

@@ -3,25 +3,29 @@ package io.milvus.param.alias;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
+/**
+ * Parameters for <code>alterAlias</code> interface.
+ */
+@Getter
 public class AlterAliasParam {
     private final String collectionName;
     private final String alias;
 
-    private AlterAliasParam(@Nonnull AlterAliasParam.Builder builder) {
+    private AlterAliasParam(@NonNull AlterAliasParam.Builder builder) {
         this.collectionName = builder.collectionName;
         this.alias = builder.alias;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getAlias() {
-        return alias;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>AlterAliasParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String alias;
@@ -29,20 +33,33 @@ public class AlterAliasParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withAlias(@Nonnull String alias) {
+        /**
+         * Set alias, alias cannot be empty or null.
+         *
+         * @param alias alias of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withAlias(@NonNull String alias) {
             this.alias = alias;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>AlterAliasParam</code> instance.
+         *
+         * @return <code>AlterAliasParam</code>
+         */
         public AlterAliasParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(alias, "Alias");
@@ -50,4 +67,17 @@ public class AlterAliasParam {
             return new AlterAliasParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>AlterAliasParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "AlterAliasParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", alias='" + alias + '\'' +
+                '}';
+    }
 }

+ 44 - 14
src/main/java/io/milvus/param/alias/CreateAliasParam.java

@@ -3,25 +3,29 @@ package io.milvus.param.alias;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
+/**
+ * Parameters for <code>createAlias</code> interface.
+ */
+@Getter
 public class CreateAliasParam {
     private final String collectionName;
     private final String alias;
 
-    private CreateAliasParam(@Nonnull CreateAliasParam.Builder builder) {
+    private CreateAliasParam(@NonNull CreateAliasParam.Builder builder) {
         this.collectionName = builder.collectionName;
         this.alias = builder.alias;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getAlias() {
-        return alias;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>CreateAliasParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String alias;
@@ -29,20 +33,33 @@ public class CreateAliasParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withAlias(@Nonnull String alias) {
+        /**
+         * Set alias, alias cannot be empty or null.
+         *
+         * @param alias alias of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withAlias(@NonNull String alias) {
             this.alias = alias;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>CreateAliasParam</code> instance.
+         *
+         * @return <code>CreateAliasParam</code>
+         */
         public CreateAliasParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(alias, "Alias");
@@ -50,4 +67,17 @@ public class CreateAliasParam {
             return new CreateAliasParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>CreateAliasParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "CreateAliasParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", alias='" + alias + '\'' +
+                '}';
+    }
 }

+ 36 - 9
src/main/java/io/milvus/param/alias/DropAliasParam.java

@@ -3,38 +3,65 @@ package io.milvus.param.alias;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
+/**
+ * Parameters for <code>dropAlias</code> interface.
+ */
+@Getter
 public class DropAliasParam {
     private final String alias;
 
-    private DropAliasParam(@Nonnull Builder builder) {
+    private DropAliasParam(@NonNull Builder builder) {
         this.alias = builder.alias;
     }
 
-    public String getAlias() {
-        return alias;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DropAliasParam</code> class.
+     */
     public static final class Builder {
         private String alias;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withAlias(@Nonnull String alias) {
+        /**
+         * Set alias, alias cannot be empty or null.
+         *
+         * @param alias alias of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withAlias(@NonNull String alias) {
             this.alias = alias;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DropAliasParam</code> instance.
+         *
+         * @return <code>DropAliasParam</code>
+         */
         public DropAliasParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(alias, "Alias");
 
             return new DropAliasParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DropAliasParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DropAliasParam{" +
+                ", alias='" + alias + '\'' +
+                '}';
+    }
 }

+ 66 - 31
src/main/java/io/milvus/param/collection/CreateCollectionParam.java

@@ -22,76 +22,106 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
-import java.util.Arrays;
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * request for create collection
- *
- * @author changzechuan
+ * Parameters for <code>createCollection</code> interface.
  */
+@Getter
 public class CreateCollectionParam {
     private final String collectionName;
     private final int shardsNum;
     private final String description;
-    private final FieldType[] fieldTypes;
+    private final List<FieldType> fieldTypes;
 
-    private CreateCollectionParam(@Nonnull Builder builder) {
+    private CreateCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.shardsNum = builder.shardsNum;
         this.description = builder.description;
         this.fieldTypes = builder.fieldTypes;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public int getShardsNum() {
-        return shardsNum;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public FieldType[] getFieldTypes() {
-        return fieldTypes;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>CreateCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private int shardsNum = 2;
         private String description = "";
-        private FieldType[] fieldTypes;
+        private List<FieldType> fieldTypes = new ArrayList<>();
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Set shards number, the number must be larger than zero, default value is 2.
+         *
+         * @param shardsNum shards number to distribute insert data into multiple data nodes and query nodes.
+         * @return <code>Builder</code>
+         */
         public Builder withShardsNum(int shardsNum) {
             this.shardsNum = shardsNum;
             return this;
         }
 
-        public Builder withDescription(@Nonnull String description) {
+        /**
+         * Set collection description, description can be empty, default is "".
+         *
+         * @param description description of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withDescription(@NonNull String description) {
             this.description = description;
             return this;
         }
 
-        public Builder withFieldTypes(@Nonnull FieldType[] fieldTypes) {
+        /**
+         * Set schema of the collection, schema cannot be empty or null.
+         * @see FieldType
+         *
+         * @param fieldTypes a <code>List</code> of <code>FieldType</code>
+         * @return <code>Builder</code>
+         */
+        public Builder withFieldTypes(@NonNull List<FieldType> fieldTypes) {
             this.fieldTypes = fieldTypes;
             return this;
         }
 
+        /**
+         * Add a field schema.
+         * @see FieldType
+         *
+         * @param fieldType a <code>FieldType</code> object
+         * @return <code>Builder</code>
+         */
+        public Builder addFieldType(@NonNull FieldType fieldType) {
+            this.fieldTypes.add(fieldType);
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>CreateCollectionParam</code> instance.
+         *
+         * @return <code>CreateCollectionParam</code>
+         */
         public CreateCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
@@ -99,7 +129,7 @@ public class CreateCollectionParam {
                 throw new ParamException("ShardNum must be larger than 0");
             }
 
-            if (fieldTypes == null || fieldTypes.length <= 0) {
+            if (fieldTypes == null || fieldTypes.isEmpty()) {
                 throw new ParamException("Field numbers must be larger than 0");
             }
 
@@ -113,13 +143,18 @@ public class CreateCollectionParam {
         }
     }
 
+    /**
+     * Construct a <code>String</code> by <code>CreateCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
     @Override
     public String toString() {
         return "CreateCollectionParam{" +
                 "collectionName='" + collectionName + '\'' +
                 ", shardsNum=" + shardsNum +
                 ", description='" + description + '\'' +
-                ", fieldTypes=" + Arrays.toString(fieldTypes) +
+                ", field count=" + fieldTypes.size() +
                 '}';
     }
 }

+ 33 - 12
src/main/java/io/milvus/param/collection/DescribeCollectionParam.java

@@ -22,43 +22,64 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>describeCollection</code> interface.
  */
+@Getter
 public class DescribeCollectionParam {
     private final String collectionName;
 
-    private DescribeCollectionParam(@Nonnull Builder builder) {
+    private DescribeCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DescribeCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DescribeCollectionParam</code> instance.
+         *
+         * @return <code>DescribeCollectionParam</code>
+         */
         public DescribeCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new DescribeCollectionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DescribeCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DescribeCollectionParam{" +
+                "collectionName='" + collectionName + '\'' + '}';
+    }
 }

+ 33 - 12
src/main/java/io/milvus/param/collection/DropCollectionParam.java

@@ -22,43 +22,64 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>dropCollection</code> interface.
  */
+@Getter
 public class DropCollectionParam {
     private final String collectionName;
 
-    private DropCollectionParam(@Nonnull Builder builder) {
+    private DropCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DropCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DropCollectionParam</code> instance.
+         *
+         * @return <code>DropCollectionParam</code>
+         */
         public DropCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new DropCollectionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DropCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DropCollectionParam{" +
+                "collectionName='" + collectionName + '\'' + '}';
+    }
 }

+ 72 - 51
src/main/java/io/milvus/param/collection/FieldType.java

@@ -24,18 +24,17 @@ import io.milvus.grpc.DataType;
 import io.milvus.param.Constant;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
-import javax.xml.crypto.Data;
+import lombok.Getter;
+import lombok.NonNull;
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Field schema for collection
- *
- * @author changzechuan
+ * Parameters for a collection field.
+ * @see CreateCollectionParam
  */
+@Getter
 public class FieldType {
-    private final long fieldID;
     private final String name;
     private final boolean primaryKey;
     private final String description;
@@ -43,8 +42,7 @@ public class FieldType {
     private final Map<String,String> typeParams;
     private final boolean autoID;
 
-    private FieldType(@Nonnull Builder builder){
-        this.fieldID = builder.fieldID;
+    private FieldType(@NonNull Builder builder){
         this.name = builder.name;
         this.primaryKey = builder.primaryKey;
         this.description = builder.description;
@@ -53,81 +51,80 @@ public class FieldType {
         this.autoID = builder.autoID;
     }
 
-    public long getFieldID() {
-        return fieldID;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public boolean isPrimaryKey() {
-        return primaryKey;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public DataType getDataType() {
-        return dataType;
-    }
-
-    public Map<String, String> getTypeParams() {
-        return typeParams;
-    }
-
-    public boolean isAutoID() {
-        return autoID;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>FieldType</code> class.
+     */
     public static final class Builder {
-        private long fieldID;
         private String name;
-        private boolean primaryKey;
-        private String description;
+        private boolean primaryKey = false;
+        private String description = "";
         private DataType dataType;
         private Map<String,String> typeParams;
-        private boolean autoID;
+        private boolean autoID = false;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withFieldID(long fieldID) {
-            this.fieldID = fieldID;
-            return this;
-        }
-
-        public Builder withName(@Nonnull String name) {
+        public Builder withName(@NonNull String name) {
             this.name = name;
             return this;
         }
 
+        /**
+         * Set field to be primary key.
+         * Note that currently Milvus version only support <code>Long</code> data type as primary key.
+         *
+         * @param primaryKey true is primary key, false is not
+         * @return <code>Builder</code>
+         */
         public Builder withPrimaryKey(boolean primaryKey) {
             this.primaryKey = primaryKey;
             return this;
         }
 
-        public Builder withDescription(@Nonnull String description) {
+        /**
+         * Set field description, description can be empty, default is "".
+         *
+         * @param description description of the field
+         * @return <code>Builder</code>
+         */
+        public Builder withDescription(@NonNull String description) {
             this.description = description;
             return this;
         }
 
+        /**
+         * Set data type for field.
+         *
+         * @param dataType data type of the field
+         * @return <code>Builder</code>
+         */
         public Builder withDataType(DataType dataType) {
             this.dataType = dataType;
             return this;
         }
 
+        /**
+         * Set more parameters for field.
+         *
+         * @param typeParams parameters of the field
+         * @return <code>Builder</code>
+         */
         public Builder withTypeParams(Map<String, String> typeParams) {
             this.typeParams = typeParams;
             return this;
         }
 
-        // for vector field, for easy use
+        /**
+         * Set dimension of a vector field. Dimension value must be larger than zero.
+         *
+         * @param dimension dimension of the field
+         * @return <code>Builder</code>
+         */
         public Builder withDimension(Integer dimension) {
             if (this.typeParams == null) {
                 this.typeParams = new HashMap<>();
@@ -136,11 +133,26 @@ public class FieldType {
             return this;
         }
 
+        /**
+         * Set the field to be auto-id. Note that only primary key field can be set as auto-id.
+         * If auto-id is enabled, Milvus will automatically generated unique id for each entities,
+         * user no need to provide values for this field during insert action.
+         *
+         * If auto-id is disabled, user need to provide values for this field during insert action.
+         *
+         * @param autoID true enable auto-id, false disable auto-id
+         * @return <code>Builder</code>
+         */
         public Builder withAutoID(boolean autoID) {
             this.autoID = autoID;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>FieldType</code> instance.
+         *
+         * @return <code>FieldType</code>
+         */
         public FieldType build() throws ParamException {
             ParamUtils.CheckNullEmptyString(name, "Field name");
 
@@ -150,7 +162,16 @@ public class FieldType {
 
             if (dataType == DataType.FloatVector || dataType == DataType.BinaryVector) {
                 if (typeParams == null || !typeParams.containsKey(Constant.VECTOR_DIM)) {
-                    throw new ParamException("Vector field dimension must be larger than zero");
+                    throw new ParamException("Vector field dimension must be specified");
+                }
+
+                try {
+                    Integer dim = Integer.valueOf(typeParams.get(Constant.VECTOR_DIM));
+                    if (dim <= 0) {
+                        throw new ParamException("Vector field dimension must be larger than zero");
+                    }
+                } catch (NumberFormatException e) {
+                    throw new ParamException("Vector field dimension must be an integer number");
                 }
             }
 

+ 166 - 0
src/main/java/io/milvus/param/collection/FlushParam.java

@@ -0,0 +1,166 @@
+package io.milvus.param.collection;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.Constant;
+import io.milvus.param.ParamUtils;
+
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parameters for <code>flush</code> interface.
+ * Note that the flush interface is not exposed currently.
+ */
+@Getter
+public class FlushParam {
+    private final List<String> collectionNames;
+    private final Boolean syncFlush;
+    private final long syncFlushWaitingInterval;
+    private final long syncFlushWaitingTimeout;
+
+    private FlushParam(@NonNull Builder builder) {
+        this.collectionNames = builder.collectionNames;
+        this.syncFlush = builder.syncFlush;
+        this.syncFlushWaitingInterval = builder.syncFlushWaitingInterval.longValue();
+        this.syncFlushWaitingTimeout = builder.syncFlushWaitingTimeout.longValue();
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for <code>FlushParam</code> class.
+     */
+    public static final class Builder {
+        private List<String> collectionNames = new ArrayList<String>();
+
+        // syncFlush:
+        //   Default behavior is sync flushing, flush() return after collection finish flushing.
+        private Boolean syncFlush = Boolean.TRUE;
+
+        // syncFlushWaitingInterval:
+        //   When syncFlush is ture, flush() will wait until collection finish flushing,
+        //   this value control the waiting interval. Unit: millisecond. Default value: 500 milliseconds.
+        private Long syncFlushWaitingInterval = 500L;
+
+        // syncFlushWaitingTimeout:
+        //   When syncFlush is ture, flush() will wait until collection finish flushing,
+        //   this value control the waiting timeout. Unit: second. Default value: 60 seconds.
+        private Long syncFlushWaitingTimeout = 60L;
+
+        private Builder() {
+        }
+
+        /**
+         * Set a list of collections to be flushed.
+         *
+         * @param collectionNames a list of collections
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionNames(@NonNull List<String> collectionNames) {
+            this.collectionNames = collectionNames;
+            return this;
+        }
+
+        /**
+         * Add a collections to be flushed.
+         *
+         * @param collectionName name of the collections
+         * @return <code>Builder</code>
+         */
+        public Builder addCollectionName(@NonNull String collectionName) {
+            this.collectionNames.add(collectionName);
+            return this;
+        }
+
+        /**
+         * Set flush action to sync mode.
+         * With sync mode, the client side will keep waiting until all segments of the collection successfully flushed.
+         *
+         * If not sync mode, client will return at once after the flush() is called.
+         *
+         * @param syncFlush <code>Boolean.TRUE</code> is sync mode, Bollean.FALSE is not
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncFlush(@NonNull Boolean syncFlush) {
+            this.syncFlush = syncFlush;
+            return this;
+        }
+
+        /**
+         * Set waiting interval in sync mode. In sync mode, the client will constantly check segments state by interval.
+         * Interval must be larger than zero, and cannot be larger than Constant.MAX_WAITING_FLUSHING_INTERVAL.
+         * @see Constant
+         *
+         * @param milliseconds interval
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncFlushWaitingInterval(@NonNull Long milliseconds) {
+            this.syncFlushWaitingInterval = milliseconds;
+            return this;
+        }
+
+        /**
+         * Set time out value for sync mode.
+         * Time out value must be larger than zero, and cannot be larger than Constant.MAX_WAITING_FLUSHING_TIMEOUT.
+         * @see Constant
+         *
+         * @param seconds time out value for sync mode
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncFlushWaitingTimeout(@NonNull Long seconds) {
+            this.syncFlushWaitingTimeout = seconds;
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>FlushParam</code> instance.
+         *
+         * @return <code>FlushParam</code>
+         */
+        public FlushParam build() throws ParamException {
+            if (collectionNames == null || collectionNames.isEmpty()) {
+                throw new ParamException("CollectionNames can not be empty");
+            }
+
+            for (String name : collectionNames) {
+                ParamUtils.CheckNullEmptyString(name, "Collection name");
+            }
+
+            if (syncFlush == Boolean.TRUE) {
+                if (syncFlushWaitingInterval <= 0) {
+                    throw new ParamException("Sync flush waiting interval must be larger than zero");
+                } else if (syncFlushWaitingInterval > Constant.MAX_WAITING_FLUSHING_INTERVAL) {
+                    throw new ParamException("Sync flush waiting interval cannot be larger than "
+                            + Constant.MAX_WAITING_FLUSHING_INTERVAL.toString() + " milliseconds");
+                }
+
+                if (syncFlushWaitingTimeout <= 0) {
+                    throw new ParamException("Sync flush waiting timeout must be larger than zero");
+                } else if (syncFlushWaitingTimeout > Constant.MAX_WAITING_FLUSHING_TIMEOUT) {
+                    throw new ParamException("Sync flush waiting timeout cannot be larger than "
+                            + Constant.MAX_WAITING_FLUSHING_TIMEOUT.toString() + " seconds");
+                }
+            }
+
+            return new FlushParam(this);
+        }
+    }
+
+    /**
+     * Construct a <code>String</code> by <code>FlushParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "FlushParam{" +
+                "collectionNames='" + collectionNames + '\'' +
+                ", syncFlush=" + syncFlush.toString() +
+                ", syncFlushWaitingInterval=" + syncFlushWaitingInterval +
+                '}';
+    }
+}

+ 51 - 11
src/main/java/io/milvus/param/collection/GetCollectionStatisticsParam.java

@@ -22,43 +22,83 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>getCollectionStatistics</code> interface.
  */
+@Getter
 public class GetCollectionStatisticsParam {
     private final String collectionName;
+    private final boolean flushCollection;
 
-    private GetCollectionStatisticsParam(@Nonnull Builder builder) {
+    private GetCollectionStatisticsParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
+        this.flushCollection = builder.flushCollection;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetCollectionStatisticsParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
+        // if flushCollection is true, getCollectionStatistics() firstly call flush() and wait flush() finish
+        // Note: use default interval and timeout to wait flush()
+        private Boolean flushCollection = Boolean.TRUE;
+
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
+            this.collectionName = collectionName;
+            return this;
         }
 
-        public Builder withCollectionName(@Nonnull String collectionName) {
-            this.collectionName = collectionName;
+        /**
+         * Require a flush action before retrieving collection statistics.
+         *
+         * @param flush <code>Boolean.TRUE</code> require a flush action
+         * @return <code>Builder</code>
+         */
+        public Builder withFlush(@NonNull Boolean flush) {
+            this.flushCollection = flush;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetCollectionStatisticsParam</code> instance.
+         *
+         * @return <code>GetCollectionStatisticsParam</code>
+         */
         public GetCollectionStatisticsParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new GetCollectionStatisticsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetCollectionStatisticsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetCollectionStatisticsParam{" +
+                "collectionName='" + collectionName + '\'' +
+                " flush=" + flushCollection +
+                '}';
+    }
 }

+ 34 - 13
src/main/java/io/milvus/param/collection/HasCollectionParam.java

@@ -22,43 +22,64 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>hasCollection</code> interface.
  */
+@Getter
 public class HasCollectionParam {
     private final String collectionName;
 
-    public String getCollectionName() {
-        return collectionName;
+    private HasCollectionParam(@NonNull Builder builder) {
+        this.collectionName = builder.collectionName;
     }
 
-    private HasCollectionParam(@Nonnull Builder builder) {
-        this.collectionName = builder.collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>HasCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>HasCollectionParam</code> instance.
+         *
+         * @return <code>HasCollectionParam</code>
+         */
         public HasCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new HasCollectionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>HasCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "HasCollectionParam{" +
+                "collectionName='" + collectionName + '\'' + '}';
+    }
 }

+ 112 - 11
src/main/java/io/milvus/param/collection/LoadCollectionParam.java

@@ -20,45 +20,146 @@
 package io.milvus.param.collection;
 
 import io.milvus.exception.ParamException;
+import io.milvus.param.Constant;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>loadCollection</code> interface.
  */
+@Getter
 public class LoadCollectionParam {
     private final String collectionName;
+    private final boolean syncLoad;
+    private final long syncLoadWaitingInterval;
+    private final long syncLoadWaitingTimeout;
 
-    public LoadCollectionParam(@Nonnull Builder builder) {
+    public LoadCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
+        this.syncLoad = builder.syncLoad;
+        this.syncLoadWaitingInterval = builder.syncLoadWaitingInterval;
+        this.syncLoadWaitingTimeout = builder.syncLoadWaitingTimeout;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>LoadCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
+        // syncLoad:
+        //   Default behavior is sync loading, loadCollection() return after collection finish loading.
+        private Boolean syncLoad = Boolean.TRUE;
+
+        // syncLoadWaitingDuration:
+        //   When syncLoad is ture, loadCollection() will wait until collection finish loading,
+        //   this value control the waiting interval. Unit: millisecond. Default value: 500 milliseconds.
+        private Long syncLoadWaitingInterval = 500L;
+
+        // syncLoadWaitingTimeout:
+        //   When syncLoad is ture, loadCollection() will wait until collection finish loading,
+        //   this value control the waiting timeout. Unit: second. Default value: 60 seconds.
+        private Long syncLoadWaitingTimeout = 60L;
+
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
+            this.collectionName = collectionName;
+            return this;
         }
 
-        public Builder withCollectionName(@Nonnull String collectionName) {
-            this.collectionName = collectionName;
+        /**
+         * Set load action to sync mode.
+         * With sync mode, the client side will keep waiting until all segments of the collection successfully loaded.
+         *
+         * If not sync mode, client will return at once after the loadCollection() is called.
+         *
+         * @param syncLoad <code>Boolean.TRUE</code> is sync mode, Bollean.FALSE is not
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoad(@NonNull Boolean syncLoad) {
+            this.syncLoad = syncLoad;
             return this;
         }
 
+        /**
+         * Set waiting interval in sync mode. In sync mode, the client will constantly check collection load state by interval.
+         * Interval must be larger than zero, and cannot be larger than Constant.MAX_WAITING_LOADING_INTERVAL.
+         * @see Constant
+         *
+         * @param milliseconds interval
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoadWaitingInterval(@NonNull Long milliseconds) {
+            this.syncLoadWaitingInterval = milliseconds;
+            return this;
+        }
+
+        /**
+         * Set time out value for sync mode.
+         * Time out value must be larger than zero, and cannot be larger than Constant.MAX_WAITING_LOADING_TIMEOUT.
+         * @see Constant
+         *
+         * @param seconds time out value for sync mode
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoadWaitingTimeout(@NonNull Long seconds) {
+            this.syncLoadWaitingTimeout = seconds;
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>LoadCollectionParam</code> instance.
+         *
+         * @return <code>LoadCollectionParam</code>
+         */
         public LoadCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
+            if (syncLoad == Boolean.TRUE) {
+                if (syncLoadWaitingInterval <= 0) {
+                    throw new ParamException("Sync load waiting interval must be larger than zero");
+                } else if (syncLoadWaitingInterval > Constant.MAX_WAITING_LOADING_INTERVAL) {
+                    throw new ParamException("Sync load waiting interval cannot be larger than "
+                            + Constant.MAX_WAITING_LOADING_INTERVAL.toString() + " milliseconds");
+                }
+
+                if (syncLoadWaitingTimeout <= 0) {
+                    throw new ParamException("Sync load waiting timeout must be larger than zero");
+                } else if (syncLoadWaitingTimeout > Constant.MAX_WAITING_LOADING_TIMEOUT) {
+                    throw new ParamException("Sync load waiting timeout cannot be larger than "
+                            + Constant.MAX_WAITING_LOADING_TIMEOUT.toString() + " seconds");
+                }
+            }
+
             return new LoadCollectionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>LoadCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "LoadCollectionParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", syncLoad=" + syncLoad +
+                ", syncLoadWaitingInterval=" + syncLoadWaitingInterval +
+                '}';
+    }
 }

+ 33 - 12
src/main/java/io/milvus/param/collection/ReleaseCollectionParam.java

@@ -22,43 +22,64 @@ package io.milvus.param.collection;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create collection RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>releaseCollection</code> interface.
  */
+@Getter
 public class ReleaseCollectionParam {
     private final String collectionName;
 
-    private ReleaseCollectionParam(@Nonnull Builder builder) {
+    private ReleaseCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>ReleaseCollectionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>ReleaseCollectionParam</code> instance.
+         *
+         * @return <code>ReleaseCollectionParam</code>
+         */
         public ReleaseCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new ReleaseCollectionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>ReleaseCollectionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "ReleaseCollectionParam{" +
+                "collectionName='" + collectionName + '\'' + '}';
+    }
 }

+ 50 - 18
src/main/java/io/milvus/param/collection/ShowCollectionsParam.java

@@ -23,32 +23,33 @@ import io.milvus.exception.ParamException;
 import io.milvus.grpc.ShowType;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Params for ShowCollections RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>showCollections</code> interface.
  */
+@Getter
 public class ShowCollectionsParam {
-    private final String[] collectionNames;
+    private final List<String> collectionNames;
     private final ShowType showType;
 
-    private ShowCollectionsParam(@Nonnull Builder builder) {
+    private ShowCollectionsParam(@NonNull Builder builder) {
         this.collectionNames = builder.collectionNames;
         this.showType = builder.showType;
     }
 
-    public String[] getCollectionNames() {
-        return collectionNames;
-    }
-
-    public ShowType getShowType() {
-        return showType;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>ShowCollectionsParam</code> class.
+     */
     public static final class Builder {
-        private String[] collectionNames;
+        private List<String> collectionNames = new ArrayList<>();
         // showType:
         //   default showType = ShowType.All
         //   if collectionNames is not empty, set showType = ShowType.InMemory
@@ -57,17 +58,35 @@ public class ShowCollectionsParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
+        /**
+         * Set a list of collection names, name cannot be empty or null.
+         *
+         * @param collectionNames list of collection names
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionNames(@NonNull List<String> collectionNames) {
+            this.collectionNames = collectionNames;
+            return this;
         }
 
-        public Builder withCollectionNames(@Nonnull String[] collectionNames) {
-            this.collectionNames = collectionNames;
+        /**
+         * Add a collection name, name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder addCollectionName(@NonNull String collectionName) {
+            this.collectionNames.add(collectionName);
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>ShowCollectionsParam</code> instance.
+         *
+         * @return <code>ShowCollectionsParam</code>
+         */
         public ShowCollectionsParam build() throws ParamException {
-            if (collectionNames != null && collectionNames.length != 0) {
+            if (collectionNames != null && !collectionNames.isEmpty()) {
                 for (String collectionName : collectionNames) {
                     ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
                 }
@@ -77,4 +96,17 @@ public class ShowCollectionsParam {
             return new ShowCollectionsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>ShowCollectionsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "ShowCollectionsParam{" +
+                "collectionNames='" + collectionNames.toString() + '\'' +
+                ", showType=" + showType.toString() +
+                '}';
+    }
 }

+ 89 - 0
src/main/java/io/milvus/param/control/GetMetricsParam.java

@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.milvus.param.control;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+
+import lombok.Getter;
+import lombok.NonNull;
+
+/**
+ * Parameters for <code>getMetric</code> interface.
+ */
+@Getter
+public class GetMetricsParam {
+    private final String request;
+
+    private GetMetricsParam(@NonNull Builder builder) {
+        this.request = builder.request;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for <code>GetMetricsParam</code> class.
+     */
+    public static final class Builder {
+        private String request;
+
+        private Builder() {
+        }
+
+        /**
+         * Set request in json format to retrieve metric information from server.
+         * @see <a href="https://wiki.lfaidata.foundation/display/MIL/MEP+8+--+Add+metrics+for+proxy">Metric function design</a>
+         *
+         * @param request request string in json format
+         * @return <code>Builder</code>
+         */
+        public Builder withRequest(@NonNull String request) {
+            this.request = request;
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>GetMetricsParam</code> instance.
+         *
+         * @return <code>GetMetricsParam</code>
+         */
+        public GetMetricsParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(request, "Request string");
+
+            // TODO: check the request string is json format
+
+            return new GetMetricsParam(this);
+        }
+    }
+
+    /**
+     * Construct a <code>String</code> by <code>GetMetricsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetMetricsParam{" +
+                "request='" + request + '\'' +
+                '}';
+    }
+}

+ 35 - 12
src/main/java/io/milvus/param/Control/GetPersistentSegmentInfoParam.java → src/main/java/io/milvus/param/control/GetPersistentSegmentInfoParam.java

@@ -17,47 +17,70 @@
  * under the License.
  */
 
-package io.milvus.param.Control;
+package io.milvus.param.control;
 
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author:weilongzhao
- * @time:2021/9/4 22:20
+ * Parameters for <code>getPersistentSegmentInfo</code> interface.
  */
+@Getter
 public class GetPersistentSegmentInfoParam {
     private final String collectionName;
 
-    private GetPersistentSegmentInfoParam(@Nonnull Builder builder) {
+    private GetPersistentSegmentInfoParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetPersistentSegmentInfoParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetPersistentSegmentInfoParam</code> instance.
+         *
+         * @return <code>GetPersistentSegmentInfoParam</code>
+         */
         public GetPersistentSegmentInfoParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new GetPersistentSegmentInfoParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetPersistentSegmentInfoParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetPersistentSegmentInfoParam{" +
+                "collectionName='" + collectionName + '\'' +
+                '}';
+    }
 }

+ 35 - 12
src/main/java/io/milvus/param/Control/GetQuerySegmentInfoParam.java → src/main/java/io/milvus/param/control/GetQuerySegmentInfoParam.java

@@ -17,47 +17,70 @@
  * under the License.
  */
 
-package io.milvus.param.Control;
+package io.milvus.param.control;
 
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author:weilongzhao
- * @time:2021/9/4 23:01
+ * Parameters for <code>getQuerySegmentInfo</code> interface.
  */
+@Getter
 public class GetQuerySegmentInfoParam {
     private final String collectionName;
 
-    private GetQuerySegmentInfoParam(@Nonnull GetQuerySegmentInfoParam.Builder builder) {
+    private GetQuerySegmentInfoParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetPersistentSegmentInfoParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static GetQuerySegmentInfoParam.Builder newBuilder() {
-            return new GetQuerySegmentInfoParam.Builder();
-        }
-
-        public GetQuerySegmentInfoParam.Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetQuerySegmentInfoParam</code> instance.
+         *
+         * @return <code>GetQuerySegmentInfoParam</code>
+         */
         public GetQuerySegmentInfoParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new GetQuerySegmentInfoParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetQuerySegmentInfoParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetQuerySegmentInfoParam{" +
+                "collectionName='" + collectionName + '\'' +
+                '}';
+    }
 }

+ 66 - 34
src/main/java/io/milvus/param/dml/CalcDistanceParam.java

@@ -20,87 +20,107 @@
 package io.milvus.param.dml;
 
 import io.milvus.exception.ParamException;
-import io.milvus.param.Constant;
 import io.milvus.param.MetricType;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 import java.util.List;
 
 /**
- * currently only support float vectors calculation
+ * Parameters for <code>calcDistance</code> interface.
+ * Note that currently only support float vectors calculation.
  */
+@Getter
 public class CalcDistanceParam {
-    private final List<List<Float>> vectors_left;
-    private final List<List<Float>> vectors_right;
+    private final List<List<Float>> vectorsLeft;
+    private final List<List<Float>> vectorsRight;
     private final String metricType;
 
-    private CalcDistanceParam(@Nonnull Builder builder) {
-        this.vectors_left = builder.vectors_left;
-        this.vectors_right = builder.vectors_right;
+    private CalcDistanceParam(@NonNull Builder builder) {
+        this.vectorsLeft = builder.vectorsLeft;
+        this.vectorsRight = builder.vectorsRight;
         this.metricType = builder.metricType.name();
     }
 
-    public List<List<Float>> getVectorsLeft() {
-        return vectors_left;
-    }
-
-    public List<List<Float>> getVectorsRight() {
-        return vectors_right;
-    }
-
-    public String getMetricType() {
-        return metricType;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>CalcDistanceParam</code> class.
+     */
     public static class Builder {
-        private List<List<Float>> vectors_left;
-        private List<List<Float>> vectors_right;
+        private List<List<Float>> vectorsLeft;
+        private List<List<Float>> vectorsRight;
         private MetricType metricType;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withVectorsLeft(@Nonnull List<List<Float>> vectors) {
-            this.vectors_left = vectors;
+        /**
+         * Set a list of left side vectors. The list cannot be null or empty, each vector list cannot be null or empty.
+         *
+         * @param vectors a list of float list, each float list is a vector.
+         * @return <code>Builder</code>
+         */
+        public Builder withVectorsLeft(@NonNull List<List<Float>> vectors) {
+            this.vectorsLeft = vectors;
             return this;
         }
 
-        public Builder withVectorsRight(@Nonnull List<List<Float>> vectors) {
-            this.vectors_right = vectors;
+        /**
+         * Set a list of right side vectors. The list cannot be null or empty, each vector list cannot be null or empty.
+         *
+         * @param vectors a list of float list, each float list is a vector.
+         * @return <code>Builder</code>
+         */
+        public Builder withVectorsRight(@NonNull List<List<Float>> vectors) {
+            this.vectorsRight = vectors;
             return this;
         }
 
+        /**
+         * Set metric type of calculation. Note that currently only support L2 and IP.
+         *
+         * @param metricType metric type
+         * @return <code>Builder</code>
+         */
         public Builder withMetricType(MetricType metricType) {
             this.metricType = metricType;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>CalcDistanceParam</code> instance.
+         *
+         * @return <code>CalcDistanceParam</code>
+         */
         public CalcDistanceParam build() throws ParamException {
             if (metricType == MetricType.INVALID) {
                 throw new ParamException("Metric type is illegal");
             }
 
-            if (vectors_left == null || vectors_left.isEmpty()) {
+            if (metricType != MetricType.L2 && metricType != MetricType.IP) {
+                throw new ParamException("Only support L2 or IP metric type now!");
+            }
+
+            if (vectorsLeft == null || vectorsLeft.isEmpty()) {
                 throw new ParamException("Left vectors can not be empty");
             }
 
-            int count = vectors_left.get(0).size();
-            for (List<Float> vector : vectors_left) {
+            int count = vectorsLeft.get(0).size();
+            for (List<Float> vector : vectorsLeft) {
                 if (vector.size() != count) {
                     throw new ParamException("Left vector's dimension must be equal");
                 }
             }
 
-            if (vectors_right == null || vectors_right.isEmpty()) {
+            if (vectorsRight == null || vectorsRight.isEmpty()) {
                 throw new ParamException("Right vectors can not be empty");
             }
 
-            count = vectors_right.get(0).size();
-            for (List<Float> vector : vectors_right) {
+            count = vectorsRight.get(0).size();
+            for (List<Float> vector : vectorsRight) {
                 if (vector.size() != count) {
                     throw new ParamException("Right vector's dimension must be equal");
                 }
@@ -109,4 +129,16 @@ public class CalcDistanceParam {
             return new CalcDistanceParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>CalcDistanceParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "CalcDistanceParam{ left vector count:" + vectorsLeft.size() +
+                " right vector count:" + vectorsRight.size() +
+                '}';
+    }
 }

+ 53 - 26
src/main/java/io/milvus/param/dml/DeleteParam.java

@@ -22,39 +22,32 @@ package io.milvus.param.dml;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
+/**
+ * Parameters for <code>delete</code> interface.
+ */
+@Getter
 public class DeleteParam {
-    private final String dbName;
     private final String collectionName;
     private final String partitionName;
     private final String expr;
 
-    private DeleteParam(@Nonnull Builder builder) {
-        this.dbName = builder.dbName;
+    private DeleteParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
         this.expr = builder.expr;
     }
 
-    public String getDbName() {
-        return dbName;
-    }
-
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
-    }
-
-    public String getExpr() {
-        return expr;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DeleteParam</code> class.
+     */
     public static class Builder {
-        private String dbName = ""; // reserved
         private String collectionName;
         private String partitionName = "";
         private String expr;
@@ -62,25 +55,45 @@ public class DeleteParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Optional. Set partition name.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
-        public Builder withExpr(@Nonnull String expr) {
+        /**
+         * Set expr to filter out entities to be deleted.
+         * @see <a href="https://milvus.io/docs/v2.0.0/boolean.md">Boolean Expression Rules</a>
+         *
+         * @param expr filtering expression
+         * @return <code>Builder</code>
+         */
+        public Builder withExpr(@NonNull String expr) {
             this.expr = expr;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DeleteParam</code> instance.
+         *
+         * @return <code>DeleteParam</code>
+         */
         public DeleteParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(expr, "Expression");
@@ -88,4 +101,18 @@ public class DeleteParam {
             return new DeleteParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DeleteParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DeleteParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                ", expr='" + expr + '\'' +
+                '}';
+    }
 }

+ 83 - 42
src/main/java/io/milvus/param/dml/InsertParam.java

@@ -23,74 +23,85 @@ import io.milvus.exception.ParamException;
 import io.milvus.grpc.DataType;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 import java.nio.ByteBuffer;
 import java.util.List;
 
 /**
- * InsertParam.Field.values:
- * if dataType is scalar: values is List<Integer>, List<Long>...
- * if dataType is FloatVector: values is List<List<Float>>
- * if dataType is BinaryVector: values is List<ByteBuffer>
+ * Parameters for <code>insert</code> interface.
  */
+@Getter
 public class InsertParam {
     private final List<Field> fields;
 
     private final String collectionName;
     private final String partitionName;
-    private final int row_count;
+    private final int rowCount;
 
-    private InsertParam(@Nonnull Builder builder) {
+    private InsertParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
         this.fields = builder.fields;
-        this.row_count = builder.row_count;
+        this.rowCount = builder.rowCount;
     }
 
-    public List<Field> getFields() {
-        return fields;
-    }
-
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
-    }
-
-    public int getRowCount() {
-        return row_count;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>InsertParam</code> class.
+     */
     public static class Builder {
         private String collectionName;
         private String partitionName = "_default";
         private List<InsertParam.Field> fields;
-        private int row_count;
+        private int rowCount;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Optional. Set partition name.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
-        public Builder withFields(@Nonnull List<InsertParam.Field> fields) {
+        /**
+         * Set insert data. The fields list cannot be empty.
+         * @see InsertParam.Field
+         *
+         * @param fields insert data
+         * @return <code>Builder</code>
+         */
+        public Builder withFields(@NonNull List<InsertParam.Field> fields) {
             this.fields = fields;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>InsertParam</code> instance.
+         *
+         * @return <code>InsertParam</code>
+         */
+        @java.lang.SuppressWarnings("unchecked")
         public InsertParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
@@ -100,13 +111,15 @@ public class InsertParam {
 
             for (InsertParam.Field field : fields) {
                 if (field == null) {
-                    throw new ParamException("Field cannot be null");
+                    throw new ParamException("Field cannot be null." +
+                            " If the field is auto-id, just ignore it from withFields()");
                 }
 
                 ParamUtils.CheckNullEmptyString(field.getName(), "Field name");
 
                 if (field.getValues() == null || field.getValues().isEmpty()) {
-                    throw new ParamException("Field value cannot be empty");
+                    throw new ParamException("Field value cannot be empty." +
+                            " If the field is auto-id, just ignore it from withFields()");
                 }
             }
 
@@ -117,7 +130,7 @@ public class InsertParam {
                     throw new ParamException("Row count of fields must be equal");
                 }
             }
-            this.row_count = count;
+            this.rowCount = count;
 
             if (count == 0) {
                 throw new ParamException("Row count is zero");
@@ -127,24 +140,32 @@ public class InsertParam {
             for (InsertParam.Field field : fields) {
                 List<?> values = field.getValues();
                 if (field.getType() == DataType.FloatVector) {
-                    if (!(values.get(0) instanceof List)) {
-                        throw new ParamException("Float vector field's value must be Lst<Float>");
-                    }
-                    List first = (List) values.get(0);
-                    if (!(first.get(0) instanceof Float)) {
-                        throw new ParamException("Float vector field's value must be Lst<Float>");
+                    for (Object obj : values) {
+                        if (!(obj instanceof List)) {
+                            throw new ParamException("Float vector field's value must be Lst<Float>");
+                        }
+
+                        List<?> temp = (List<?>)obj;
+                        for (Object v : temp) {
+                            if (!(v instanceof Float)) {
+                                throw new ParamException("Float vector's value type must be Float");
+                            }
+                        }
                     }
 
+                    List<Float> first = (List<Float>) values.get(0);
                     int dim = first.size();
                     for (int i = 1; i < values.size(); ++i) {
-                        List temp = (List) values.get(i);
+                        List<Float> temp = (List<Float>) values.get(i);
                         if (dim != temp.size()) {
                             throw new ParamException("Vector dimension must be equal");
                         }
                     }
                 } else if (field.getType() == DataType.BinaryVector) {
-                    if (!(values.get(0) instanceof ByteBuffer)) {
-                        throw new ParamException("Binary vector field's value must be ByteBuffer");
+                    for (Object obj : values) {
+                        if (!(obj instanceof ByteBuffer)) {
+                            throw new ParamException("Binary vector's type must be ByteBuffer");
+                        }
                     }
 
                     ByteBuffer first = (ByteBuffer) values.get(0);
@@ -162,6 +183,26 @@ public class InsertParam {
         }
     }
 
+    /**
+     * Construct a <code>String</code> by <code>InsertParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "InsertParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                ", row_count=" + rowCount +
+                '}';
+    }
+
+    /**
+     * Internal class for insert data.
+     * if dataType is scalar(bool/int/float/double): values is List<Integer>, List<Long>...
+     * if dataType is FloatVector: values is List<List<Float>>
+     * if dataType is BinaryVector: values is List<ByteBuffer>
+     */
     public static class Field {
         private final String name;
         private final DataType type;

+ 61 - 32
src/main/java/io/milvus/param/dml/QueryParam.java

@@ -23,79 +23,94 @@ import com.google.common.collect.Lists;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * Parameters for <code>query</code> interface.
+ */
+@Getter
 public class QueryParam {
-    private final String dbName;
     private final String collectionName;
     private final List<String> partitionNames;
     private final List<String> outFields;
     private final String expr;
 
-    private QueryParam(@Nonnull Builder builder) {
-        this.dbName = builder.dbName;
+    private QueryParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
         this.outFields = builder.outFields;
         this.expr = builder.expr;
     }
 
-    public String getDbName() {
-        return dbName;
-    }
-
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public List<String> getPartitionNames() {
-        return partitionNames;
-    }
-
-    public List<String> getOutFields() {
-        return outFields;
-    }
-
-    public String getExpr() {
-        return expr;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>QueryParam</code> class.
+     */
     public static class Builder {
-        private String dbName = ""; // reserved
         private String collectionName;
         private List<String> partitionNames = Lists.newArrayList();
         private List<String> outFields = new ArrayList<>();
-        private String expr;
+        private String expr = "";
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionNames(@Nonnull List<String> partitionNames) {
+        /**
+         * Optional. Set partition names list to specify query scope.
+         *
+         * @param partitionNames partition names list
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionNames(@NonNull List<String> partitionNames) {
             this.partitionNames = partitionNames;
             return this;
         }
 
-        public Builder withOutFields(@Nonnull List<String> outFields) {
+        /**
+         * Optional. Specify output fields.
+         *
+         * @param outFields output fields
+         * @return <code>Builder</code>
+         */
+        public Builder withOutFields(@NonNull List<String> outFields) {
             this.outFields = outFields;
             return this;
         }
 
-        public Builder withExpr(@Nonnull String expr) {
+        /**
+         * Set expression to filter out entities to be queried.
+         * @see <a href="https://milvus.io/docs/v2.0.0/boolean.md">Boolean Expression Rules</a>
+         *
+         * @param expr filtering expression
+         * @return <code>Builder</code>
+         */
+        public Builder withExpr(@NonNull String expr) {
             this.expr = expr;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>QueryParam</code> instance.
+         *
+         * @return <code>QueryParam</code>
+         */
         public QueryParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(expr, "Expression");
@@ -103,4 +118,18 @@ public class QueryParam {
             return new QueryParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>QueryParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "QueryParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionNames='" + partitionNames.toString() + '\'' +
+                ", expr='" + expr + '\'' +
+                '}';
+    }
 }

+ 120 - 65
src/main/java/io/milvus/param/dml/SearchParam.java

@@ -24,32 +24,29 @@ import io.milvus.exception.ParamException;
 import io.milvus.param.MetricType;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
- * SearchParam.vectors:
- * if is FloatVector: vectors is List<List<Float>>
- * if is BinaryVector: vectors is List<ByteBuffer>
+ * Parameters for <code>search</code> interface.
  */
+@Getter
 public class SearchParam {
-    private final String dbName;
     private final String collectionName;
     private final List<String> partitionNames;
     private final String metricType;
     private final String vectorFieldName;
-    private final Integer topK;
+    private final int topK;
     private final String expr;
     private final List<String> outFields;
     private final List<?> vectors;
+    private final int roundDecimal;
     private final String params;
 
-    private SearchParam(@Nonnull Builder builder) {
-        this.dbName = builder.dbName;
+    private SearchParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
         this.metricType = builder.metricType.name();
@@ -58,114 +55,153 @@ public class SearchParam {
         this.expr = builder.expr;
         this.outFields = builder.outFields;
         this.vectors = builder.vectors;
+        this.roundDecimal = builder.roundDecimal;
         this.params = builder.params;
     }
 
-    public String getDbName() {
-        return dbName;
-    }
-
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public List<String> getPartitionNames() {
-        return partitionNames;
-    }
-
-    public String getMetricType() {
-        return metricType;
-    }
-
-    public String getVectorFieldName() {
-        return vectorFieldName;
-    }
-
-    public Integer getTopK() {
-        return topK;
-    }
-
-    public String getExpr() {
-        return expr;
-    }
-
-    public List<String> getOutFields() {
-        return outFields;
-    }
-
-    public List<?> getVectors() {
-        return vectors;
-    }
-
-    public String getParams() {
-        return params;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>SearchParam</code> class.
+     */
     public static class Builder {
-        private String dbName = ""; // reserved
         private String collectionName;
         private List<String> partitionNames = Lists.newArrayList();
         private MetricType metricType = MetricType.L2;
         private String vectorFieldName;
         private Integer topK;
-        private String expr;
+        private String expr = "";
         private List<String> outFields = new ArrayList<>();
         private List<?> vectors;
-        private String params;
+        private Integer roundDecimal = -1;
+        private String params = "{}";
 
-        private Builder() {
+       Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionNames(@Nonnull List<String> partitionNames) {
+        /**
+         * Optional. Set partition names list to specify search scope.
+         *
+         * @param partitionNames partition names list
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionNames(@NonNull List<String> partitionNames) {
             this.partitionNames = partitionNames;
             return this;
         }
 
-        public Builder withMetricType(@Nonnull MetricType metricType) {
+        /**
+         * Set metric type of ANN searching.
+         *
+         * @param metricType metric type
+         * @return <code>Builder</code>
+         */
+        public Builder withMetricType(@NonNull MetricType metricType) {
             this.metricType = metricType;
             return this;
         }
 
-        public Builder withVectorFieldName(@Nonnull String vectorFieldName) {
+        /**
+         * Set target vector field name. Field name cannot be empty or null.
+         *
+         * @param vectorFieldName vector field name
+         * @return <code>Builder</code>
+         */
+        public Builder withVectorFieldName(@NonNull String vectorFieldName) {
             this.vectorFieldName = vectorFieldName;
             return this;
         }
 
-        public Builder withTopK(@Nonnull Integer topK) {
+        /**
+         * Set topK value of ANN search.
+         *
+         * @param topK topK value
+         * @return <code>Builder</code>
+         */
+        public Builder withTopK(@NonNull Integer topK) {
             this.topK = topK;
             return this;
         }
 
-        public Builder withExpr(@Nonnull String expr) {
+        /**
+         * Optional. Set expression to filter out entities before searching.
+         * @see <a href="https://milvus.io/docs/v2.0.0/boolean.md">Boolean Expression Rules</a>
+         *
+         * @param expr filtering expression
+         * @return <code>Builder</code>
+         */
+        public Builder withExpr(@NonNull String expr) {
             this.expr = expr;
             return this;
         }
 
-        public Builder withOutFields(@Nonnull List<String> outFields) {
+        /**
+         * Optional. Specify output fields.
+         *
+         * @param outFields output fields
+         * @return <code>Builder</code>
+         */
+        public Builder withOutFields(@NonNull List<String> outFields) {
             this.outFields = outFields;
             return this;
         }
 
-        public Builder withVectors(@Nonnull List<?> vectors) {
+        /**
+         * Set target vectors.
+         *
+         * @param vectors list of target vectors
+         *                If vector type is FloatVector: vectors is List<List<Float>>
+         *                If vector type is BinaryVector: vectors is List<ByteBuffer>
+         * @return <code>Builder</code>
+         */
+        public Builder withVectors(@NonNull List<?> vectors) {
             this.vectors = vectors;
             return this;
         }
 
+        /**
+         * Specify how many digits after the decimal point for returned results.
+         *
+         * @param decimal how many digits after the decimal point
+         * @return <code>Builder</code>
+         */
+        public Builder withRoundDecimal(@NonNull Integer decimal) {
+            this.roundDecimal = decimal;
+            return this;
+        }
 
-        public Builder withParams(@Nonnull String params) {
+        /**
+         * Set extra search parameters according to index type.
+         *
+         * For example: IVF index, the extra parameters can be "{\"nprobe\":10}"
+         * For more information: @see <a href="https://milvus.io/docs/v2.0.0/index_selection.md">Index Selection</a>
+         *
+         * @param params extra parameters in json format
+         * @return <code>Builder</code>
+         */
+        public Builder withParams(@NonNull String params) {
             this.params = params;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>SearchParam</code> instance.
+         *
+         * @return <code>SearchParam</code>
+         */
         public SearchParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(vectorFieldName, "Target field name");
@@ -209,4 +245,23 @@ public class SearchParam {
             return new SearchParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>SearchParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "SearchParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionNames='" + partitionNames.toString() + '\'' +
+                ", metricType=" + metricType.toString() +
+                ", target vectors count=" + vectors.size() +
+                ", vectorFieldName='" + vectorFieldName + '\'' +
+                ", topK=" + topK +
+                ", expr='" + expr + '\'' +
+                ", params='" + params + '\'' +
+                '}';
+    }
 }

+ 67 - 24
src/main/java/io/milvus/param/index/CreateIndexParam.java

@@ -19,27 +19,27 @@
 
 package io.milvus.param.index;
 
-import com.google.api.Context;
-import com.google.api.Metric;
 import io.milvus.exception.ParamException;
 import io.milvus.param.Constant;
 import io.milvus.param.IndexType;
 import io.milvus.param.MetricType;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * @author changzechuan
+ * Parameters for <code>createIndex</code> interface.
  */
+@Getter
 public class CreateIndexParam {
     private final String collectionName;
     private final String fieldName;
     private final Map<String, String> extraParam = new HashMap<>();
 
-    private CreateIndexParam(@Nonnull Builder builder) {
+    private CreateIndexParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.fieldName = builder.fieldName;
         this.extraParam.put(Constant.INDEX_TYPE, builder.indexType.name());
@@ -47,18 +47,13 @@ public class CreateIndexParam {
         this.extraParam.put(Constant.PARAMS, builder.extraParam);
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getFieldName() {
-        return fieldName;
-    }
-
-    public Map<String, String> getExtraParam() {
-        return extraParam;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>CreateIndexParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String fieldName;
@@ -69,35 +64,69 @@ public class CreateIndexParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withFieldName(@Nonnull String fieldName) {
+        /**
+         * Set target field name. Field name cannot be empty or null.
+         *
+         * @param fieldName field name
+         * @return <code>Builder</code>
+         */
+        public Builder withFieldName(@NonNull String fieldName) {
             this.fieldName = fieldName;
             return this;
         }
 
-        public Builder withIndexType(@Nonnull IndexType indexType) {
+        /**
+         * Set index type of the index.
+         *
+         * @param indexType index type
+         * @return <code>Builder</code>
+         */
+        public Builder withIndexType(@NonNull IndexType indexType) {
             this.indexType = indexType;
             return this;
         }
 
-        public Builder withMetricType(@Nonnull MetricType metricType) {
+        /**
+         * Set metric type of the index.
+         *
+         * @param metricType metric type
+         * @return <code>Builder</code>
+         */
+        public Builder withMetricType(@NonNull MetricType metricType) {
             this.metricType = metricType;
             return this;
         }
 
-        public Builder withExtraParam(@Nonnull String extraParam) {
+        /**
+         * Set extra index parameters according to index type.
+         *
+         * For example: IVF index, the extra parameters can be "{\"nlist\":1024}"
+         * For more information: @see <a href="https://milvus.io/docs/v2.0.0/index_selection.md">Index Selection</a>
+         *
+         * @param extraParam extra parameters in json format
+         * @return <code>Builder</code>
+         */
+        public Builder withExtraParam(@NonNull String extraParam) {
             this.extraParam = extraParam;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>CreateIndexParam</code> instance.
+         *
+         * @return <code>CreateIndexParam</code>
+         */
         public CreateIndexParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(fieldName, "Field name");
@@ -115,4 +144,18 @@ public class CreateIndexParam {
             return new CreateIndexParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>CreateIndexParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "CreateIndexParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", fieldName='" + fieldName + '\'' +
+                ", params='" + extraParam.toString() + '\'' +
+                '}';
+    }
 }

+ 42 - 15
src/main/java/io/milvus/param/index/DescribeIndexParam.java

@@ -22,28 +22,29 @@ package io.milvus.param.index;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author changzechuan
+ * Parameters for <code>describeIndex</code> interface.
  */
+@Getter
 public class DescribeIndexParam {
     private final String collectionName;
     private final String fieldName;
 
-    private DescribeIndexParam(@Nonnull Builder builder) {
+    private DescribeIndexParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.fieldName = builder.fieldName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getFieldName() {
-        return fieldName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DescribeIndexParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String fieldName;
@@ -51,20 +52,33 @@ public class DescribeIndexParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withFieldName(@Nonnull String fieldName) {
+        /**
+         * Set target field name. Field name cannot be empty or null.
+         *
+         * @param fieldName field name
+         * @return <code>Builder</code>
+         */
+        public Builder withFieldName(@NonNull String fieldName) {
             this.fieldName = fieldName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DescribeIndexParam</code> instance.
+         *
+         * @return <code>DescribeIndexParam</code>
+         */
         public DescribeIndexParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(fieldName, "Field name");
@@ -72,4 +86,17 @@ public class DescribeIndexParam {
             return new DescribeIndexParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DescribeIndexParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DescribeIndexParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", fieldName='" + fieldName + '\'' +
+                '}';
+    }
 }

+ 42 - 15
src/main/java/io/milvus/param/index/DropIndexParam.java

@@ -22,28 +22,29 @@ package io.milvus.param.index;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author changzechuan
+ * Parameters for <code>dropIndex</code> interface.
  */
+@Getter
 public class DropIndexParam {
     private final String collectionName;
     private final String fieldName;
 
-    private DropIndexParam(@Nonnull Builder builder) {
+    private DropIndexParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.fieldName = builder.fieldName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getFieldName() {
-        return fieldName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DropIndexParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String fieldName;
@@ -51,20 +52,33 @@ public class DropIndexParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withFieldName(@Nonnull String fieldName) {
+        /**
+         * Set target field name. Field name cannot be empty or null.
+         *
+         * @param fieldName field name
+         * @return <code>Builder</code>
+         */
+        public Builder withFieldName(@NonNull String fieldName) {
             this.fieldName = fieldName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DropIndexParam</code> instance.
+         *
+         * @return <code>DropIndexParam</code>
+         */
         public DropIndexParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(fieldName, "Field name");
@@ -72,4 +86,17 @@ public class DropIndexParam {
             return new DropIndexParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DropIndexParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DropIndexParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", fieldName='" + fieldName + '\'' +
+                '}';
+    }
 }

+ 34 - 10
src/main/java/io/milvus/param/index/GetIndexBuildProgressParam.java

@@ -22,41 +22,65 @@ package io.milvus.param.index;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author changzechuan
+ * Parameters for <code>getIndexBuildProgress</code> interface.
  */
+@Getter
 public class GetIndexBuildProgressParam {
     private final String collectionName;
 
-    private GetIndexBuildProgressParam(@Nonnull Builder builder) {
+    private GetIndexBuildProgressParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetIndexBuildProgressParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetIndexBuildProgressParam</code> instance.
+         *
+         * @return <code>GetIndexBuildProgressParam</code>
+         */
         public GetIndexBuildProgressParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
             return new GetIndexBuildProgressParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetIndexBuildProgressParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetIndexBuildProgressParam{" +
+                "collectionName='" + collectionName + '\'' +
+                '}';
+    }
 }

+ 42 - 15
src/main/java/io/milvus/param/index/GetIndexStateParam.java

@@ -22,28 +22,29 @@ package io.milvus.param.index;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * @author changzechuan
+ * Parameters for <code>getIndexState</code> interface.
  */
+@Getter
 public class GetIndexStateParam {
     private final String collectionName;
     private final String fieldName;
 
-    private GetIndexStateParam(@Nonnull Builder builder) {
+    private GetIndexStateParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.fieldName = builder.fieldName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getFieldName() {
-        return fieldName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetIndexStateParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String fieldName;
@@ -51,20 +52,33 @@ public class GetIndexStateParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withFieldName(@Nonnull String fieldName) {
+        /**
+         * Set target field name. Field name cannot be empty or null.
+         *
+         * @param fieldName field name
+         * @return <code>Builder</code>
+         */
+        public Builder withFieldName(@NonNull String fieldName) {
             this.fieldName = fieldName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetIndexStateParam</code> instance.
+         *
+         * @return <code>GetIndexStateParam</code>
+         */
         public GetIndexStateParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(fieldName, "Field name");
@@ -72,4 +86,17 @@ public class GetIndexStateParam {
             return new GetIndexStateParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetIndexStateParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetIndexStateParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", fieldName='" + fieldName + '\'' +
+                '}';
+    }
 }

+ 42 - 17
src/main/java/io/milvus/param/partition/CreatePartitionParam.java

@@ -22,30 +22,29 @@ package io.milvus.param.partition;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for create partition RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>createPartition</code> interface.
  */
+@Getter
 public class CreatePartitionParam {
     private final String collectionName;
     private final String partitionName;
 
-    private CreatePartitionParam(@Nonnull Builder builder) {
+    private CreatePartitionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>CreatePartitionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String partitionName;
@@ -53,20 +52,33 @@ public class CreatePartitionParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Set partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>CreatePartitionParam</code> instance.
+         *
+         * @return <code>CreatePartitionParam</code>
+         */
         public CreatePartitionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(partitionName, "Partition name");
@@ -74,4 +86,17 @@ public class CreatePartitionParam {
             return new CreatePartitionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>CreatePartitionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "CreatePartitionParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                '}';
+    }
 }

+ 42 - 17
src/main/java/io/milvus/param/partition/DropPartitionParam.java

@@ -22,30 +22,29 @@ package io.milvus.param.partition;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for drop partition RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>dropPartition</code> interface.
  */
+@Getter
 public class DropPartitionParam {
     private final String collectionName;
     private final String partitionName;
 
-    private DropPartitionParam(@Nonnull Builder builder) {
+    private DropPartitionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>DropPartitionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String partitionName;
@@ -53,20 +52,33 @@ public class DropPartitionParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Set partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>DropPartitionParam</code> instance.
+         *
+         * @return <code>DropPartitionParam</code>
+         */
         public DropPartitionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(partitionName, "Partition name");
@@ -74,4 +86,17 @@ public class DropPartitionParam {
             return new DropPartitionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>DropPartitionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DropPartitionParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                '}';
+    }
 }

+ 42 - 17
src/main/java/io/milvus/param/partition/GetPartitionStatisticsParam.java

@@ -22,30 +22,29 @@ package io.milvus.param.partition;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for get partition statistics RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>getPartitionStatistics</code> interface.
  */
+@Getter
 public class GetPartitionStatisticsParam {
     private final String collectionName;
     private final String partitionName;
 
-    private GetPartitionStatisticsParam(@Nonnull Builder builder) {
+    private GetPartitionStatisticsParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>GetPartitionStatisticsParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String partitionName;
@@ -53,20 +52,33 @@ public class GetPartitionStatisticsParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Set partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>GetPartitionStatisticsParam</code> instance.
+         *
+         * @return <code>GetPartitionStatisticsParam</code>
+         */
         public GetPartitionStatisticsParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(partitionName, "Partition name");
@@ -74,4 +86,17 @@ public class GetPartitionStatisticsParam {
             return new GetPartitionStatisticsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>GetPartitionStatisticsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetPartitionStatisticsParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                '}';
+    }
 }

+ 42 - 17
src/main/java/io/milvus/param/partition/HasPartitionParam.java

@@ -22,30 +22,29 @@ package io.milvus.param.partition;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
 
 /**
- * Params for has partition RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>hasPartition</code> interface.
  */
+@Getter
 public class HasPartitionParam {
     private final String collectionName;
     private final String partitionName;
 
-    private HasPartitionParam(@Nonnull Builder builder) {
+    private HasPartitionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String getPartitionName() {
-        return partitionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>HasPartitionParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
         private String partitionName;
@@ -53,20 +52,33 @@ public class HasPartitionParam {
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionName(@Nonnull String partitionName) {
+        /**
+         * Set partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionName(@NonNull String partitionName) {
             this.partitionName = partitionName;
             return this;
         }
 
+        /**
+         * Verify parameters and create a new <code>HasPartitionParam</code> instance.
+         *
+         * @return <code>HasPartitionParam</code>
+         */
         public HasPartitionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
             ParamUtils.CheckNullEmptyString(partitionName, "Partition name");
@@ -74,4 +86,17 @@ public class HasPartitionParam {
             return new HasPartitionParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>HasPartitionParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "HasPartitionParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionName + '\'' +
+                '}';
+    }
 }

+ 138 - 20
src/main/java/io/milvus/param/partition/LoadPartitionsParam.java

@@ -20,57 +20,144 @@
 package io.milvus.param.partition;
 
 import io.milvus.exception.ParamException;
+import io.milvus.param.Constant;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import io.milvus.param.collection.LoadCollectionParam;
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Params for load partitions RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>loadPartition</code> interface.
  */
+@Getter
 public class LoadPartitionsParam {
     private final String collectionName;
-    private final String[] partitionNames;
+    private final List<String> partitionNames;
+    private final boolean syncLoad;
+    private final long syncLoadWaitingInterval;
+    private final long syncLoadWaitingTimeout;
 
-    private LoadPartitionsParam(@Nonnull Builder builder) {
+    private LoadPartitionsParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
+        this.syncLoad = builder.syncLoad;
+        this.syncLoadWaitingInterval = builder.syncLoadWaitingInterval;
+        this.syncLoadWaitingTimeout = builder.syncLoadWaitingTimeout;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String[] getPartitionNames() {
-        return partitionNames;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>LoadPartitionsParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
-        private String[] partitionNames;
+        private List<String> partitionNames = new ArrayList<>();
 
-        private Builder() {
-        }
+        // syncLoad:
+        //   Default behavior is sync loading, loadPartition() return after partition finish loading.
+        private Boolean syncLoad = Boolean.TRUE;
+
+        // syncLoadWaitingDuration:
+        //   When syncLoad is ture, loadPartition() will wait until partition finish loading,
+        //   this value control the waiting interval. Unit: millisecond. Default value: 500 milliseconds.
+        private Long syncLoadWaitingInterval = 500L;
 
-        public static Builder newBuilder() {
-            return new Builder();
+        // syncLoadWaitingTimeout:
+        //   When syncLoad is ture, loadPartition() will wait until partition finish loading,
+        //   this value control the waiting timeout. Unit: second. Default value: 60 seconds.
+        private Long syncLoadWaitingTimeout = 60L;
+
+        private Builder() {
         }
 
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionNames(@Nonnull String[] partitionNames) {
+        /**
+         * Set partition names list. Partition names list cannot be null or empty.
+         *
+         * @param partitionNames partition names list
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionNames(@NonNull List<String> partitionNames) {
             this.partitionNames = partitionNames;
             return this;
         }
 
+        /**
+         * Add a partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder addPartitionName(@NonNull String partitionName) {
+            this.partitionNames.add(partitionName);
+            return this;
+        }
+
+        /**
+         * Set load action to sync mode.
+         * With sync mode, the client side will keep waiting until all segments of the partition successfully loaded.
+         *
+         * If not sync mode, client will return at once after the loadPartitions() is called.
+         *
+         * @param syncLoad <code>Boolean.TRUE</code> is sync mode, Bollean.FALSE is not
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoad(@NonNull Boolean syncLoad) {
+            this.syncLoad = syncLoad;
+            return this;
+        }
+
+        /**
+         * Set waiting interval in sync mode. In sync mode, the client will constantly check partition load state by interval.
+         * Interval must be larger than zero, and cannot be larger than Constant.MAX_WAITING_LOADING_INTERVAL.
+         * @see Constant
+         *
+         * @param milliseconds interval
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoadWaitingInterval(@NonNull Long milliseconds) {
+            this.syncLoadWaitingInterval = milliseconds;
+            return this;
+        }
+
+        /**
+         * Set time out value for sync mode.
+         * Time out value must be larger than zero, and cannot be larger than Constant.MAX_WAITING_LOADING_TIMEOUT.
+         * @see Constant
+         *
+         * @param seconds time out value for sync mode
+         * @return <code>Builder</code>
+         */
+        public Builder withSyncLoadWaitingTimeout(@NonNull Long seconds) {
+            this.syncLoadWaitingTimeout = seconds;
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>LoadPartitionsParam</code> instance.
+         *
+         * @return <code>LoadPartitionsParam</code>
+         */
         public LoadPartitionsParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
-            if (partitionNames == null || partitionNames.length == 0) {
+            if (partitionNames == null || partitionNames.isEmpty()) {
                 throw new ParamException("Partition names cannot be empty");
             }
 
@@ -78,7 +165,38 @@ public class LoadPartitionsParam {
                 ParamUtils.CheckNullEmptyString(name, "Partition name");
             }
 
+            if (syncLoad == Boolean.TRUE) {
+                if (syncLoadWaitingInterval <= 0) {
+                    throw new ParamException("Sync load waiting interval must be larger than zero");
+                } else if (syncLoadWaitingInterval > Constant.MAX_WAITING_LOADING_INTERVAL) {
+                    throw new ParamException("Sync load waiting interval cannot be larger than "
+                            + Constant.MAX_WAITING_LOADING_INTERVAL.toString() + " milliseconds");
+                }
+
+                if (syncLoadWaitingTimeout <= 0) {
+                    throw new ParamException("Sync load waiting interval must be larger than zero");
+                } else if (syncLoadWaitingTimeout > Constant.MAX_WAITING_LOADING_TIMEOUT) {
+                    throw new ParamException("Sync load waiting interval cannot be larger than "
+                            + Constant.MAX_WAITING_LOADING_TIMEOUT.toString() + " seconds");
+                }
+            }
+
             return new LoadPartitionsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>LoadPartitionsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "LoadPartitionsParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionName='" + partitionNames.toString() + '\'' +
+                ", syncLoad=" + syncLoad +
+                ", syncLoadWaitingInterval=" + syncLoadWaitingInterval +
+                '}';
+    }
 }

+ 58 - 20
src/main/java/io/milvus/param/partition/ReleasePartitionsParam.java

@@ -22,55 +22,80 @@ package io.milvus.param.partition;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Params release partitions RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>releasePartition</code> interface.
  */
+@Getter
 public class ReleasePartitionsParam {
     private final String collectionName;
-    private final String[] partitionNames;
+    private final List<String> partitionNames;
 
-    private ReleasePartitionsParam(@Nonnull Builder builder) {
+    private ReleasePartitionsParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
     }
 
-    public String getCollectionName() {
-        return collectionName;
-    }
-
-    public String[] getPartitionNames() {
-        return partitionNames;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>ReleasePartitionsParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
-        private String[] partitionNames;
+        private List<String> partitionNames = new ArrayList<>();
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        public Builder withCollectionName(@Nonnull String collectionName) {
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
             this.collectionName = collectionName;
             return this;
         }
 
-        public Builder withPartitionNames(@Nonnull String[] partitionNames) {
+        /**
+         * Set partition names list. Partition names list cannot be null or empty.
+         *
+         * @param partitionNames partition names list
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionNames(@NonNull List<String> partitionNames) {
             this.partitionNames = partitionNames;
             return this;
         }
 
+        /**
+         * Add a partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder addPartitionName(@NonNull String partitionName) {
+            this.partitionNames.add(partitionName);
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>ReleasePartitionsParam</code> instance.
+         *
+         * @return <code>ReleasePartitionsParam</code>
+         */
         public ReleasePartitionsParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
-            if (partitionNames == null || partitionNames.length == 0) {
+            if (partitionNames == null || partitionNames.isEmpty()) {
                 throw new ParamException("Partition names cannot be empty");
             }
 
@@ -81,4 +106,17 @@ public class ReleasePartitionsParam {
             return new ReleasePartitionsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>ReleasePartitionsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "ReleasePartitionsParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionNames='" + partitionNames.toString() + '\'' +
+                '}';
+    }
 }

+ 77 - 11
src/main/java/io/milvus/param/partition/ShowPartitionsParam.java

@@ -20,45 +20,111 @@
 package io.milvus.param.partition;
 
 import io.milvus.exception.ParamException;
+import io.milvus.grpc.ShowType;
 import io.milvus.param.ParamUtils;
 
-import javax.annotation.Nonnull;
+import lombok.Getter;
+import lombok.NonNull;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Params for show partition RPC operation
- *
- * @author changzechuan
+ * Parameters for <code>showPartition</code> interface.
  */
+@Getter
 public class ShowPartitionsParam {
     private final String collectionName;
+    private final List<String> partitionNames;
+    private final ShowType showType;
 
-    private ShowPartitionsParam(@Nonnull Builder builder) {
+    private ShowPartitionsParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
+        this.partitionNames = builder.partitionNames;
+        this.showType = builder.showType;
     }
 
-    public String getCollectionName() {
-        return collectionName;
+    public static Builder newBuilder() {
+        return new Builder();
     }
 
+    /**
+     * Builder for <code>ShowPartitionsParam</code> class.
+     */
     public static final class Builder {
         private String collectionName;
+        private List<String> partitionNames = new ArrayList<>();
+
+        // showType:
+        //   default showType = ShowType.All
+        //   if partitionNames is not empty, set showType = ShowType.InMemory
+        private ShowType showType = ShowType.All;
 
         private Builder() {
         }
 
-        public static Builder newBuilder() {
-            return new Builder();
+        /**
+         * Set collection name. Collection name cannot be empty or null.
+         *
+         * @param collectionName collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withCollectionName(@NonNull String collectionName) {
+            this.collectionName = collectionName;
+            return this;
         }
 
-        public Builder withCollectionName(@Nonnull String collectionName) {
-            this.collectionName = collectionName;
+        /**
+         * Set partition names list. Partition names list cannot be null or empty.
+         *
+         * @param partitionNames partition names list
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionNames(@NonNull List<String> partitionNames) {
+            this.partitionNames = partitionNames;
             return this;
         }
 
+        /**
+         * Add a partition name. Partition name cannot be empty or null.
+         *
+         * @param partitionName partition name
+         * @return <code>Builder</code>
+         */
+        public Builder addPartitionName(@NonNull String partitionName) {
+            this.partitionNames.add(partitionName);
+            return this;
+        }
+
+        /**
+         * Verify parameters and create a new <code>ShowPartitionsParam</code> instance.
+         *
+         * @return <code>ShowPartitionsParam</code>
+         */
         public ShowPartitionsParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
+            if (partitionNames != null && !partitionNames.isEmpty()) {
+                for (String partitionName : partitionNames) {
+                    ParamUtils.CheckNullEmptyString(partitionName, "Partition name");
+                }
+                this.showType = ShowType.InMemory;
+            }
+
             return new ShowPartitionsParam(this);
         }
     }
+
+    /**
+     * Construct a <code>String</code> by <code>ShowPartitionsParam</code> instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "ShowPartitionsParam{" +
+                "collectionName='" + collectionName + '\'' +
+                ", partitionNames='" + partitionNames.toString() + '\'' +
+                ", showType=" + showType.toString() +
+                '}';
+    }
 }

+ 294 - 57
src/test/java/io/milvus/client/MilvusClientDockerTest.java

@@ -19,17 +19,17 @@
 
 package io.milvus.client;
 
-import io.milvus.grpc.DataType;
-import io.milvus.grpc.FlushResponse;
-import io.milvus.grpc.MutationResult;
-import io.milvus.grpc.SearchResults;
-import io.milvus.param.*;
-import io.milvus.param.collection.CreateCollectionParam;
-import io.milvus.param.collection.DropCollectionParam;
-import io.milvus.param.collection.FieldType;
-import io.milvus.param.collection.LoadCollectionParam;
+import io.milvus.Response.*;
+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.collection.*;
 import io.milvus.param.dml.InsertParam;
+import io.milvus.param.dml.QueryParam;
 import io.milvus.param.dml.SearchParam;
+
 import org.apache.commons.text.RandomStringGenerator;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -38,15 +38,17 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.nio.ByteBuffer;
 import java.util.*;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class MilvusClientDockerTest {
     private static final Logger logger = LogManager.getLogger("MilvusClientTest");
     private static MilvusClient client;
     private static RandomStringGenerator generator;
-    private int dimension = 128;
+    private static final int dimension = 128;
     private static final Boolean useDockerCompose = Boolean.FALSE;
 
     private static void startDockerContainer() {
@@ -105,7 +107,7 @@ public class MilvusClientDockerTest {
             logger.error("Clean up volume directory of Docker");
             FileUtils.deleteDirectory("volumes");
         } catch (Throwable t) {
-            logger.error("Failed to remove docker com", t);
+            logger.error("Failed to remove docker compose volume", t);
         }
     }
 
@@ -132,111 +134,346 @@ public class MilvusClientDockerTest {
     }
 
     private static ConnectParam.Builder connectParamBuilder(String host, int port) {
-        return ConnectParam.Builder.newBuilder().withHost(host).withPort(port);
+        return ConnectParam.newBuilder().withHost(host).withPort(port);
     }
 
-    private static void checkR(R<?> r) {
-        if (r.getStatus() != R.Status.Success.getCode()) {
-            logger.error("Error code: {}" + r.getMessage(), r.getStatus());
+    private List<List<Float>> generateFloatVectors(int count) {
+        Random ran = new Random();
+        List<List<Float>> vectors = new ArrayList<>();
+        for (int n = 0; n < count; ++n) {
+            List<Float> vector = new ArrayList<>();
+            for (int i = 0; i < dimension; ++i) {
+                vector.add(ran.nextFloat());
+            }
+            vectors.add(vector);
         }
+
+        return vectors;
+    }
+
+    private List<ByteBuffer> generateBinaryVectors(int count) {
+        Random ran = new Random();
+        List<ByteBuffer> vectors = new ArrayList<>();
+        int byteCount = dimension/8;
+        for (int n = 0; n < count; ++n) {
+            ByteBuffer vector = ByteBuffer.allocate(byteCount);
+            for (int i = 0; i < byteCount; ++i) {
+                vector.put((byte)ran.nextInt(Byte.MAX_VALUE));
+            }
+            vectors.add(vector);
+        }
+        return vectors;
+
     }
 
     @Test
-    public void testMainWorkflow() {
+    public void testFloatVectors() {
+        String randomCollectionName = generator.generate(10);
+
+        // collection schema
+        String field1Name = "int_field";
+        String field2Name = "vec_field";
+        String field3Name = "bool_field";
+        String field4Name = "double_field";
+        List<FieldType> fieldsSchema = new ArrayList<>();
+        fieldsSchema.add(FieldType.newBuilder()
+                .withPrimaryKey(true)
+                .withAutoID(false)
+                .withDataType(DataType.Int64)
+                .withName(field1Name)
+                .withDescription("identity")
+                .build());
+
+        fieldsSchema.add(FieldType.newBuilder()
+                .withDataType(DataType.FloatVector)
+                .withName(field2Name)
+                .withDescription("face")
+                .withDimension(dimension)
+                .build());
+
+        fieldsSchema.add(FieldType.newBuilder()
+                .withDataType(DataType.Bool)
+                .withName(field3Name)
+                .withDescription("gender")
+                .build());
+
+        fieldsSchema.add(FieldType.newBuilder()
+                .withDataType(DataType.Double)
+                .withName(field4Name)
+                .withDescription("weight")
+                .build());
+
+        // create collection
+        CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withDescription("test")
+                .withFieldTypes(fieldsSchema)
+                .build();
+
+        R<RpcStatus> createR = client.createCollection(createParam);
+        assertEquals(createR.getStatus().intValue(), R.Status.Success.getCode());
+
+        // insert data
+        int rowCount = 10000;
+        List<Long> ids = new ArrayList<>();
+        List<Boolean> genders = new ArrayList<>();
+        List<Double> weights = new ArrayList<>();
+        for (long i = 0L; i < rowCount; ++i) {
+            ids.add(i);
+            genders.add(i%3 == 0 ? Boolean.TRUE : Boolean.FALSE);
+            weights.add((double) (i / 100));
+        }
+        List<List<Float>> vectors = generateFloatVectors(rowCount);
+
+        List<InsertParam.Field> fieldsInsert = new ArrayList<>();
+        fieldsInsert.add(new InsertParam.Field(field1Name, DataType.Int64, ids));
+        fieldsInsert.add(new InsertParam.Field(field2Name, DataType.FloatVector, vectors));
+        fieldsInsert.add(new InsertParam.Field(field3Name, DataType.Bool, genders));
+        fieldsInsert.add(new InsertParam.Field(field4Name, DataType.Double, weights));
+
+        InsertParam insertParam = InsertParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFields(fieldsInsert)
+                .build();
+
+        R<MutationResult> insertR = client.insert(insertParam);
+        assertEquals(insertR.getStatus().intValue(), R.Status.Success.getCode());
+//        System.out.println(insertR.getData());
+
+        // get collection statistics
+        R<GetCollectionStatisticsResponse> statR = client.getCollectionStatistics(GetCollectionStatisticsParam
+                .newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFlush(true)
+                .build());
+        assertEquals(statR.getStatus().intValue(), R.Status.Success.getCode());
+
+        GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(statR.getData());
+        System.out.println("Collection row count: " + stat.GetRowCount());
+
+        // load collection
+        R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build());
+        assertEquals(loadR.getStatus().intValue(), R.Status.Success.getCode());
+
+        // query vectors to verify
+        List<Long> queryIDs = new ArrayList<>();
+        List<Boolean> compareGenders = new ArrayList<>();
+        List<Double> compareWeights = new ArrayList<>();
+        int nq = 5;
+        Random ran = new Random();
+        for (int i = 0; i < nq; ++i) {
+            int randomIndex = ran.nextInt(rowCount);
+            queryIDs.add(ids.get(randomIndex));
+            compareGenders.add(genders.get(randomIndex));
+            compareWeights.add(weights.get(randomIndex));
+        }
+        String expr = field1Name + " in " + queryIDs.toString();
+        List<String> outputFields = Arrays.asList(field1Name, field2Name, field3Name, field4Name);
+        QueryParam queryParam = QueryParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withExpr(expr)
+                .withOutFields(outputFields)
+                .build();
+
+        R<QueryResults> queryR= client.query(queryParam);
+//        System.out.println(queryR);
+        assertEquals(queryR.getStatus().intValue(), R.Status.Success.getCode());
+
+        // verify query result
+        QueryResultsWrapper queryResultsWrapper = new QueryResultsWrapper(queryR.getData());
+        for (String fieldName : outputFields) {
+            System.out.println("Query data of " + fieldName);
+            System.out.println(queryResultsWrapper.getFieldWrapper(fieldName).getFieldData());
+        }
+
+        if (outputFields.contains(field1Name)) {
+            List<?> out = queryResultsWrapper.getFieldWrapper(field1Name).getFieldData();
+            assertEquals(out.size(), nq);
+            for (Object o : out) {
+                long id = (Long) o;
+                assertTrue(queryIDs.contains(id));
+            }
+        }
+
+        // Note: the query() return vectors are not in same sequence to the input
+        // here we cannot compare vector one by one
+        if (outputFields.contains(field2Name)) {
+            assertTrue(queryResultsWrapper.getFieldWrapper(field2Name).isVectorField());
+            List<?> out = queryResultsWrapper.getFieldWrapper(field2Name).getFieldData();
+            assertEquals(out.size(), nq);
+        }
+
+        if (outputFields.contains(field3Name)) {
+            List<?> out = queryResultsWrapper.getFieldWrapper(field3Name).getFieldData();
+            assertEquals(out.size(), nq);
+            for (Object o : out) {
+                boolean b = (Boolean)o;
+                assertTrue(compareGenders.contains(b));
+            }
+        }
+
+        if (outputFields.contains(field4Name)) {
+            List<?> out = queryResultsWrapper.getFieldWrapper(field4Name).getFieldData();
+            assertEquals(out.size(), nq);
+            for (Object o : out) {
+                double d = (Double)o;
+                assertTrue(compareWeights.contains(d));
+            }
+        }
+
+
+        // pick some vectors to search
+        List<Long> targetVectorIDs = new ArrayList<>();
+        List<List<Float>> targetVectors = new ArrayList<>();
+        for (int i = 0; i < nq; ++i) {
+            int randomIndex = ran.nextInt(rowCount);
+            targetVectorIDs.add(ids.get(randomIndex));
+            targetVectors.add(vectors.get(randomIndex));
+        }
+
+        int topK = 5;
+        SearchParam searchParam = SearchParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withMetricType(MetricType.L2)
+                .withTopK(topK)
+                .withVectors(targetVectors)
+                .withVectorFieldName(field2Name)
+                .build();
+
+        R<SearchResults> searchR = client.search(searchParam);
+//        System.out.println(searchR);
+        assertEquals(searchR.getStatus().intValue(), R.Status.Success.getCode());
+
+        // verify the search result
+        SearchResultsWrapper results = new SearchResultsWrapper(searchR.getData().getResults());
+        for (int i = 0; i < targetVectors.size(); ++i) {
+            List<SearchResultsWrapper.IDScore> scores = results.GetIDScore(i);
+            System.out.println("The result of No." + i + " target vector(ID = " + targetVectorIDs.get(i) + "):");
+            System.out.println(scores);
+            assertEquals(targetVectorIDs.get(i).longValue(), scores.get(0).getLongID());
+        }
+
+        // drop collection
+        DropCollectionParam dropParam = DropCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build();
+
+        R<RpcStatus> dropR = client.dropCollection(dropParam);
+        assertEquals(dropR.getStatus().intValue(), R.Status.Success.getCode());
+    }
+
+    @Test
+    public void testBinaryVectors() {
         String randomCollectionName = generator.generate(10);
 
         // collection schema
         String field1Name = "field1";
         String field2Name = "field2";
-        FieldType[] fieldTypes = new FieldType[2];
-        fieldTypes[0] = FieldType.Builder.newBuilder().withFieldID(0l)
+        FieldType field1 = FieldType.newBuilder()
                 .withPrimaryKey(true)
+                .withAutoID(true)
                 .withDataType(DataType.Int64)
                 .withName(field1Name)
                 .withDescription("hello")
                 .build();
 
-        fieldTypes[1] = FieldType.Builder.newBuilder().withFieldID(1l)
-                .withDataType(DataType.FloatVector)
+        FieldType field2 = FieldType.newBuilder()
+                .withDataType(DataType.BinaryVector)
                 .withName(field2Name)
                 .withDescription("world")
                 .withDimension(dimension)
                 .build();
 
         // create collection
-        CreateCollectionParam createParam = CreateCollectionParam.Builder.newBuilder()
+        CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withDescription("test")
-                .withFieldTypes(fieldTypes)
+                .addFieldType(field1)
+                .addFieldType(field2)
                 .build();
 
         R<RpcStatus> createR = client.createCollection(createParam);
         assertEquals(createR.getStatus().intValue(), R.Status.Success.getCode());
 
         // insert data
-        List<Long> ids = new ArrayList<>();
-        List<List<Float>> vectors = new ArrayList<>();
-
-        Random ran=new Random();
-        for (Long i = 0L; i < 10000; ++i) {
-            ids.add(i + 100L);
-            List<Float> vector = new ArrayList<>();
-            for (int d = 0; d < dimension; ++d) {
-                vector.add(ran.nextFloat());
-            }
-            vectors.add(vector);
-        }
+        int rowCount = 10000;
+        List<ByteBuffer> vectors = generateBinaryVectors(rowCount);
 
         List<InsertParam.Field> fields = new ArrayList<>();
-        fields.add(new InsertParam.Field(field1Name, DataType.Int64, ids));
-        fields.add(new InsertParam.Field(field2Name, DataType.FloatVector, vectors));
+        // no need to provide id here since this field is auto_id
+        fields.add(new InsertParam.Field(field2Name, DataType.BinaryVector, vectors));
 
-        InsertParam insertParam = InsertParam.Builder
-                .newBuilder()
+        InsertParam insertParam = InsertParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withFields(fields)
                 .build();
 
         R<MutationResult> insertR = client.insert(insertParam);
         assertEquals(insertR.getStatus().intValue(), R.Status.Success.getCode());
+//        System.out.println(insertR.getData());
+        InsertResultWrapper insertResultWrapper = new InsertResultWrapper(insertR.getData());
+        System.out.println(insertResultWrapper.getInsertCount() + " rows inserted");
+        List<Long> ids = insertResultWrapper.getLongIDs();
+//        System.out.println("Auto-generated ids: " + ids);
+
+        // get collection statistics
+        R<GetCollectionStatisticsResponse> statR = client.getCollectionStatistics(GetCollectionStatisticsParam
+                .newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFlush(true)
+                .build());
+        assertEquals(statR.getStatus().intValue(), R.Status.Success.getCode());
 
-        // flush
-        R<FlushResponse> flushR = client.flush(randomCollectionName);
-        assertEquals(flushR.getStatus().intValue(), R.Status.Success.getCode());
+        GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(statR.getData());
+        System.out.println("Collection row count: " + stat.GetRowCount());
 
         // load collection
-        R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.Builder
-                .newBuilder()
+        R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .build());
         assertEquals(loadR.getStatus().intValue(), R.Status.Success.getCode());
 
-        // search
-        List<Float> vector = new ArrayList<>();
-        for (int i = 0; i < 2; ++i) {
-            vector.add(ran.nextFloat());
+        // pick some vectors to search
+        int nq = 5;
+        List<Long> targetVectorIDs = new ArrayList<>();
+        List<ByteBuffer> targetVectors = new ArrayList<>();
+        Random ran = new Random();
+        for (int i = 0; i < nq; ++i) {
+            int randomIndex = ran.nextInt(rowCount);
+            targetVectorIDs.add(ids.get(randomIndex));
+            targetVectors.add(vectors.get(randomIndex));
         }
 
-        List<String> outFields = Collections.singletonList(field1Name);
-        SearchParam searchParam = SearchParam.Builder.newBuilder()
+        int topK = 5;
+        SearchParam searchParam = SearchParam.newBuilder()
                 .withCollectionName(randomCollectionName)
-                .withMetricType(MetricType.L2)
-                .withOutFields(outFields)
-                .withTopK(5)
-                .withVectors(Collections.singletonList(vector))
+                .withMetricType(MetricType.HAMMING)
+                .withTopK(topK)
+                .withVectors(targetVectors)
                 .withVectorFieldName(field2Name)
                 .build();
 
-
         R<SearchResults> searchR = client.search(searchParam);
+//        System.out.println(searchR);
         assertEquals(searchR.getStatus().intValue(), R.Status.Success.getCode());
 
+        // verify the search result
+        SearchResultsWrapper results = new SearchResultsWrapper(searchR.getData().getResults());
+        for (int i = 0; i < targetVectors.size(); ++i) {
+            List<SearchResultsWrapper.IDScore> scores = results.GetIDScore(i);
+            System.out.println("The result of No." + i + " target vector(ID = " + targetVectorIDs.get(i) + "):");
+            System.out.println(scores);
+            assertEquals(targetVectorIDs.get(i).longValue(), scores.get(0).getLongID());
+        }
+
         // drop collection
-        DropCollectionParam dropParam = DropCollectionParam.Builder.newBuilder()
+        DropCollectionParam dropParam = DropCollectionParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .build();
 
-        R<RpcStatus> deleteR = client.dropCollection(dropParam);
-        assertEquals(deleteR.getStatus().intValue(), R.Status.Success.getCode());
+        R<RpcStatus> dropR = client.dropCollection(dropParam);
+        assertEquals(dropR.getStatus().intValue(), R.Status.Success.getCode());
     }
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 756 - 664
src/test/java/io/milvus/client/MilvusServiceClientTest.java


+ 4 - 9
src/test/java/io/milvus/server/MockMilvusServer.java

@@ -31,9 +31,9 @@ public class MockMilvusServer {
     private static final Logger logger = LoggerFactory.getLogger(MockMilvusServer.class.getName());
 
     private Server rpcServer;
-    private int serverPort;
+    private final int serverPort;
 
-    private MilvusServiceGrpc.MilvusServiceImplBase serviceImpl;
+    private final MilvusServiceGrpc.MilvusServiceImplBase serviceImpl;
 
     public MockMilvusServer(int port, MilvusServiceGrpc.MilvusServiceImplBase impl) {
         serverPort = port;
@@ -52,19 +52,14 @@ public class MockMilvusServer {
         }
 
         logger.info("Server started on port: " + serverPort);
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            @Override
-            public void run() {
-                MockMilvusServer.this.stop();
-            }
-        });
+        Runtime.getRuntime().addShutdownHook(new Thread(MockMilvusServer.this::stop));
     }
 
     public void stop() {
         if (rpcServer != null) {
             logger.info("RPC server is shutting down...");
             try {
-                rpcServer.shutdown().awaitTermination(1, TimeUnit.SECONDS);
+                rpcServer.shutdown().awaitTermination(10, TimeUnit.SECONDS);
             } catch (Exception e) {
                 logger.error("Failed to shutdown RPC server");
             }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است