Browse Source

Upgrade to v2.3 (#579)

Signed-off-by: yhmo <yihua.mo@zilliz.com>
groot 2 years ago
parent
commit
97cf8fa2ee
78 changed files with 5725 additions and 672 deletions
  1. 5 0
      .gitignore
  2. 113 0
      CHANGELOG.md
  3. 4 3
      README.md
  4. 9 7
      docker-compose.yml
  5. 62 72
      examples/main/java/io/milvus/GeneralExample.java
  6. 266 0
      examples/main/java/io/milvus/HighLevelExample.java
  7. 20 1
      examples/main/java/io/milvus/RBACExample.java
  8. 164 0
      examples/main/java/io/milvus/SimpleExample.java
  9. 106 0
      examples/main/java/io/milvus/TLSExample.java
  10. 24 0
      examples/main/java/io/milvus/tls/gen.sh
  11. 213 0
      examples/main/java/io/milvus/tls/openssl.cnf
  12. 2 2
      examples/pom.xml
  13. 9 4
      pom.xml
  14. 365 178
      src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java
  15. 120 0
      src/main/java/io/milvus/client/MilvusClient.java
  16. 100 1
      src/main/java/io/milvus/client/MilvusMultiServiceClient.java
  17. 558 26
      src/main/java/io/milvus/client/MilvusServiceClient.java
  18. 2 1
      src/main/java/io/milvus/common/clientenum/ConsistencyLevelEnum.java
  19. 1 1
      src/main/java/io/milvus/common/utils/JacksonUtils.java
  20. 55 0
      src/main/java/io/milvus/common/utils/URLParser.java
  21. 36 0
      src/main/java/io/milvus/common/utils/VectorUtils.java
  22. 2 1
      src/main/java/io/milvus/connection/ClusterListener.java
  23. 147 70
      src/main/java/io/milvus/param/ConnectParam.java
  24. 9 0
      src/main/java/io/milvus/param/Constant.java
  25. 37 14
      src/main/java/io/milvus/param/IndexType.java
  26. 8 0
      src/main/java/io/milvus/param/LogLevel.java
  27. 0 3
      src/main/java/io/milvus/param/MetricType.java
  28. 149 111
      src/main/java/io/milvus/param/MultiConnectParam.java
  29. 207 18
      src/main/java/io/milvus/param/ParamUtils.java
  30. 3 2
      src/main/java/io/milvus/param/bulkinsert/ListBulkInsertTasksParam.java
  31. 1 1
      src/main/java/io/milvus/param/collection/AlterCollectionParam.java
  32. 70 22
      src/main/java/io/milvus/param/collection/CreateCollectionParam.java
  33. 85 0
      src/main/java/io/milvus/param/collection/CreateDatabaseParam.java
  34. 16 10
      src/main/java/io/milvus/param/collection/DescribeCollectionParam.java
  35. 14 0
      src/main/java/io/milvus/param/collection/DropCollectionParam.java
  36. 84 0
      src/main/java/io/milvus/param/collection/DropDatabaseParam.java
  37. 44 1
      src/main/java/io/milvus/param/collection/FieldType.java
  38. 14 0
      src/main/java/io/milvus/param/collection/FlushParam.java
  39. 14 0
      src/main/java/io/milvus/param/collection/GetCollectionStatisticsParam.java
  40. 16 13
      src/main/java/io/milvus/param/collection/GetLoadStateParam.java
  41. 16 10
      src/main/java/io/milvus/param/collection/HasCollectionParam.java
  42. 16 15
      src/main/java/io/milvus/param/collection/LoadCollectionParam.java
  43. 105 0
      src/main/java/io/milvus/param/collection/RenameCollectionParam.java
  44. 8 0
      src/main/java/io/milvus/param/collection/ShowCollectionsParam.java
  45. 61 0
      src/main/java/io/milvus/param/control/GetFlushAllStateParam.java
  46. 1 1
      src/main/java/io/milvus/param/credential/UpdateCredentialParam.java
  47. 93 16
      src/main/java/io/milvus/param/dml/InsertParam.java
  48. 12 1
      src/main/java/io/milvus/param/dml/QueryParam.java
  49. 13 2
      src/main/java/io/milvus/param/dml/SearchParam.java
  50. 271 0
      src/main/java/io/milvus/param/highlevel/collection/CreateSimpleCollectionParam.java
  51. 63 0
      src/main/java/io/milvus/param/highlevel/collection/ListCollectionsParam.java
  52. 34 0
      src/main/java/io/milvus/param/highlevel/collection/response/ListCollectionsResponse.java
  53. 108 0
      src/main/java/io/milvus/param/highlevel/dml/DeleteIdsParam.java
  54. 137 0
      src/main/java/io/milvus/param/highlevel/dml/GetIdsParam.java
  55. 103 0
      src/main/java/io/milvus/param/highlevel/dml/InsertRowsParam.java
  56. 174 0
      src/main/java/io/milvus/param/highlevel/dml/QuerySimpleParam.java
  57. 205 0
      src/main/java/io/milvus/param/highlevel/dml/SearchSimpleParam.java
  58. 36 0
      src/main/java/io/milvus/param/highlevel/dml/response/DeleteResponse.java
  59. 35 0
      src/main/java/io/milvus/param/highlevel/dml/response/GetResponse.java
  60. 35 0
      src/main/java/io/milvus/param/highlevel/dml/response/InsertResponse.java
  61. 35 0
      src/main/java/io/milvus/param/highlevel/dml/response/QueryResponse.java
  62. 35 0
      src/main/java/io/milvus/param/highlevel/dml/response/SearchResponse.java
  63. 19 19
      src/main/java/io/milvus/param/index/CreateIndexParam.java
  64. 14 0
      src/main/java/io/milvus/param/index/DescribeIndexParam.java
  65. 14 0
      src/main/java/io/milvus/param/partition/LoadPartitionsParam.java
  66. 15 1
      src/main/java/io/milvus/param/role/GrantRolePrivilegeParam.java
  67. 72 0
      src/main/java/io/milvus/response/DescCollResponseWrapper.java
  68. 70 0
      src/main/java/io/milvus/response/FieldDataWrapper.java
  69. 2 2
      src/main/java/io/milvus/response/GetBulkInsertStateWrapper.java
  70. 15 0
      src/main/java/io/milvus/response/MutationResultWrapper.java
  71. 121 2
      src/main/java/io/milvus/response/QueryResultsWrapper.java
  72. 154 7
      src/main/java/io/milvus/response/SearchResultsWrapper.java
  73. 4 0
      src/main/java/io/milvus/response/ShowCollResponseWrapper.java
  74. 81 0
      src/main/java/io/milvus/response/basic/RowRecordWrapper.java
  75. 1 1
      src/main/milvus-proto
  76. 223 13
      src/test/java/io/milvus/client/MilvusClientDockerTest.java
  77. 44 4
      src/test/java/io/milvus/client/MilvusMultiClientDockerTest.java
  78. 126 16
      src/test/java/io/milvus/client/MilvusServiceClientTest.java

+ 5 - 0
.gitignore

@@ -29,3 +29,8 @@ hs_err_pid*
 target/
 volumes/
 *.iml
+
+# Example files
+examples/main/java/io/milvus/tls/*
+!examples/main/java/io/milvus/tls/gen.sh
+!examples/main/java/io/milvus/tls/openssl.cnf

+ 113 - 0
CHANGELOG.md

@@ -1,5 +1,118 @@
 # Changelog
 
+## milvus-sdk-java 2.2.12 (2023-08-10)
+
+### Improvement
+
+- Fix a bug that could not create index for scalar field with Milvus v2.2.12(change IndexType.SORT to IndexType.STL_SORT)
+
+## milvus-sdk-java 2.2.11 (2023-08-09)
+
+### Improvement
+
+- Fix a bug that could not create index for VARCHAR field with Milvus v2.2.12
+
+### Deprecated
+
+- withGuaranteeTimestamp()/withGracefulTime() are marked as Deprecated for SearchParam/QueryParam. From Milvus v2.2.9, the time settings are determined by the server side.
+
+## milvus-sdk-java 2.2.10 (2023-08-08)
+
+### Feature
+
+- Support TLS connection
+- Support retry for interface
+
+## milvus-sdk-java 2.2.9 (2023-07-03)
+
+### Improvement
+
+- Fix a bug of listBulkInsertTasks()
+- Set default shard number to be 1
+
+## milvus-sdk-java 2.2.8 (2023-06-29)
+
+### Improvement
+
+- Fix bug of high-level API
+- Add index type SORT for scalar field
+- Set log level in runtime
+
+## milvus-sdk-java 2.2.7 (2023-06-21)
+
+### Improvement
+
+- Provide easy to used high-level interfaces
+- Add more examples
+
+## milvus-sdk-java 2.2.6 (2023-06-05)
+
+### Improvement
+
+- Support JSON type field
+- Support dynamic field
+- Support partition key
+- Support database management: createDatabase/dropDatabase/listDatabases
+
+## milvus-sdk-java 2.2.5 (2023-04-04)
+
+### Improvement
+
+- Implement flushAll() interface
+- Add ignoreGrowing flag for query/search
+
+## milvus-sdk-java 2.2.4 (2023-03-26)
+
+### Improvement
+
+- Implement alterCollection() interface
+- Use the same grpc version v1.46.0 as milvus-proto repo
+
+## milvus-sdk-java 2.2.3 (2023-02-11)
+
+### Improvement
+
+- Implement getLoadState() interface
+- Add refresh parameter to load() interface
+- Add getProcess() for bulkinsert task state
+- Fix example error
+
+## milvus-sdk-java 2.2.2 (2023-01-04)
+
+### Bug
+
+- Fix search param offset not avaliable bug
+
+
+## milvus-sdk-java 2.2.1 (2022-11-22)
+
+### Improvement
+
+- Support pagination for query() interface
+- Upgrade commons-text to 1.10.0 to avoid security vulnerabilities
+
+
+## milvus-sdk-java 2.2.0 (2022-11-18)
+
+### Improvement
+
+- Supports Role-Based Access Control (RBAC)
+- Support bulk insert data
+- Support DISKANN index
+
+
+## milvus-sdk-java 2.1.0 (2022-08-31)
+
+### Bug
+
+- Fix keepAliveTimeout timeunit error for ConnectParam
+
+### Improvement
+
+- Remove withGuaranteeTimestamp/withGracefulTime of SearchParam/QueryParam. User only need to provide consistency level
+- Change the default consistency level from Strong to Bounded in SearchParam/QueryParam
+
+
 ## milvus-sdk-java 2.1.0-beta4 (2022-07-22)
 
 ### Feature

+ 4 - 3
README.md

@@ -17,7 +17,8 @@ The following table shows compatibilities between Milvus and Java SDK.
 | :------------: |:----------------:|
 |     2.0      |      2.0.4       |
 |     2.1      |   2.1.0-beta4    |
-|     2.2      |     2.2.2        |
+|     2.2.0 ~ 2.2.8      |      2.2.0 ~ 2.2.5       |
+|     >= 2.2.9      |      2.2.7 ~ 2.2.12       |
 
 ### Install Java SDK
 
@@ -29,14 +30,14 @@ You can use **Apache Maven** or **Gradle**/**Grails** to download the SDK.
         <dependency>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>2.2.2</version>
+            <version>2.2.12</version>
         </dependency>
        ```
 
    - Gradle/Grails
 
         ```gradle
-        compile 'io.milvus:milvus-sdk-java:2.2.2'
+        compile 'io.milvus:milvus-sdk-java:2.2.12'
         ```
 
 ### Examples

+ 9 - 7
docker-compose.yml

@@ -3,7 +3,7 @@ version: '3.5'
 services:
   etcd:
     container_name: milvus-javasdk-test-etcd
-    image: quay.io/coreos/etcd:v3.5.0
+    image: quay.io/coreos/etcd:v3.5.5
     volumes:
       - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
     command: etcd -listen-peer-urls=http://127.0.0.1:2380 -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 -initial-advertise-peer-urls=http://127.0.0.1:2380 --initial-cluster default=http://127.0.0.1:2380 --data-dir /etcd
@@ -17,12 +17,13 @@ services:
     image: minio/minio:RELEASE.2022-03-17T06-34-49Z
     ports:
       - "9000:9000"
+      - "9001:9001"
     environment:
       MINIO_ACCESS_KEY: minioadmin
       MINIO_SECRET_KEY: minioadmin
     volumes:
       - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
-    command: minio server /minio_data
+    command: minio server /minio_data --console-address ":9001"
     healthcheck:
       test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ]
       interval: 30s
@@ -31,7 +32,7 @@ services:
 
   standalone:
     container_name: milvus-javasdk-test-standalone
-    image: milvusdb/milvus:master-20230203-787ce7c1
+    image: milvusdb/milvus:master-20230815-ec65a4e0
     command: ["milvus", "run", "standalone"]
     environment:
       ETCD_ENDPOINTS: etcd:2379
@@ -47,7 +48,7 @@ services:
 
   etcdslave:
     container_name: milvus-javasdk-test-etcd-slave
-    image: quay.io/coreos/etcd:v3.5.0
+    image: quay.io/coreos/etcd:v3.5.5
     volumes:
       - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd-slave:/etcd
     command: etcd -listen-peer-urls=http://127.0.0.1:2380 -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 -initial-advertise-peer-urls=http://127.0.0.1:2380 --initial-cluster default=http://127.0.0.1:2380 --data-dir /etcd
@@ -60,13 +61,14 @@ services:
     container_name: milvus-javasdk-test-minio-slave
     image: minio/minio:RELEASE.2022-03-17T06-34-49Z
     ports:
-      - "9001:9000"
+      - "19000:9000"
+      - "19001:9001"
     environment:
       MINIO_ACCESS_KEY: minioadmin
       MINIO_SECRET_KEY: minioadmin
     volumes:
       - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio-slave:/minio_data
-    command: minio server /minio_data
+    command: minio server /minio_data --console-address ":9001"
     healthcheck:
       test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ]
       interval: 30s
@@ -75,7 +77,7 @@ services:
 
   standaloneslave:
     container_name: milvus-javasdk-test-slave-standalone
-    image: milvusdb/milvus:master-20230203-787ce7c1
+    image: milvusdb/milvus:master-20230815-ec65a4e0
     command: ["milvus", "run", "standalone"]
     environment:
       ETCD_ENDPOINTS: etcdslave:2379

+ 62 - 72
examples/main/java/io/milvus/GeneralExample.java

@@ -19,7 +19,10 @@
 
 package io.milvus;
 
+import com.alibaba.fastjson.JSONObject;
+import com.google.protobuf.ByteString;
 import io.milvus.client.MilvusServiceClient;
+import io.milvus.common.utils.JacksonUtils;
 import io.milvus.grpc.*;
 import io.milvus.param.*;
 import io.milvus.param.collection.*;
@@ -51,13 +54,11 @@ public class GeneralExample {
         milvusClient = new MilvusServiceClient(connectParam);
     }
 
-    private static final String COLLECTION_NAME = "TEST";
+    private static final String COLLECTION_NAME = "java_sdk_example_general";
     private static final String ID_FIELD = "userID";
     private static final String VECTOR_FIELD = "userFace";
     private static final Integer VECTOR_DIM = 64;
     private static final String AGE_FIELD = "userAge";
-//    private static final String PROFILE_FIELD = "userProfile";
-//    private static final Integer BINARY_DIM = 128;
 
     private static final String INDEX_NAME = "userFaceIndex";
     private static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
@@ -95,24 +96,17 @@ public class GeneralExample {
                 .withDataType(DataType.Int8)
                 .build();
 
-//        FieldType fieldType4 = FieldType.newBuilder()
-//                .withName(PROFILE_FIELD)
-//                .withDescription("user profile")
-//                .withDataType(DataType.BinaryVector)
-//                .withDimension(BINARY_DIM)
-//                .build();
-
         CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withDescription("customer info")
                 .withShardsNum(2)
+                .withEnableDynamicField(false)
                 .addFieldType(fieldType1)
                 .addFieldType(fieldType2)
                 .addFieldType(fieldType3)
-//                .addFieldType(fieldType4)
                 .build();
         R<RpcStatus> response = milvusClient.withTimeout(timeoutMilliseconds, TimeUnit.MILLISECONDS)
-                                            .createCollection(createCollectionReq);
+                .createCollection(createCollectionReq);
         handleResponseStatus(response);
         System.out.println(response);
         return response;
@@ -172,7 +166,7 @@ public class GeneralExample {
         // call flush() to flush the insert buffer to storage,
         // so that the getCollectionStatistics() can get correct number
         milvusClient.flush(FlushParam.newBuilder().addCollectionName(COLLECTION_NAME).build());
-        
+
         System.out.println("========== getCollectionStatistics() ==========");
         R<GetCollectionStatisticsResponse> response = milvusClient.getCollectionStatistics(
                 GetCollectionStatisticsParam.newBuilder()
@@ -249,7 +243,17 @@ public class GeneralExample {
 
     private R<RpcStatus> createIndex() {
         System.out.println("========== createIndex() ==========");
+        // create index for scalar field
         R<RpcStatus> response = milvusClient.createIndex(CreateIndexParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFieldName(AGE_FIELD)
+                .withIndexType(IndexType.STL_SORT)
+                .withSyncMode(Boolean.TRUE)
+                .build());
+        handleResponseStatus(response);
+
+        // create index for vector field
+        response = milvusClient.createIndex(CreateIndexParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
                 .withFieldName(VECTOR_FIELD)
                 .withIndexName(INDEX_NAME)
@@ -358,43 +362,6 @@ public class GeneralExample {
         return response;
     }
 
-//    private R<SearchResults> searchProfile(String expr) {
-//        System.out.println("========== searchProfile() ==========");
-//        long begin = System.currentTimeMillis();
-//
-//        List<String> outFields = Collections.singletonList(AGE_FIELD);
-//        List<ByteBuffer> vectors = generateBinaryVectors(5);
-//
-//        SearchParam searchParam = SearchParam.newBuilder()
-//                .withCollectionName(COLLECTION_NAME)
-//                .withMetricType(MetricType.HAMMING)
-//                .withOutFields(outFields)
-//                .withTopK(SEARCH_K)
-//                .withVectors(vectors)
-//                .withVectorFieldName(PROFILE_FIELD)
-//                .withExpr(expr)
-//                .withParams(SEARCH_PARAM)
-//                .build();
-//
-//
-//        R<SearchResults> response = milvusClient.search(searchParam);
-//        long end = System.currentTimeMillis();
-//        long cost = (end - begin);
-//        System.out.println("Search time cost: " + cost + "ms");
-//
-//        handleResponseStatus(response);
-//        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData().getResults());
-//        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);
-//            System.out.println("Output field data for No." + i);
-//            System.out.println(wrapper.getFieldData(AGE_FIELD, i));
-//        }
-//
-//        return response;
-//    }
-
     private R<QueryResults> query(String expr) {
         System.out.println("========== query() ==========");
         List<String> fields = Arrays.asList(ID_FIELD, AGE_FIELD);
@@ -421,10 +388,9 @@ public class GeneralExample {
         return response;
     }
 
-    private R<MutationResult> insert(String partitionName, int count) {
-        System.out.println("========== insert() ==========");
+    private R<MutationResult> insertColumns(String partitionName, int count) {
+        System.out.println("========== insertColumns() ==========");
         List<List<Float>> vectors = generateFloatVectors(count);
-//        List<ByteBuffer> profiles = generateBinaryVectors(count);
 
         Random ran = new Random();
         List<Integer> ages = new ArrayList<>();
@@ -435,7 +401,6 @@ public class GeneralExample {
         List<InsertParam.Field> fields = new ArrayList<>();
         fields.add(new InsertParam.Field(AGE_FIELD, ages));
         fields.add(new InsertParam.Field(VECTOR_FIELD, vectors));
-//        fields.add(new InsertParam.Field(PROFILE_FIELD, profiles));
 
         InsertParam insertParam = InsertParam.newBuilder()
                 .withCollectionName(COLLECTION_NAME)
@@ -448,6 +413,30 @@ public class GeneralExample {
         return response;
     }
 
+    private R<MutationResult> insertRows(String partitionName, int count) {
+        System.out.println("========== insertRows() ==========");
+
+        List<JSONObject> rowsData = new ArrayList<>();
+        Random ran = new Random();
+        for (long i = 0L; i < count; ++i) {
+            JSONObject row = new JSONObject();
+            row.put(AGE_FIELD, ran.nextInt(99));
+            row.put(VECTOR_FIELD, generateFloatVector());
+
+            rowsData.add(row);
+        }
+
+        InsertParam insertParam = InsertParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withPartitionName(partitionName)
+                .withRows(rowsData)
+                .build();
+
+        R<MutationResult> response = milvusClient.insert(insertParam);
+        handleResponseStatus(response);
+        return response;
+    }
+
     private List<List<Float>> generateFloatVectors(int count) {
         Random ran = new Random();
         List<List<Float>> vectors = new ArrayList<>();
@@ -462,19 +451,14 @@ public class GeneralExample {
         return vectors;
     }
 
-//    private List<ByteBuffer> generateBinaryVectors(int count) {
-//        Random ran = new Random();
-//        List<ByteBuffer> vectors = new ArrayList<>();
-//        int byteCount = BINARY_DIM/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;
-//    }
+    private List<Float> generateFloatVector() {
+        Random ran = new Random();
+        List<Float> vector = new ArrayList<>();
+        for (int i = 0; i < VECTOR_DIM; ++i) {
+            vector.add(ran.nextFloat());
+        }
+        return vector;
+    }
 
     public static void main(String[] args) {
         GeneralExample example = new GeneralExample();
@@ -496,11 +480,20 @@ public class GeneralExample {
         List<Long> deleteIds = new ArrayList<>();
         Random ran = new Random();
         for (int i = 0; i < 100; ++i) {
-            R<MutationResult> result = example.insert(partitionName, row_count);
+            // insertColumns
+            R<MutationResult> result = example.insertColumns(partitionName, row_count);
             MutationResultWrapper wrapper = new MutationResultWrapper(result.getData());
             List<Long> ids = wrapper.getLongIDs();
             deleteIds.add(ids.get(ran.nextInt(row_count)));
         }
+
+        // insertRows
+        R<MutationResult> result = example.insertRows(partitionName, 10);
+        MutationResultWrapper wrapper = new MutationResultWrapper(result.getData());
+        long insertCount = wrapper.getInsertCount();
+        List<Long> longIDs = wrapper.getLongIDs();
+        System.out.println("complete insertRows, insertCount:" + insertCount + "; longIDs:" + longIDs);
+
         example.getCollectionStatistics();
 
         // Must create an index before load(), FLAT is brute-force search(no index)
@@ -542,15 +535,12 @@ public class GeneralExample {
         String queryExpr = AGE_FIELD + " == 60";
         example.query(queryExpr);
 
-//        searchExpr = AGE_FIELD + " <= 30";
-//        example.searchProfile(searchExpr);
         example.compact();
         example.getCollectionStatistics();
 
-//        example.releasePartition(partitionName); // releasing partitions after loading collection is not supported currently
         example.releaseCollection();
         example.dropPartition(partitionName);
         example.dropIndex();
         example.dropCollection();
     }
-}
+}

+ 266 - 0
examples/main/java/io/milvus/HighLevelExample.java

@@ -0,0 +1,266 @@
+/*
+ * 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;
+
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
+import io.milvus.client.MilvusServiceClient;
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.common.utils.JacksonUtils;
+import io.milvus.common.utils.VectorUtils;
+import io.milvus.grpc.*;
+import io.milvus.param.*;
+import io.milvus.param.collection.*;
+import io.milvus.param.highlevel.collection.response.ListCollectionsResponse;
+import io.milvus.param.highlevel.collection.CreateSimpleCollectionParam;
+import io.milvus.param.highlevel.collection.ListCollectionsParam;
+import io.milvus.param.highlevel.dml.*;
+import io.milvus.param.highlevel.dml.response.*;
+import io.milvus.response.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Note:
+// Due do a technical limitation, the Milvus 2.0 not allow to create multi-vector-fields within a collection.
+// So this example only create a single vector field in the collection, but we suppose the next version
+// should support this function.
+///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+public class HighLevelExample {
+    private static final MilvusServiceClient milvusClient;
+
+    static {
+        ConnectParam connectParam = ConnectParam.newBuilder()
+                .withHost("localhost")
+                .withPort(19530)
+                .withAuthorization("root","Milvus")
+                .build();
+        milvusClient = new MilvusServiceClient(connectParam);
+    }
+
+    private static final String COLLECTION_NAME = "java_sdk_example_hl";
+    private static final String ID_FIELD = "userID";
+    private static final String VECTOR_FIELD = "userFace";
+    private static final String USER_JSON_FIELD = "userJson";
+    private static final Integer VECTOR_DIM = 36;
+    private static final String AGE_FIELD = "userAge";
+
+    private static final String INDEX_NAME = "userFaceIndex";
+    private static final IndexType INDEX_TYPE = IndexType.IVF_FLAT;
+    private static final String INDEX_PARAM = "{\"nlist\":128}";
+
+    private static final String INT32_FIELD_NAME = "int32";
+    private static final String INT64_FIELD_NAME = "int64";
+    private static final String VARCHAR_FIELD_NAME = "varchar";
+    private static final String BOOL_FIELD_NAME = "bool";
+    private static final String FLOAT_FIELD_NAME = "float";
+    private static final String DOUBLE_FIELD_NAME = "double";
+
+    private static void handleResponseStatus(R<?> r) {
+        if (r.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException(r.getMessage());
+        }
+    }
+
+    private R<DescribeCollectionResponse> describeCollection() {
+        System.out.println("========== describeCollection() ==========");
+        R<DescribeCollectionResponse> response = milvusClient.describeCollection(DescribeCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .build());
+        handleResponseStatus(response);
+        DescCollResponseWrapper wrapper = new DescCollResponseWrapper(response.getData());
+        System.out.println(wrapper);
+        return response;
+    }
+
+    private List<Float> generateFloatVector() {
+        Random ran = new Random();
+        List<Float> vector = new ArrayList<>();
+        for (int i = 0; i < VECTOR_DIM; ++i) {
+            vector.add(ran.nextFloat());
+        }
+        return vector;
+    }
+
+    // >>>>>>>>>>>>> high level api
+    private R<RpcStatus> createCollection() {
+        System.out.println("========== high level createCollection ==========");
+        CreateSimpleCollectionParam createSimpleCollectionParam = CreateSimpleCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withDimension(VECTOR_DIM)
+                .withPrimaryField(ID_FIELD)
+                .withVectorField(VECTOR_FIELD)
+                .withAutoId(true)
+                .build();
+
+        R<RpcStatus> response = milvusClient.createCollection(createSimpleCollectionParam);
+        handleResponseStatus(response);
+        System.out.println(JacksonUtils.toJsonString(response.getData()));
+        return response;
+    }
+
+    private R<ListCollectionsResponse> listCollections() {
+        System.out.println("========== high level listCollections ==========");
+        ListCollectionsParam listCollectionsParam = ListCollectionsParam.newBuilder()
+                .build();
+
+        R<ListCollectionsResponse> response = milvusClient.listCollections(listCollectionsParam);
+        handleResponseStatus(response);
+        System.out.println(response);
+        return response;
+    }
+
+    private R<InsertResponse> insertRows(int rowCount) {
+        System.out.println("========== high level insertRows ==========");
+        List<JSONObject> rowsData = new ArrayList<>();
+        Random ran = new Random();
+        for (long i = 0L; i < rowCount; ++i) {
+            JSONObject row = new JSONObject();
+            row.put(AGE_FIELD, ran.nextInt(99));
+            row.put(VECTOR_FIELD, generateFloatVector());
+
+            // $meta if collection EnableDynamicField, you can input this field not exist in schema, else deny
+            row.put(INT32_FIELD_NAME, ran.nextInt());
+            row.put(INT64_FIELD_NAME, ran.nextLong());
+            row.put(VARCHAR_FIELD_NAME, "测试varchar");
+            row.put(FLOAT_FIELD_NAME, ran.nextFloat());
+            row.put(DOUBLE_FIELD_NAME, ran.nextDouble());
+            row.put(BOOL_FIELD_NAME, ran.nextBoolean());
+
+            // $json
+            JSONObject jsonObject = new JSONObject();
+            jsonObject.put(INT32_FIELD_NAME, ran.nextInt());
+            jsonObject.put(INT64_FIELD_NAME, ran.nextLong());
+            jsonObject.put(VARCHAR_FIELD_NAME, "测试varchar");
+            jsonObject.put(FLOAT_FIELD_NAME, ran.nextFloat());
+            jsonObject.put(DOUBLE_FIELD_NAME, ran.nextDouble());
+            jsonObject.put(BOOL_FIELD_NAME, ran.nextBoolean());
+            row.put(USER_JSON_FIELD, jsonObject);
+
+            rowsData.add(row);
+        }
+
+        InsertRowsParam insertRowsParam = InsertRowsParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withRows(rowsData)
+                .build();
+
+        R<InsertResponse> response = milvusClient.insert(insertRowsParam);
+        handleResponseStatus(response);
+        System.out.println("insertCount: " + response.getData().getInsertCount());
+        System.out.println("insertIds: " + response.getData().getInsertIds());
+        return response;
+    }
+
+    private R<DeleteResponse> delete(List<?> ids) {
+        System.out.println("========== high level insertRows ==========");
+        DeleteIdsParam deleteIdsParam = DeleteIdsParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withPrimaryIds(ids)
+                .build();
+
+        R<DeleteResponse> response = milvusClient.delete(deleteIdsParam);
+        handleResponseStatus(response);
+        System.out.println(response);
+        return response;
+    }
+
+    private R<GetResponse> get(List<?> ids) {
+        System.out.println("========== high level get ==========");
+        GetIdsParam getParam = GetIdsParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withPrimaryIds(ids)
+                .build();
+
+        R<GetResponse> response = milvusClient.get(getParam);
+        handleResponseStatus(response);
+        for (QueryResultsWrapper.RowRecord rowRecord : response.getData().getRowRecords()) {
+            System.out.println(rowRecord);
+        }
+        return response;
+    }
+
+    private R<SearchResponse> searchSimple(String filter) {
+        System.out.println("========== high level search ==========");
+        SearchSimpleParam searchSimpleParam = SearchSimpleParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withVectors(generateFloatVector())
+                .withFilter(filter)
+                .withLimit(100L)
+                .withOffset(0L)
+                .withOutputFields(Lists.newArrayList("int32", "int64"))
+                .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
+                .build();
+
+        R<SearchResponse> response = milvusClient.search(searchSimpleParam);
+        handleResponseStatus(response);
+        for (QueryResultsWrapper.RowRecord rowRecord : response.getData().getRowRecords()) {
+            System.out.println(rowRecord);
+        }
+        return response;
+    }
+
+    private R<QueryResponse> querySimple(String filter) {
+        milvusClient.flush(FlushParam.newBuilder().addCollectionName(COLLECTION_NAME).build());
+
+        System.out.println("========== high level query ==========");
+        QuerySimpleParam querySimpleParam = QuerySimpleParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFilter(filter)
+                .withOutputFields(Lists.newArrayList("int32", "int64"))
+                .withLimit(100L)
+                .withOffset(0L)
+                .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
+                .build();
+
+        R<QueryResponse> response = milvusClient.query(querySimpleParam);
+        handleResponseStatus(response);
+        for (QueryResultsWrapper.RowRecord rowRecord : response.getData().getRowRecords()) {
+            System.out.println(rowRecord);
+        }
+        return response;
+    }
+
+    public static void main(String[] args) {
+        HighLevelExample example = new HighLevelExample();
+        example.createCollection();
+        example.listCollections();
+
+        R<DescribeCollectionResponse> describeCollectionResponseR = example.describeCollection();
+        DescCollResponseWrapper descCollResponseWrapper = new DescCollResponseWrapper(describeCollectionResponseR.getData());
+
+        int dataCount = 5;
+        R<InsertResponse> insertResponse = example.insertRows(dataCount);
+
+        List<?> insertIds = insertResponse.getData().getInsertIds();
+        example.get(insertIds);
+
+        String expr = VectorUtils.convertPksExpr(insertIds, descCollResponseWrapper);
+        example.querySimple(expr);
+        example.searchSimple(expr);
+
+        // Asynchronous deletion. A successful response does not guarantee immediate data deletion. Please wait for a certain period of time for the deletion operation to take effect.
+        example.delete(insertIds);
+    }
+
+}

+ 20 - 1
examples/main/java/io/milvus/RBACExample.java

@@ -1,3 +1,22 @@
+/*
+ * 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;
 
 import io.milvus.client.MilvusServiceClient;
@@ -116,4 +135,4 @@ public class RBACExample {
         resp = dropRole("role1");
         Validate.isTrue(resp.getStatus() == R.success().getStatus(), "drop role fail!");
     }
-}
+}

+ 164 - 0
examples/main/java/io/milvus/SimpleExample.java

@@ -0,0 +1,164 @@
+/*
+ * 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;
+
+import com.alibaba.fastjson.JSONObject;
+import io.milvus.client.MilvusServiceClient;
+import io.milvus.grpc.*;
+import io.milvus.param.*;
+import io.milvus.param.collection.*;
+import io.milvus.param.dml.*;
+import io.milvus.param.index.*;
+import io.milvus.response.*;
+import java.util.*;
+
+public class SimpleExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_simple";
+    private static final String ID_FIELD = "book_id";
+    private static final String VECTOR_FIELD = "book_intro";
+    private static final String TITLE_FIELD = "book_title";
+    private static final Integer VECTOR_DIM = 4;
+
+    public static void main(String[] args) {
+        // Connect to Milvus server. Replace the "localhost" and port with your Milvus server address.
+        MilvusServiceClient milvusClient = new MilvusServiceClient(ConnectParam.newBuilder()
+                .withHost("localhost")
+                .withPort(19530)
+                .build());
+
+        // set log level, only show errors
+        milvusClient.setLogLevel(LogLevel.Error);
+
+        // Define fields
+        List<FieldType> fieldsSchema = Arrays.asList(
+                FieldType.newBuilder()
+                        .withName(ID_FIELD)
+                        .withDataType(DataType.Int64)
+                        .withPrimaryKey(true)
+                        .withAutoID(false)
+                        .build(),
+                FieldType.newBuilder()
+                        .withName(VECTOR_FIELD)
+                        .withDataType(DataType.FloatVector)
+                        .withDimension(VECTOR_DIM)
+                        .build(),
+                FieldType.newBuilder()
+                        .withName(TITLE_FIELD)
+                        .withDataType(DataType.VarChar)
+                        .withMaxLength(64)
+                        .build()
+        );
+
+        // Create the collection with 3 fields
+        R<RpcStatus> ret = milvusClient.createCollection(CreateCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFieldTypes(fieldsSchema)
+                .build());
+        if (ret.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to create collection! Error: " + ret.getMessage());
+        }
+
+        // Specify an index type on the vector field.
+        ret = milvusClient.createIndex(CreateIndexParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFieldName(VECTOR_FIELD)
+                .withIndexType(IndexType.FLAT)
+                .withMetricType(MetricType.L2)
+                .build());
+        if (ret.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to create index on vector field! Error: " + ret.getMessage());
+        }
+
+        // Specify an index type on the varchar field.
+        ret = milvusClient.createIndex(CreateIndexParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFieldName(TITLE_FIELD)
+                .withIndexType(IndexType.TRIE)
+                .build());
+        if (ret.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to create index on varchar field! Error: " + ret.getMessage());
+        }
+
+        // Call loadCollection() to enable automatically loading data into memory for searching
+        milvusClient.loadCollection(LoadCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .build());
+
+        System.out.println("Collection created");
+
+        // Insert 10 records into the collection
+        List<JSONObject> rows = new ArrayList<>();
+        for (long i = 1L; i <= 10; ++i) {
+            JSONObject row = new JSONObject();
+            row.put(ID_FIELD, i);
+            List<Float> vector = Arrays.asList((float)i, (float)i, (float)i, (float)i);
+            row.put(VECTOR_FIELD, vector);
+            row.put(TITLE_FIELD, "Tom and Jerry " + i);
+            rows.add(row);
+        }
+
+        R<MutationResult> insertRet = milvusClient.insert(InsertParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withRows(rows)
+                .build());
+        if (insertRet.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to insert! Error: " + insertRet.getMessage());
+        }
+
+        // Call flush to make sure the inserted records are consumed by Milvus server, so that the records
+        // be searchable immediately. Just a special action in this example.
+        // In practice, you don't need to call flush() frequently.
+        milvusClient.flush(FlushParam.newBuilder()
+                .addCollectionName(COLLECTION_NAME)
+                .build());
+
+        System.out.println("10 entities inserted");
+
+        // Construct a vector to search top5 similar records, return the book title for us.
+        // This vector is equal to the No.3 record, we suppose the No.3 record is the most similar.
+        List<Float> vector = Arrays.asList(3.0f, 3.0f, 3.0f, 3.0f);
+        R<SearchResults> searchRet = milvusClient.search(SearchParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withMetricType(MetricType.L2)
+                .withTopK(5)
+                .withVectors(Arrays.asList(vector))
+                .withVectorFieldName(VECTOR_FIELD)
+                .withParams("{}")
+                .addOutField(TITLE_FIELD)
+                .build());
+        if (searchRet.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to search! Error: " + searchRet.getMessage());
+        }
+
+        // The search() allows multiple target vectors to search in a batch.
+        // Here we only input one vector to search, get the result of No.0 vector to print out
+        SearchResultsWrapper resultsWrapper = new SearchResultsWrapper(searchRet.getData().getResults());
+        List<SearchResultsWrapper.IDScore> scores = resultsWrapper.getIDScore(0);
+        System.out.println("The result of No.0 target vector:");
+        for (SearchResultsWrapper.IDScore score:scores) {
+            System.out.println(score);
+        }
+
+        // drop the collection if you don't need the collection anymore
+        milvusClient.dropCollection(DropCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .build());
+    }
+}

+ 106 - 0
examples/main/java/io/milvus/TLSExample.java

@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import com.alibaba.fastjson.JSONObject;
+import io.milvus.client.MilvusServiceClient;
+import io.milvus.grpc.*;
+import io.milvus.param.*;
+import io.milvus.param.collection.*;
+import io.milvus.param.dml.*;
+import io.milvus.param.index.*;
+import io.milvus.response.*;
+import java.util.*;
+
+
+// Note: read the following description before running this example
+// 1. cmd into the "tls" folder, generate certificate by the following commands.
+// (more details read the https://milvus.io/docs/tls.md)
+//   chmod +x gen.sh
+//   ./gen.sh
+//
+// 2. Configure the file paths of server.pem, server.key, and ca.pem for the server in config/milvus.yaml.
+//    Set tlsMode to 1 for one-way authentication. Set tlsMode to 2 for two-way authentication.
+// (read the doc to know how to config milvus: https://milvus.io/docs/configure-docker.md)
+//    tls:
+//        serverPemPath: [path_to_tls]/tls/server.pem
+//        serverKeyPath: [path_to_tls]/tls/server.key
+//        caPemPath: [path_to_tls]/tls/ca.pem
+//
+//    common:
+//        security:
+//            tlsMode: 2
+//
+// 3. Start milvus server
+// 4. Run this example.
+//    Connect server by oneWayAuth() if the server tlsMode=1, connect server by twoWayAuth() if the server tlsMode=2.
+//
+public class TLSExample {
+
+    private static void oneWayAuth() {
+        String path = ClassLoader.getSystemResource("").getPath();
+        ConnectParam connectParam = ConnectParam.newBuilder()
+                .withHost("localhost")
+                .withPort(19530)
+                .withServerName("localhost")
+                .withServerPemPath(path + "/tls/server.pem")
+                .build();
+        MilvusServiceClient milvusClient = new MilvusServiceClient(connectParam);
+
+        R<CheckHealthResponse> health = milvusClient.checkHealth();
+        if (health.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException(health.getMessage());
+        } else {
+            System.out.println(health);
+        }
+    }
+
+    private static void twoWayAuth() {
+        String path = ClassLoader.getSystemResource("").getPath();
+        ConnectParam connectParam = ConnectParam.newBuilder()
+                .withHost("localhost")
+                .withPort(19530)
+                .withServerName("localhost")
+                .withCaPemPath(path + "/tls/ca.pem")
+                .withClientKeyPath(path + "/tls/client.key")
+                .withClientPemPath(path + "/tls/client.pem")
+                .build();
+        MilvusServiceClient milvusClient = new MilvusServiceClient(connectParam);
+
+        R<CheckHealthResponse> health = milvusClient.checkHealth();
+        if (health.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException(health.getMessage());
+        } else {
+            System.out.println(health);
+        }
+    }
+
+    // tlsMode=1, set oneWay=true
+    // tlsMode=2, set oneWay=false
+    private static final boolean oneWay = false;
+
+    public static void main(String[] args) {
+        if (oneWay) {
+            oneWayAuth();
+        } else {
+            twoWayAuth();
+        }
+    }
+}

+ 24 - 0
examples/main/java/io/milvus/tls/gen.sh

@@ -0,0 +1,24 @@
+Country="CN"
+State="Shanghai"
+Location="Shanghai"
+Organization="milvus"
+Organizational="milvus"
+CommonName="localhost"
+
+echo "generate ca.key"
+openssl genrsa -out ca.key 2048
+
+echo "generate ca.pem"
+openssl req -new -x509 -key ca.key -out ca.pem -days 3650 -subj "/C=$Country/ST=$State/L=$Location/O=$Organization/OU=$Organizational/CN=$CommonName"
+
+echo "generate server SAN certificate"
+openssl genpkey -algorithm RSA -out server.key
+openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=$Country/O=$Organization/OU=$Organizational/CN=$CommonName" -config ./openssl.cnf -extensions v3_req
+openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
+
+echo "generate client SAN certificate"
+openssl genpkey -algorithm RSA -out client.key
+openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=$Country/O=$Organization/OU=$Organizational/CN=$CommonName" -config ./openssl.cnf -extensions v3_req
+openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
+
+

+ 213 - 0
examples/main/java/io/milvus/tls/openssl.cnf

@@ -0,0 +1,213 @@
+
+HOME			= .
+RANDFILE		= $ENV::HOME/.rnd
+
+oid_section		= new_oids
+
+
+[ new_oids ]
+
+
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+[ ca ]
+default_ca	= CA_default		# The default ca section
+
+[ CA_default ]
+
+dir		= ./demoCA		# Where everything is kept
+certs		= $dir/certs		# Where the issued certs are kept
+crl_dir		= $dir/crl		# Where the issued crl are kept
+database	= $dir/index.txt	# database index file.
+					# several ctificates with same subject.
+new_certs_dir	= $dir/newcerts		# default place for new certs.
+
+certificate	= $dir/cacert.pem 	# The CA certificate
+serial		= $dir/serial 		# The current serial number
+crlnumber	= $dir/crlnumber	# the current crl number
+					# must be commented out to leave a V1 CRL
+crl		= $dir/crl.pem 		# The current CRL
+private_key	= $dir/private/cakey.pem# The private key
+RANDFILE	= $dir/private/.rand	# private random number file
+
+x509_extensions	= usr_cert		# The extentions to add to the cert
+
+name_opt 	= ca_default		# Subject Name options
+cert_opt 	= ca_default		# Certificate field options
+
+copy_extensions = copy
+
+
+default_days	= 365			# how long to certify for
+default_crl_days= 30			# how long before next CRL
+default_md	= default		# use public key default MD
+preserve	= no			# keep passed DN ordering
+
+policy		= policy_match
+
+[ policy_match ]
+countryName		= match
+stateOrProvinceName	= match
+organizationName	= match
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+[ policy_anything ]
+countryName		= optional
+stateOrProvinceName	= optional
+localityName		= optional
+organizationName	= optional
+organizationalUnitName	= optional
+commonName		= supplied
+emailAddress		= optional
+
+[ req ]
+default_bits		= 2048
+default_keyfile 	= privkey.pem
+distinguished_name	= req_distinguished_name
+attributes		= req_attributes
+x509_extensions	= v3_ca	# The extentions to add to the self signed cert
+
+
+string_mask = utf8only
+
+req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName			= Country Name (2 letter code)
+countryName_default		= AU
+countryName_min			= 2
+countryName_max			= 2
+
+stateOrProvinceName		= State or Province Name (full name)
+stateOrProvinceName_default	= Some-State
+
+localityName			= Locality Name (eg, city)
+
+0.organizationName		= Organization Name (eg, company)
+0.organizationName_default	= Internet Widgits Pty Ltd
+
+
+organizationalUnitName		= Organizational Unit Name (eg, section)
+
+commonName			= Common Name (e.g. server FQDN or YOUR name)
+commonName_max			= 64
+
+emailAddress			= Email Address
+emailAddress_max		= 64
+
+
+[ req_attributes ]
+challengePassword		= A challenge password
+challengePassword_min		= 4
+challengePassword_max		= 20
+
+unstructuredName		= An optional company name
+
+[ usr_cert ]
+
+
+
+basicConstraints=CA:FALSE
+
+
+
+
+
+
+
+nsComment			= "OpenSSL Generated Certificate"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+
+
+
+
+[ v3_req ]
+
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = localhost
+DNS.2 = *.ronething.cn
+DNS.3 = *.ronething.com
+
+[ v3_ca ]
+
+
+
+
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+basicConstraints = CA:true
+
+
+
+
+
+[ crl_ext ]
+
+
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+
+
+basicConstraints=CA:FALSE
+
+
+
+
+
+
+
+nsComment			= "OpenSSL Generated Certificate"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+
+
+
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+[ tsa ]
+
+default_tsa = tsa_config1	# the default TSA section
+
+[ tsa_config1 ]
+
+dir		= ./demoCA		# TSA root directory
+serial		= $dir/tsaserial	# The current serial number (mandatory)
+crypto_device	= builtin		# OpenSSL engine to use for signing
+signer_cert	= $dir/tsacert.pem 	# The TSA signing certificate
+					# (optional)
+certs		= $dir/cacert.pem	# Certificate chain to include in reply
+					# (optional)
+signer_key	= $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy	= tsa_policy1		# Policy if request did not specify it
+					# (optional)
+other_policies	= tsa_policy2, tsa_policy3	# acceptable policies (optional)
+digests		= md5, sha1		# Acceptable message digests (mandatory)
+accuracy	= secs:1, millisecs:500, microsecs:100	# (optional)
+clock_precision_digits  = 0	# number of digits after dot. (optional)
+ordering		= yes	# Is ordering defined for timestamps?
+				# (optional, default: no)
+tsa_name		= yes	# Must the TSA name be included in the reply?
+				# (optional, default: no)
+ess_cert_id_chain	= no	# Must the ESS cert id chain be included?
+				# (optional, default: no)
+

+ 2 - 2
examples/pom.xml

@@ -25,7 +25,7 @@
 
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java-examples</artifactId>
-    <version>2.2.2</version>
+    <version>2.3.0</version>
 
     <build>
         <plugins>
@@ -64,7 +64,7 @@
         <dependency>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>2.2.3</version>
+            <version>2.3.0</version>
         </dependency>
         <dependency>
             <groupId>com.google.code.gson</groupId>

+ 9 - 4
pom.xml

@@ -25,11 +25,11 @@
 
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java</artifactId>
-    <version>2.2.2</version>
+    <version>2.3.0</version>
     <packaging>jar</packaging>
 
     <name>io.milvus:milvus-sdk-java</name>
-    <description>Java SDK for Milvus, a distributed high-performance vector search engine.</description>
+    <description>Java SDK for Milvus, a distributed high-performance vector database.</description>
     <url>https://github.com/milvus-io/milvus-sdk-java</url>
 
     <licenses>
@@ -93,6 +93,7 @@
         <jackson.version>2.12.7.1</jackson.version>
         <gson.version>2.9.0</gson.version>
         <kotlin.version>1.6.0</kotlin.version>
+        <version.fastjson>1.2.83</version.fastjson>
     </properties>
 
     <dependencyManagement>
@@ -110,9 +111,8 @@
     <dependencies>
         <dependency>
             <groupId>io.grpc</groupId>
-            <artifactId>grpc-netty-shaded</artifactId>
+            <artifactId>grpc-netty</artifactId>
             <version>${grpc.version}</version>
-            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>io.grpc</groupId>
@@ -203,6 +203,11 @@
             <artifactId>kotlin-stdlib</artifactId>
             <version>${kotlin.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>${version.fastjson}</version>
+        </dependency>
     </dependencies>
 
     <profiles>

File diff suppressed because it is too large
+ 365 - 178
src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java


+ 120 - 0
src/main/java/io/milvus/client/MilvusClient.java

@@ -26,9 +26,14 @@ import io.milvus.param.RpcStatus;
 import io.milvus.param.alias.*;
 import io.milvus.param.bulkinsert.*;
 import io.milvus.param.collection.*;
+import io.milvus.param.highlevel.collection.response.ListCollectionsResponse;
 import io.milvus.param.control.*;
 import io.milvus.param.credential.*;
 import io.milvus.param.dml.*;
+import io.milvus.param.highlevel.collection.CreateSimpleCollectionParam;
+import io.milvus.param.highlevel.collection.ListCollectionsParam;
+import io.milvus.param.highlevel.dml.*;
+import io.milvus.param.highlevel.dml.response.*;
 import io.milvus.param.index.*;
 import io.milvus.param.partition.*;
 import io.milvus.param.role.*;
@@ -47,6 +52,21 @@ public interface MilvusClient {
      */
     MilvusClient withTimeout(long timeout, TimeUnit timeoutUnit);
 
+    /**
+     * Number of retry attempts.
+     *
+     * @param retryTimes     number of retry attempts.
+     */
+    MilvusClient withRetry(int retryTimes);
+
+    /**
+     * Time interval between retry attempts. Default value is 500ms.
+     *
+     * @param interval     time interval between retry attempts.
+     * @param timeUnit     time unit
+     */
+    MilvusClient withRetryInterval(long interval, TimeUnit timeUnit);
+
     /**
      * Disconnects from a Milvus server with timeout of 1 minute
      */
@@ -73,6 +93,29 @@ public interface MilvusClient {
      */
     R<Boolean> hasCollection(HasCollectionParam requestParam);
 
+    /**
+     * Creates a database in Milvus.
+     *
+     * @param requestParam {@link CreateDatabaseParam}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
+     */
+    R<RpcStatus> createDatabase(CreateDatabaseParam requestParam);
+
+    /**
+     * Drops a database. Note that this method drops all data in the database.
+     *
+     * @param requestParam {@link DropDatabaseParam}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
+     */
+    R<RpcStatus> dropDatabase(DropDatabaseParam requestParam);
+
+    /**
+     * List databases. Note that this method list all database in the cluster.
+     *
+     * @return {status:result code, data:RpcStatus{msg: result message}}
+     */
+    R<ListDatabasesResponse> listDatabases();
+
     /**
      * Creates a collection in Milvus.
      *
@@ -122,6 +165,14 @@ public interface MilvusClient {
      */
     R<GetCollectionStatisticsResponse> getCollectionStatistics(GetCollectionStatisticsParam requestParam);
 
+    /**
+     * rename a collection
+     *
+     * @param requestParam {@link RenameCollectionParam}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
+     */
+    R<RpcStatus> renameCollection(RenameCollectionParam requestParam);
+
     /**
      * Lists all collections or gets collection loading status.
      *
@@ -358,6 +409,14 @@ public interface MilvusClient {
      */
     R<GetFlushStateResponse> getFlushState(GetFlushStateParam requestParam);
 
+    /**
+     * Get flush state of all segments.
+     *
+     * @param requestParam {@link GetFlushAllStateParam}
+     * @return {status:result code, data:GetMetricsResponse{status,metrics}}
+     */
+    R<GetFlushAllStateResponse> getFlushAllState(GetFlushAllStateParam requestParam);
+
     /**
      * Gets the information of persistent segments from data node, including row count,
      * persistence state(growing or flushed), etc.
@@ -595,4 +654,65 @@ public interface MilvusClient {
      * @return {status:result code, data:GetLoadStateResponse{status}}
      */
     R<GetLoadStateResponse> getLoadState(GetLoadStateParam requestParam);
+
+
+
+    ///////////////////// High Level API//////////////////////
+    /**
+     * Creates a collection in Milvus.
+     *
+     * @param requestParam {@link CreateSimpleCollectionParam}
+     * @return {status:result code, data:RpcStatus{msg: result message}}
+     */
+    R<RpcStatus> createCollection(CreateSimpleCollectionParam requestParam);
+
+    /**
+     * Lists all collections
+     *
+     * @param requestParam {@link ListCollectionsParam}
+     * @return {status:result code, data: ListCollectionsResponse{collection_names}}
+     */
+    R<ListCollectionsResponse> listCollections(ListCollectionsParam requestParam);
+
+    /**
+     * Inserts rows data into a specified collection . Note that you don't need to
+     * input primary key field if auto_id is enabled.
+     *
+     * @param requestParam {@link InsertRowsParam}
+     * @return {status:result code, data: MutationResult{insert results}}
+     */
+    R<InsertResponse> insert(InsertRowsParam requestParam);
+
+    /**
+     * Deletes entity(s) based on the value of primary key.
+     *
+     * @param requestParam {@link DeleteIdsParam}
+     * @return {status:result code, data: MutationResult{delete results}}
+     */
+    R<DeleteResponse> delete(DeleteIdsParam requestParam);
+
+    /**
+     * Get entity(s) based on the value of primary key.
+     *
+     * @param requestParam {@link GetIdsParam}
+     * @return {status:result code, data: QueryResults{query results}}
+     */
+    R<GetResponse> get(GetIdsParam requestParam);
+
+    /**
+     * Queries entity(s) based on scalar field(s) filtered by boolean expression.
+     * Note that the order of the returned entities cannot be guaranteed.
+     *
+     * @param requestParam {@link QuerySimpleParam}
+     * @return {status:result code,data: QueryResults{filter results}}
+     */
+    R<QueryResponse> query(QuerySimpleParam requestParam);
+
+    /**
+     * Conducts ANN search on a vector field. Use expression to do filtering before search.
+     *
+     * @param requestParam {@link SearchSimpleParam}
+     * @return {status:result code, data: SearchResults{topK results}}
+     */
+    R<SearchResponse> search(SearchSimpleParam requestParam);
 }

+ 100 - 1
src/main/java/io/milvus/client/MilvusMultiServiceClient.java

@@ -31,9 +31,14 @@ import io.milvus.param.ServerAddress;
 import io.milvus.param.alias.*;
 import io.milvus.param.bulkinsert.*;
 import io.milvus.param.collection.*;
+import io.milvus.param.highlevel.collection.response.ListCollectionsResponse;
 import io.milvus.param.control.*;
 import io.milvus.param.credential.*;
 import io.milvus.param.dml.*;
+import io.milvus.param.highlevel.collection.CreateSimpleCollectionParam;
+import io.milvus.param.highlevel.collection.ListCollectionsParam;
+import io.milvus.param.highlevel.dml.*;
+import io.milvus.param.highlevel.dml.response.*;
 import io.milvus.param.index.*;
 import io.milvus.param.partition.*;
 import io.milvus.param.role.*;
@@ -89,7 +94,7 @@ public class MilvusMultiServiceClient implements MilvusClient {
                 .withKeepAliveTime(keepAliveTimeMs, TimeUnit.MILLISECONDS)
                 .withKeepAliveTimeout(keepAliveTimeoutMs, TimeUnit.MILLISECONDS)
                 .keepAliveWithoutCalls(keepAliveWithoutCalls)
-                .secure(secure)
+                .withSecure(secure)
                 .withIdleTimeout(idleTimeoutMs, TimeUnit.MILLISECONDS)
                 .withAuthorization(multiConnectParam.getAuthorization())
                 .build();
@@ -102,6 +107,16 @@ public class MilvusMultiServiceClient implements MilvusClient {
         return clusterFactory.getMaster().getClient().withTimeout(timeout, timeoutUnit);
     }
 
+    @Override
+    public MilvusClient withRetry(int retryTimes) {
+        return clusterFactory.getMaster().getClient().withRetry(retryTimes);
+    }
+
+    @Override
+    public MilvusClient withRetryInterval(long interval, TimeUnit timeUnit) {
+        return clusterFactory.getMaster().getClient().withRetryInterval(interval, timeUnit);
+    }
+
     @Override
     public void close(long maxWaitSeconds) throws InterruptedException {
         this.clusterFactory.getAvailableServerSettings().parallelStream()
@@ -114,6 +129,30 @@ public class MilvusMultiServiceClient implements MilvusClient {
         return this.clusterFactory.getMaster().getClient().hasCollection(requestParam);
     }
 
+    @Override
+    public R<RpcStatus> createDatabase(CreateDatabaseParam requestParam) {
+        List<R<RpcStatus>> response = this.clusterFactory.getAvailableServerSettings().stream()
+                .map(serverSetting -> serverSetting.getClient().createDatabase(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
+    @Override
+    public R<RpcStatus> dropDatabase(DropDatabaseParam requestParam) {
+        List<R<RpcStatus>> response = this.clusterFactory.getAvailableServerSettings().stream()
+                .map(serverSetting -> serverSetting.getClient().dropDatabase(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
+    @Override
+    public R<ListDatabasesResponse> listDatabases() {
+        List<R<ListDatabasesResponse>> response = this.clusterFactory.getAvailableServerSettings().stream()
+                .map(serverSetting -> serverSetting.getClient().listDatabases())
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
     @Override
     public R<RpcStatus> createCollection(CreateCollectionParam requestParam) {
         List<R<RpcStatus>> response = this.clusterFactory.getAvailableServerSettings().stream()
@@ -146,6 +185,14 @@ public class MilvusMultiServiceClient implements MilvusClient {
         return handleResponse(response);
     }
 
+    @Override
+    public R<RpcStatus> renameCollection(RenameCollectionParam requestParam) {
+        List<R<RpcStatus>> response = this.clusterFactory.getAvailableServerSettings().stream()
+                .map(serverSetting -> serverSetting.getClient().renameCollection(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
     @Override
     public R<DescribeCollectionResponse> describeCollection(DescribeCollectionParam requestParam) {
         return this.clusterFactory.getMaster().getClient().describeCollection(requestParam);
@@ -356,6 +403,11 @@ public class MilvusMultiServiceClient implements MilvusClient {
         return this.clusterFactory.getMaster().getClient().getFlushState(requestParam);
     }
 
+    @Override
+    public R<GetFlushAllStateResponse> getFlushAllState(GetFlushAllStateParam requestParam) {
+        return this.clusterFactory.getMaster().getClient().getFlushAllState(requestParam);
+    }
+
     @Override
     public R<GetPersistentSegmentInfoResponse> getPersistentSegmentInfo(GetPersistentSegmentInfoParam requestParam) {
         return this.clusterFactory.getMaster().getClient().getPersistentSegmentInfo(requestParam);
@@ -497,6 +549,53 @@ public class MilvusMultiServiceClient implements MilvusClient {
         return this.clusterFactory.getMaster().getClient().getLoadState(requestParam);
     }
 
+    ///////////////////// High Level API//////////////////////
+
+
+    @Override
+    public R<RpcStatus> createCollection(CreateSimpleCollectionParam requestParam) {
+        List<R<RpcStatus>> response = this.clusterFactory.getAvailableServerSettings().parallelStream()
+                .map(serverSetting -> serverSetting.getClient().createCollection(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
+    @Override
+    public R<ListCollectionsResponse> listCollections(ListCollectionsParam requestParam) {
+        return this.clusterFactory.getMaster().getClient().listCollections(requestParam);
+    }
+
+    @Override
+    public R<InsertResponse> insert(InsertRowsParam requestParam) {
+        List<R<InsertResponse>> response = this.clusterFactory.getAvailableServerSettings().parallelStream()
+                .map(serverSetting -> serverSetting.getClient().insert(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
+    @Override
+    public R<DeleteResponse> delete(DeleteIdsParam requestParam) {
+        List<R<DeleteResponse>> response = this.clusterFactory.getAvailableServerSettings().stream()
+                .map(serverSetting -> serverSetting.getClient().delete(requestParam))
+                .collect(Collectors.toList());
+        return handleResponse(response);
+    }
+
+    @Override
+    public R<GetResponse> get(GetIdsParam requestParam) {
+        return this.clusterFactory.getMaster().getClient().get(requestParam);
+    }
+
+    @Override
+    public R<QueryResponse> query(QuerySimpleParam requestParam) {
+        return this.clusterFactory.getMaster().getClient().query(requestParam);
+    }
+
+    @Override
+    public R<SearchResponse> search(SearchSimpleParam requestParam) {
+        return this.clusterFactory.getMaster().getClient().search(requestParam);
+    }
+
     private <T> R<T> handleResponse(List<R<T>> response) {
         if (CollectionUtils.isNotEmpty(response)) {
             R<T> rSuccess = null;

+ 558 - 26
src/main/java/io/milvus/client/MilvusServiceClient.java

@@ -21,42 +21,157 @@ package io.milvus.client;
 
 import io.grpc.*;
 import io.grpc.stub.MetadataUtils;
-import io.milvus.grpc.MilvusServiceGrpc;
+import io.milvus.grpc.*;
 import io.milvus.param.ConnectParam;
 
+import io.milvus.param.LogLevel;
+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.bulkinsert.BulkInsertParam;
+import io.milvus.param.bulkinsert.GetBulkInsertStateParam;
+import io.milvus.param.bulkinsert.ListBulkInsertTasksParam;
+import io.milvus.param.collection.*;
+import io.milvus.param.control.*;
+import io.milvus.param.credential.CreateCredentialParam;
+import io.milvus.param.credential.DeleteCredentialParam;
+import io.milvus.param.credential.ListCredUsersParam;
+import io.milvus.param.credential.UpdateCredentialParam;
+import io.milvus.param.dml.DeleteParam;
+import io.milvus.param.dml.InsertParam;
+import io.milvus.param.dml.QueryParam;
+import io.milvus.param.dml.SearchParam;
+import io.milvus.param.highlevel.collection.CreateSimpleCollectionParam;
+import io.milvus.param.highlevel.collection.ListCollectionsParam;
+import io.milvus.param.highlevel.collection.response.ListCollectionsResponse;
+import io.milvus.param.highlevel.dml.*;
+import io.milvus.param.highlevel.dml.response.*;
+import io.milvus.param.index.*;
+import io.milvus.param.partition.*;
+import io.milvus.param.role.*;
 import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.Callable;
+
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.NettyChannelBuilder;
+import io.netty.handler.ssl.SslContext;
+
 
 public class MilvusServiceClient extends AbstractMilvusGrpcClient {
 
-    private final ManagedChannel channel;
+    private ManagedChannel channel;
     private final MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub;
     private final MilvusServiceGrpc.MilvusServiceFutureStub futureStub;
+    private final long rpcDeadlineMs;
+    private long timeoutMs = 0;
+    private int retryTimes = 0;
+    private long retryIntervalMs = 500L;
 
     public MilvusServiceClient(@NonNull ConnectParam connectParam) {
+        this.rpcDeadlineMs = connectParam.getRpcDeadlineMs();
+
         Metadata metadata = new Metadata();
         metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), connectParam.getAuthorization());
+        if (StringUtils.isNotEmpty(connectParam.getDatabaseName())) {
+            metadata.put(Metadata.Key.of("dbname", Metadata.ASCII_STRING_MARSHALLER), connectParam.getDatabaseName());
+        }
 
-        ManagedChannelBuilder builder = ManagedChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort())
-                .usePlaintext()
-                .maxInboundMessageSize(Integer.MAX_VALUE)
-                .keepAliveTime(connectParam.getKeepAliveTimeMs(), TimeUnit.MILLISECONDS)
-                .keepAliveTimeout(connectParam.getKeepAliveTimeoutMs(), TimeUnit.MILLISECONDS)
-                .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
-                .idleTimeout(connectParam.getIdleTimeoutMs(), TimeUnit.MILLISECONDS)
-                .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
-
-        if(connectParam.isSecure()){
-            builder.useTransportSecurity();
+        try {
+            if (StringUtils.isNotEmpty(connectParam.getServerPemPath())) {
+                // one-way tls
+                SslContext sslContext = GrpcSslContexts.forClient()
+                        .trustManager(new File(connectParam.getServerPemPath()))
+                        .build();
+
+                NettyChannelBuilder builder = NettyChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort())
+                        .overrideAuthority(connectParam.getServerName())
+                        .sslContext(sslContext)
+                        .maxInboundMessageSize(Integer.MAX_VALUE)
+                        .keepAliveTime(connectParam.getKeepAliveTimeMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveTimeout(connectParam.getKeepAliveTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
+                        .idleTimeout(connectParam.getIdleTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
+                if(connectParam.isSecure()){
+                    builder.useTransportSecurity();
+                }
+                channel = builder.build();
+            } else if (StringUtils.isNotEmpty(connectParam.getClientPemPath())
+                    && StringUtils.isNotEmpty(connectParam.getClientKeyPath())
+                    && StringUtils.isNotEmpty(connectParam.getCaPemPath())) {
+                // tow-way tls
+                SslContext sslContext = GrpcSslContexts.forClient()
+                        .trustManager(new File(connectParam.getCaPemPath()))
+                        .keyManager(new File(connectParam.getClientPemPath()), new File(connectParam.getClientKeyPath()))
+                        .build();
+
+                NettyChannelBuilder builder = NettyChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort())
+                        .sslContext(sslContext)
+                        .maxInboundMessageSize(Integer.MAX_VALUE)
+                        .keepAliveTime(connectParam.getKeepAliveTimeMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveTimeout(connectParam.getKeepAliveTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
+                        .idleTimeout(connectParam.getIdleTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
+                if(connectParam.isSecure()){
+                    builder.useTransportSecurity();
+                }
+                if (StringUtils.isNotEmpty(connectParam.getServerName())) {
+                    builder.overrideAuthority(connectParam.getServerName());
+                }
+                channel = builder.build();
+            } else {
+                // no tls
+                ManagedChannelBuilder<?> builder = ManagedChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort())
+                        .usePlaintext()
+                        .maxInboundMessageSize(Integer.MAX_VALUE)
+                        .keepAliveTime(connectParam.getKeepAliveTimeMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveTimeout(connectParam.getKeepAliveTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
+                        .idleTimeout(connectParam.getIdleTimeoutMs(), TimeUnit.MILLISECONDS)
+                        .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata));
+                if(connectParam.isSecure()){
+                    builder.useTransportSecurity();
+                }
+                channel = builder.build();
+            }
+        } catch (IOException e) {
+            logError("Failed to open credentials file, error:{}\n", e.getMessage());
         }
-        channel = builder.build();
 
+        assert channel != null;
         blockingStub = MilvusServiceGrpc.newBlockingStub(channel);
         futureStub = MilvusServiceGrpc.newFutureStub(channel);
     }
 
+    protected MilvusServiceClient(MilvusServiceClient src) {
+        this.channel = src.channel;
+        this.blockingStub = src.blockingStub;
+        this.futureStub = src.futureStub;
+        this.rpcDeadlineMs = src.rpcDeadlineMs;
+        this.timeoutMs = src.timeoutMs;
+        this.retryTimes = src.retryTimes;
+        this.retryIntervalMs = src.retryIntervalMs;
+    }
+
+    // set log level in runtime
+    public void setLogLevel(LogLevel level) {
+        logLevel = level;
+    }
+
     @Override
     protected MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub() {
+        if (this.rpcDeadlineMs > 0) {
+            return this.blockingStub.withWaitForReady()
+                    .withDeadlineAfter(this.rpcDeadlineMs, TimeUnit.MILLISECONDS);
+        }
         return this.blockingStub;
     }
 
@@ -100,12 +215,7 @@ public class MilvusServiceClient extends AbstractMilvusGrpcClient {
         final MilvusServiceGrpc.MilvusServiceFutureStub futureStubTimeout =
                 this.futureStub.withInterceptors(timeoutInterceptor);
 
-        return new AbstractMilvusGrpcClient() {
-            @Override
-            protected boolean clientIsReady() {
-                return MilvusServiceClient.this.clientIsReady();
-            }
-
+        MilvusServiceClient newClient = new MilvusServiceClient(this) {
             @Override
             protected MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub() {
                 return blockingStubTimeout;
@@ -115,17 +225,439 @@ public class MilvusServiceClient extends AbstractMilvusGrpcClient {
             protected MilvusServiceGrpc.MilvusServiceFutureStub futureStub() {
                 return futureStubTimeout;
             }
+        };
+        newClient.timeoutMs = timeoutMillis;
+        return newClient;
+    }
 
-            @Override
-            public void close(long maxWaitSeconds) throws InterruptedException {
-                MilvusServiceClient.this.close(maxWaitSeconds);
+    @Override
+    public MilvusClient withRetry(int retryTimes) {
+        if (retryTimes <= 0) {
+            return this;
+        }
+
+        MilvusServiceClient newClient = new MilvusServiceClient(this);
+        newClient.retryTimes = retryTimes;
+        return newClient;
+    }
+
+    @Override
+    public MilvusClient withRetryInterval(long interval, TimeUnit timeUnit) {
+        if (interval <= 0) {
+            return this;
+        }
+
+        MilvusServiceClient newClient = new MilvusServiceClient(this);
+        newClient.retryIntervalMs = timeUnit.toMillis(interval);
+        return newClient;
+    }
+
+    private <T> R<T> retry(Callable<R<T>> callable) {
+        // no retry, direct call the method
+        if (this.retryTimes <= 1) {
+            try {
+                return callable.call();
+            } catch (Exception e) {
+                return R.failed(e);
             }
+        }
 
-            @Override
-            public MilvusClient withTimeout(long timeout, TimeUnit timeoutUnit) {
-                return MilvusServiceClient.this.withTimeout(timeout, timeoutUnit);
+        // method to check timeout
+        long begin = System.currentTimeMillis();
+        Callable<Void> timeoutChecker = ()->{
+            long current = System.currentTimeMillis();
+            long cost = (current - begin);
+            if (this.timeoutMs > 0 && cost >= this.timeoutMs) {
+                String msg = String.format("Retry timeout: %dms", this.timeoutMs);
+                throw new RuntimeException(msg);
             }
+            return null;
         };
+
+        // retry within timeout
+        for (int i = 0; i < this.retryTimes; i++) {
+            try {
+                R<T> resp = callable.call();
+                if (resp.getStatus() == R.Status.Success.getCode()) {
+                    return resp;
+                }
+
+                if (i != this.retryTimes-1) {
+                    timeoutChecker.call();
+                    TimeUnit.MILLISECONDS.sleep(this.retryIntervalMs);
+                    timeoutChecker.call();
+                    logInfo(String.format("Retry again after %dms...", this.retryIntervalMs));
+                }
+            } catch (Exception e) {
+                logError(e.getMessage());
+                return R.failed(e);
+            }
+        }
+        String msg = String.format("Retry run out of %d retry times", this.retryTimes);
+        logError(msg);
+        return R.failed(new RuntimeException(msg));
+    }
+
+    @Override
+    public R<Boolean> hasCollection(HasCollectionParam requestParam) {
+        return retry(()-> super.hasCollection(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> createDatabase(CreateDatabaseParam requestParam) {
+        return retry(()-> super.createDatabase(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> dropDatabase(DropDatabaseParam requestParam) {
+        return retry(()-> super.dropDatabase(requestParam));
+    }
+
+    @Override
+    public R<ListDatabasesResponse> listDatabases() {
+        return retry(super::listDatabases);
+    }
+
+    @Override
+    public R<RpcStatus> createCollection(CreateCollectionParam requestParam) {
+        return retry(()-> super.createCollection(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> dropCollection(DropCollectionParam requestParam) {
+        return retry(()-> super.dropCollection(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> loadCollection(LoadCollectionParam requestParam) {
+        return retry(()-> super.loadCollection(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> releaseCollection(ReleaseCollectionParam requestParam) {
+        return retry(()-> super.releaseCollection(requestParam));
+    }
+
+    @Override
+    public R<DescribeCollectionResponse> describeCollection(DescribeCollectionParam requestParam) {
+        return retry(()-> super.describeCollection(requestParam));
+    }
+
+    @Override
+    public R<GetCollectionStatisticsResponse> getCollectionStatistics(GetCollectionStatisticsParam requestParam) {
+        return retry(()-> super.getCollectionStatistics(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> renameCollection(RenameCollectionParam requestParam) {
+        return retry(()-> super.renameCollection(requestParam));
+    }
+
+    @Override
+    public R<ShowCollectionsResponse> showCollections(ShowCollectionsParam requestParam) {
+        return retry(()-> super.showCollections(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> alterCollection(AlterCollectionParam requestParam) {
+        return retry(()-> super.alterCollection(requestParam));
+    }
+
+    @Override
+    public R<FlushResponse> flush(FlushParam requestParam) {
+        return retry(()-> super.flush(requestParam));
+    }
+
+    @Override
+    public R<FlushAllResponse> flushAll(boolean syncFlushAll, long syncFlushAllWaitingInterval, long syncFlushAllTimeout) {
+        return retry(()-> super.flushAll(syncFlushAll, syncFlushAllWaitingInterval, syncFlushAllTimeout));
+    }
+
+    @Override
+    public R<RpcStatus> createPartition(CreatePartitionParam requestParam) {
+        return retry(()-> super.createPartition(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> dropPartition(DropPartitionParam requestParam) {
+        return retry(()-> super.dropPartition(requestParam));
+    }
+
+    @Override
+    public R<Boolean> hasPartition(HasPartitionParam requestParam) {
+        return retry(()-> super.hasPartition(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> loadPartitions(LoadPartitionsParam requestParam) {
+        return retry(()-> super.loadPartitions(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> releasePartitions(ReleasePartitionsParam requestParam) {
+        return retry(()-> super.releasePartitions(requestParam));
+    }
+
+    @Override
+    public R<GetPartitionStatisticsResponse> getPartitionStatistics(GetPartitionStatisticsParam requestParam) {
+        return retry(()-> super.getPartitionStatistics(requestParam));
+    }
+
+    @Override
+    public R<ShowPartitionsResponse> showPartitions(ShowPartitionsParam requestParam) {
+        return retry(()-> super.showPartitions(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> createAlias(CreateAliasParam requestParam) {
+        return retry(()-> super.createAlias(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> dropAlias(DropAliasParam requestParam) {
+        return retry(()-> super.dropAlias(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> alterAlias(AlterAliasParam requestParam) {
+        return retry(()-> super.alterAlias(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> createIndex(CreateIndexParam requestParam) {
+        return retry(()-> super.createIndex(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> dropIndex(DropIndexParam requestParam) {
+        return retry(()-> super.dropIndex(requestParam));
+    }
+
+    @Override
+    public R<DescribeIndexResponse> describeIndex(DescribeIndexParam requestParam) {
+        return retry(()-> super.describeIndex(requestParam));
+    }
+
+    @Override
+    public R<MutationResult> insert(InsertParam requestParam) {
+        return retry(()-> super.insert(requestParam));
+    }
+
+    @Override
+    public R<MutationResult> delete(DeleteParam requestParam) {
+        return retry(()-> super.delete(requestParam));
+    }
+
+    @Override
+    public R<SearchResults> search(SearchParam requestParam) {
+        return retry(()-> super.search(requestParam));
+    }
+
+    @Override
+    public R<QueryResults> query(QueryParam requestParam) {
+        return retry(()-> super.query(requestParam));
+    }
+
+    @Override
+    public R<GetMetricsResponse> getMetrics(GetMetricsParam requestParam) {
+        return retry(()-> super.getMetrics(requestParam));
+    }
+
+    @Override
+    public R<GetFlushStateResponse> getFlushState(GetFlushStateParam requestParam) {
+        return retry(()-> super.getFlushState(requestParam));
+    }
+
+    @Override
+    public R<GetFlushAllStateResponse> getFlushAllState(GetFlushAllStateParam requestParam) {
+        return retry(()-> super.getFlushAllState(requestParam));
+    }
+
+    @Override
+    public R<GetPersistentSegmentInfoResponse> getPersistentSegmentInfo(GetPersistentSegmentInfoParam requestParam) {
+        return retry(()-> super.getPersistentSegmentInfo(requestParam));
+    }
+
+    @Override
+    public R<GetQuerySegmentInfoResponse> getQuerySegmentInfo(GetQuerySegmentInfoParam requestParam) {
+        return retry(()-> super.getQuerySegmentInfo(requestParam));
+    }
+
+    @Override
+    public R<GetReplicasResponse> getReplicas(GetReplicasParam requestParam) {
+        return retry(()-> super.getReplicas(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> loadBalance(LoadBalanceParam requestParam) {
+        return retry(()-> super.loadBalance(requestParam));
+    }
+
+    @Override
+    public R<GetCompactionStateResponse> getCompactionState(GetCompactionStateParam requestParam) {
+        return retry(()-> super.getCompactionState(requestParam));
+    }
+
+    @Override
+    public R<ManualCompactionResponse> manualCompact(ManualCompactParam requestParam) {
+        return retry(()-> super.manualCompact(requestParam));
+    }
+
+    @Override
+    public R<GetCompactionPlansResponse> getCompactionStateWithPlans(GetCompactionPlansParam requestParam) {
+        return retry(()-> super.getCompactionStateWithPlans(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> createCredential(CreateCredentialParam requestParam) {
+        return retry(()-> super.createCredential(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> updateCredential(UpdateCredentialParam requestParam) {
+        return retry(()-> super.updateCredential(requestParam));
+    }
+
+    @Override
+    public R<RpcStatus> deleteCredential(DeleteCredentialParam requestParam) {
+        return retry(()-> super.deleteCredential(requestParam));
+    }
+
+    @Override
+    public R<ListCredUsersResponse> listCredUsers(ListCredUsersParam requestParam) {
+        return retry(()-> super.listCredUsers(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> createRole(CreateRoleParam requestParam) {
+        return retry(()-> super.createRole(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> dropRole(DropRoleParam requestParam) {
+        return retry(()-> super.dropRole(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> addUserToRole(AddUserToRoleParam requestParam) {
+        return retry(()-> super.addUserToRole(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> removeUserFromRole(RemoveUserFromRoleParam requestParam) {
+        return retry(()-> super.removeUserFromRole(requestParam));
+    }
+
+
+    @Override
+    public R<SelectRoleResponse> selectRole(SelectRoleParam requestParam) {
+        return retry(()-> super.selectRole(requestParam));
+    }
+
+
+    @Override
+    public R<SelectUserResponse> selectUser(SelectUserParam requestParam) {
+        return retry(()-> super.selectUser(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> grantRolePrivilege(GrantRolePrivilegeParam requestParam) {
+        return retry(()-> super.grantRolePrivilege(requestParam));
+    }
+
+
+    @Override
+    public R<RpcStatus> revokeRolePrivilege(RevokeRolePrivilegeParam requestParam) {
+        return retry(()-> super.revokeRolePrivilege(requestParam));
+    }
+
+
+    @Override
+    public R<SelectGrantResponse> selectGrantForRole(SelectGrantForRoleParam requestParam) {
+        return retry(()-> super.selectGrantForRole(requestParam));
+    }
+
+
+    @Override
+    public R<SelectGrantResponse> selectGrantForRoleAndObject(SelectGrantForRoleAndObjectParam requestParam) {
+        return retry(()-> super.selectGrantForRoleAndObject(requestParam));
+    }
+
+    @Override
+    public R<ImportResponse> bulkInsert(BulkInsertParam requestParam) {
+        return retry(()-> super.bulkInsert(requestParam));
+    }
+
+    @Override
+    public R<GetImportStateResponse> getBulkInsertState(GetBulkInsertStateParam requestParam) {
+        return retry(()-> super.getBulkInsertState(requestParam));
+    }
+
+    @Override
+    public R<ListImportTasksResponse> listBulkInsertTasks(ListBulkInsertTasksParam requestParam) {
+        return retry(()-> super.listBulkInsertTasks(requestParam));
+    }
+
+    @Override
+    public R<CheckHealthResponse> checkHealth() {
+        return retry(super::checkHealth);
+    }
+
+    @Override
+    public R<GetVersionResponse> getVersion() {
+        return retry(super::getVersion);
+    }
+
+    @Override
+    public R<GetLoadingProgressResponse> getLoadingProgress(GetLoadingProgressParam requestParam) {
+        return retry(()-> super.getLoadingProgress(requestParam));
+    }
+
+    @Override
+    public R<GetLoadStateResponse> getLoadState(GetLoadStateParam requestParam) {
+        return retry(()-> super.getLoadState(requestParam));
+    }
+
+
+
+    @Override
+    public R<RpcStatus> createCollection(CreateSimpleCollectionParam requestParam) {
+        return retry(()-> super.createCollection(requestParam));
+    }
+
+    @Override
+    public R<ListCollectionsResponse> listCollections(ListCollectionsParam requestParam) {
+        return retry(()-> super.listCollections(requestParam));
+    }
+
+    @Override
+    public R<InsertResponse> insert(InsertRowsParam requestParam) {
+        return retry(()-> super.insert(requestParam));
+    }
+
+    @Override
+    public R<DeleteResponse> delete(DeleteIdsParam requestParam) {
+        return retry(()-> super.delete(requestParam));
+    }
+
+    @Override
+    public R<GetResponse> get(GetIdsParam requestParam) {
+        return retry(()-> super.get(requestParam));
+    }
+
+    @Override
+    public R<QueryResponse> query(QuerySimpleParam requestParam) {
+        return retry(()-> super.query(requestParam));
+    }
+
+    @Override
+    public R<SearchResponse> search(SearchSimpleParam requestParam) {
+        return retry(()-> super.search(requestParam));
     }
 }
 

+ 2 - 1
src/main/java/io/milvus/common/clientenum/ConsistencyLevelEnum.java

@@ -5,7 +5,8 @@ import lombok.Getter;
 public enum ConsistencyLevelEnum {
 
     STRONG("Strong", 0),
-    SESSION("Session", 1),
+    // Session level is not allowed here because no ORM is implemented
+//    SESSION("Session", 1),
     BOUNDED("Bounded", 2),
     EVENTUALLY("Eventually",3),
     ;

+ 1 - 1
src/main/java/io/milvus/common/utils/JacksonUtils.java

@@ -61,4 +61,4 @@ public class JacksonUtils {
         }
     }
 
-}
+}

+ 55 - 0
src/main/java/io/milvus/common/utils/URLParser.java

@@ -0,0 +1,55 @@
+package io.milvus.utils;
+
+import lombok.Getter;
+import lombok.ToString;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Objects;
+
+/**
+ * @author: wei.hu@zilliz.com
+ * @date: 2023/5/1
+ */
+@Getter
+@ToString
+public class URLParser {
+
+    private String hostname;
+    private int port;
+    private String database;
+    private boolean secure;
+
+    public URLParser(String url) {
+        try {
+            // secure
+            if (url.startsWith("https://")) {
+                secure = true;
+            }
+
+            // host
+            URI uri = new URI(url);
+            hostname = uri.getHost();
+            if (Objects.isNull(hostname)) {
+                throw new IllegalArgumentException("Missing hostname in url");
+            }
+
+            // port
+            port = uri.getPort();
+            if(port <= 0){
+                port = 19530;
+            }
+
+            // database
+            String path = uri.getPath();
+            if (Objects.isNull(path) || path.isEmpty() || "/".equals(path)) {
+                database = null;
+            } else {
+                database = path.substring(1);
+            }
+        } catch (URISyntaxException e) {
+            throw new IllegalArgumentException("Invalid url: " + url, e);
+        }
+    }
+
+}

+ 36 - 0
src/main/java/io/milvus/common/utils/VectorUtils.java

@@ -0,0 +1,36 @@
+package io.milvus.common.utils;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.collection.FieldType;
+import io.milvus.response.DescCollResponseWrapper;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class VectorUtils {
+    public static String convertPksExpr(List<?> primaryIds, DescCollResponseWrapper wrapper) {
+        Optional<FieldType> optional = wrapper.getFields().stream().filter(FieldType::isPrimaryKey).findFirst();
+        String expr;
+        if (optional.isPresent()) {
+            FieldType primaryField = optional.get();
+            switch (primaryField.getDataType()) {
+                case Int64:
+                case VarChar:
+                    List<String> primaryStringIds = primaryIds.stream().map(String::valueOf).collect(Collectors.toList());
+                    expr = convertPksExpr(primaryStringIds, primaryField.getName());
+                    break;
+                default:
+                    throw new ParamException("The primary key is not of type int64 or varchar, and the current operation is not supported.");
+            }
+        } else {
+            throw new ParamException("No primary key found.");
+        }
+        return expr;
+    }
+
+    public static String convertPksExpr(List<?> primaryIds, String primaryFieldName) {
+        return primaryFieldName + " in [" + Strings.join(primaryIds, ',') + "]";
+    }
+}

+ 2 - 1
src/main/java/io/milvus/connection/ClusterListener.java

@@ -34,7 +34,8 @@ public class ClusterListener implements Listener {
                 serverSetting.getServerAddress().getHealthPort());
 
         boolean isRunning = false;
-        try (Response response = get(url)) {
+        try {
+            Response response = get(url);
             isRunning = checkResponse(response);
             if (isRunning) {
                 logger.debug("Host [{}] heartbeat Success of Milvus Cluster Listener.",

+ 147 - 70
src/main/java/io/milvus/param/ConnectParam.java

@@ -20,79 +20,59 @@
 package io.milvus.param;
 
 import io.milvus.exception.ParamException;
+import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 import org.apache.commons.lang3.StringUtils;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 import java.util.concurrent.TimeUnit;
 
-import static io.milvus.common.constant.MilvusClientConstant.MilvusConsts.HOST_HTTPS_PREFIX;
-import static io.milvus.common.constant.MilvusClientConstant.MilvusConsts.HOST_HTTP_PREFIX;
-import static io.milvus.common.constant.MilvusClientConstant.StringValue.COLON;
-
 /**
  * Parameters for client connection.
  */
+@Getter
+@ToString
 public class ConnectParam {
     private final String host;
     private final int port;
+    private final String databaseName;
     private final String uri;
+    private final String token;
     private final long connectTimeoutMs;
     private final long keepAliveTimeMs;
     private final long keepAliveTimeoutMs;
     private final boolean keepAliveWithoutCalls;
+    private final long rpcDeadlineMs;
     private final boolean secure;
     private final long idleTimeoutMs;
     private final String authorization;
+    private final String clientKeyPath;
+    private final String clientPemPath;
+    private final String caPemPath;
+    private final String serverPemPath;
+    private final String serverName;
 
-    private ConnectParam(@NonNull Builder builder) {
+    protected ConnectParam(@NonNull Builder builder) {
         this.host = builder.host;
         this.port = builder.port;
+        this.token = builder.token;
+        this.databaseName = builder.databaseName;
         this.uri = builder.uri;
         this.connectTimeoutMs = builder.connectTimeoutMs;
         this.keepAliveTimeMs = builder.keepAliveTimeMs;
         this.keepAliveTimeoutMs = builder.keepAliveTimeoutMs;
         this.keepAliveWithoutCalls = builder.keepAliveWithoutCalls;
         this.idleTimeoutMs = builder.idleTimeoutMs;
+        this.rpcDeadlineMs = builder.rpcDeadlineMs;
         this.secure = builder.secure;
         this.authorization = builder.authorization;
-    }
-
-    public String getHost() {
-        return host;
-    }
-
-    public int getPort() {
-        return port;
-    }
-
-    public long getConnectTimeoutMs() {
-        return connectTimeoutMs;
-    }
-
-    public long getKeepAliveTimeMs() {
-        return keepAliveTimeMs;
-    }
-
-    public long getKeepAliveTimeoutMs() {
-        return keepAliveTimeoutMs;
-    }
-
-    public boolean isKeepAliveWithoutCalls() {
-        return keepAliveWithoutCalls;
-    }
-
-    public boolean isSecure() {
-        return secure;
-    }
-
-    public long getIdleTimeoutMs() {
-        return idleTimeoutMs;
-    }
-
-    public String getAuthorization() {
-        return authorization;
+        this.clientKeyPath = builder.clientKeyPath;
+        this.clientPemPath = builder.clientPemPath;
+        this.caPemPath = builder.caPemPath;
+        this.serverPemPath = builder.serverPemPath;
+        this.serverName = builder.serverName;
     }
 
     public static Builder newBuilder() {
@@ -102,19 +82,30 @@ public class ConnectParam {
     /**
      * Builder for {@link ConnectParam}
      */
+    @Getter
     public static class Builder {
         private String host = "localhost";
         private int port = 19530;
+        private String databaseName = "default";
         private String uri;
+        private String token;
         private long connectTimeoutMs = 10000;
         private long keepAliveTimeMs = Long.MAX_VALUE; // Disabling keep alive
         private long keepAliveTimeoutMs = 20000;
         private boolean keepAliveWithoutCalls = false;
-        private boolean secure = false;
+        private long rpcDeadlineMs = 0; // Disabling deadline
+
+        private String clientKeyPath;
+        private String clientPemPath;
+        private String caPemPath;
+        private String serverPemPath;
+        private String serverName;
+
+        protected boolean secure = false;
         private long idleTimeoutMs = TimeUnit.MILLISECONDS.convert(24, TimeUnit.HOURS);
         private String authorization = Base64.getEncoder().encodeToString("root:milvus".getBytes(StandardCharsets.UTF_8));
 
-        private Builder() {
+        protected Builder() {
         }
 
         /**
@@ -139,10 +130,21 @@ public class ConnectParam {
             return this;
         }
 
+        /**
+         * Sets the database name.
+         *
+         * @param databaseName databaseName
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(@NonNull String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the uri
          *
-         * @param uri
+         * @param uri the uri of Milvus instance
          * @return <code>Builder</code>
          */
         public Builder withUri(String uri) {
@@ -150,6 +152,17 @@ public class ConnectParam {
             return this;
         }
 
+        /**
+         * Sets the token
+         *
+         * @param token serving as the key for identification and authentication purposes.
+         * @return <code>Builder</code>
+         */
+        public Builder withToken(String token) {
+            this.token = token;
+            return this;
+        }
+
         /**
          * Sets the connection timeout value of client channel. The timeout value must be greater than zero.
          *
@@ -203,6 +216,7 @@ public class ConnectParam {
          * @param enable true keep-alive
          * @return <code>Builder</code>
          */
+        @java.lang.Deprecated
         public Builder secure(boolean enable) {
             secure = enable;
             return this;
@@ -220,19 +234,33 @@ public class ConnectParam {
             return this;
         }
 
+        /**
+         * Set a deadline for how long you are willing to wait for a reply from the server.
+         * With a deadline setting, the client will wait when encounter fast RPC fail caused by network fluctuations.
+         * The deadline value must be larger than or equal to zero. Default value is 0, deadline is disabled.
+         *
+         * @param deadline deadline value
+         * @param timeUnit deadline unit
+         * @return <code>Builder</code>
+         */
+        public Builder withRpcDeadline(long deadline, @NonNull TimeUnit timeUnit) {
+            this.rpcDeadlineMs = timeUnit.toMillis(deadline);
+            return this;
+        }
+
         /**
          * Sets the username and password for this connection
          * @param username current user
          * @param password password
          * @return <code>Builder</code>
          */
-        public Builder withAuthorization(@NonNull String username, @NonNull String password) {
+        public Builder withAuthorization(String username, String password) {
             this.authorization = Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8));
             return this;
         }
 
         /**
-         * Sets secure the authorization for this connection
+         * Sets secure the authorization for this connection, set to True to enable TLS
          * @param secure boolean
          * @return <code>Builder</code>
          */
@@ -251,25 +279,84 @@ public class ConnectParam {
             return this;
         }
 
+        /**
+         * Set the client.key path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param clientKeyPath path of client.key
+         * @return <code>Builder</code>
+         */
+        public Builder withClientKeyPath(@NonNull String clientKeyPath) {
+            this.clientKeyPath = clientKeyPath;
+            return this;
+        }
+
+        /**
+         * Set the client.pem path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param clientPemPath path of client.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withClientPemPath(@NonNull String clientPemPath) {
+            this.clientPemPath = clientPemPath;
+            return this;
+        }
+
+        /**
+         * Set the ca.pem path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param caPemPath path of ca.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withCaPemPath(@NonNull String caPemPath) {
+            this.caPemPath = caPemPath;
+            return this;
+        }
+
+        /**
+         * Set the server.pem path for tls one-way authentication, only takes effect when "secure" is True.
+         * @param serverPemPath path of server.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withServerPemPath(@NonNull String serverPemPath) {
+            this.serverPemPath = serverPemPath;
+            return this;
+        }
+
+        /**
+         * Set target name override for SSL host name checking, only takes effect when "secure" is True.
+         * Note: this value is passed to grpc.ssl_target_name_override
+         * @param serverName override name for SSL host
+         * @return <code>Builder</code>
+         */
+        public Builder withServerName(@NonNull String serverName) {
+            this.serverName = serverName;
+            return this;
+        }
+
         /**
          * Verifies parameters and creates a new {@link ConnectParam} instance.
          *
          * @return {@link ConnectParam}
          */
         public ConnectParam build() throws ParamException {
+            verify();
+
+            return new ConnectParam(this);
+        }
+
+        protected void verify() throws ParamException {
             ParamUtils.CheckNullEmptyString(host, "Host name");
             if (StringUtils.isNotEmpty(uri)) {
-                if (uri.startsWith(HOST_HTTPS_PREFIX)) {
-                    this.uri = uri.replace(HOST_HTTPS_PREFIX, "");
-                    this.secure = true;
-                } else if (uri.startsWith(HOST_HTTP_PREFIX)) {
-                    this.uri = uri.replace(HOST_HTTP_PREFIX, "");
-                }
-                String[] uriArray = uri.split(COLON);
-                this.host = uriArray[0];
-                if(uriArray.length == 2){
-                    this.port = Integer.valueOf(uriArray[1]);
+                io.milvus.utils.URLParser result = new io.milvus.utils.URLParser(uri);
+                this.secure = result.isSecure();
+                this.host = result.getHostname();
+                this.port = result.getPort();
+                this.databaseName = result.getDatabase();
+            }
+
+            if (StringUtils.isNotEmpty(token)) {
+                this.authorization = Base64.getEncoder().encodeToString(String.format("%s", token).getBytes(StandardCharsets.UTF_8));
+                if (!token.contains(":")) {
+                    this.port = 443;
                 }
+                this.secure = true; //
             }
 
             if (port < 0 || port > 0xFFFF) {
@@ -292,20 +379,10 @@ public class ConnectParam {
                 throw new ParamException("Idle timeout must be positive!");
             }
 
-            return new ConnectParam(this);
+            if (StringUtils.isNotEmpty(serverPemPath) || StringUtils.isNotEmpty(caPemPath)
+                    || StringUtils.isNotEmpty(clientPemPath) || StringUtils.isNotEmpty(clientKeyPath)) {
+                secure = true;
+            }
         }
     }
-
-    /**
-     * Constructs a <code>String</code> by {@link ConnectParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "ConnectParam{" +
-                "host='" + host + '\'' +
-                ", port='" + port +
-                '}';
-    }
 }

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

@@ -71,4 +71,13 @@ public class Constant {
     // set this value for "withGuaranteeTimestamp" of QueryParam/SearchParam
     // to instruct server execute query/search after all DML operations finished.
     public static final Long GUARANTEE_STRONG_TS = 0L;
+
+    // high level api
+    public static final String VECTOR_FIELD_NAME_DEFAULT  = "vector";
+    public static final String PRIMARY_FIELD_NAME_DEFAULT = "id";
+    public static final String VECTOR_INDEX_NAME_DEFAULT  = "vector_idx";
+    public static final Long LIMIT_DEFAULT = 100L;
+    public static final Long OFFSET_DEFAULT = 0L;
+    public static final String ALL_OUTPUT_FIELDS = "*";
+
 }

+ 37 - 14
src/main/java/io/milvus/param/IndexType.java

@@ -19,28 +19,51 @@
 
 package io.milvus.param;
 
+import lombok.Getter;
+
 /**
  * Represents the 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,
-    FLAT,
-    IVF_FLAT,
-    IVF_SQ8,
-    IVF_PQ,
-    HNSW,
-    ANNOY,
-    RHNSW_FLAT,
-    RHNSW_PQ,
-    RHNSW_SQ,
-    DISKANN,
-    AUTOINDEX,
+    //Only supported for float vectors
+    FLAT(1),
+    IVF_FLAT(2),
+    IVF_SQ8(3),
+    IVF_PQ(4),
+    HNSW(5),
+    DISKANN(10),
+    AUTOINDEX(11),
     //Only supported for binary vectors
-    BIN_FLAT,
-    BIN_IVF_FLAT,
+    BIN_FLAT(12),
+    BIN_IVF_FLAT(13),
 
+    //Scalar field index start from here
     //Only for varchar type field
-    TRIE,
+    TRIE("Trie", 100),
+    //Only for scalar type field
+    STL_SORT(200),
     ;
+
+    @Getter
+    private final String name;
+
+    @Getter
+    private final int code;
+
+    IndexType(){
+        this.name = this.name();
+        this.code = this.ordinal();
+    }
+
+    IndexType(int code){
+        this.name = this.name();
+        this.code = code;
+    }
+
+    IndexType(String name, int code){
+        this.name = name;
+        this.code = code;
+    }
 }

+ 8 - 0
src/main/java/io/milvus/param/LogLevel.java

@@ -0,0 +1,8 @@
+package io.milvus.param;
+
+public enum LogLevel {
+    Debug,
+    Info,
+    Warning,
+    Error,
+}

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

@@ -30,8 +30,5 @@ public enum MetricType {
     // Only supported for binary vectors
     HAMMING,
     JACCARD,
-    TANIMOTO,
-    SUBSTRUCTURE,
-    SUPERSTRUCTURE,
     ;
 }

+ 149 - 111
src/main/java/io/milvus/param/MultiConnectParam.java

@@ -2,11 +2,11 @@ package io.milvus.param;
 
 import com.google.common.collect.Lists;
 import io.milvus.exception.ParamException;
+import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 import org.apache.commons.collections4.CollectionUtils;
 
-import java.nio.charset.StandardCharsets;
-import java.util.Base64;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -16,63 +16,16 @@ import static io.milvus.common.constant.MilvusClientConstant.MilvusConsts.HOST_H
 /**
  * Parameters for client connection of multi server.
  */
-public class MultiConnectParam {
+@Getter
+@ToString
+public class MultiConnectParam extends ConnectParam {
     private final List<ServerAddress> hosts;
     private final QueryNodeSingleSearch queryNodeSingleSearch;
-    private final long connectTimeoutMs;
-    private final long keepAliveTimeMs;
-    private final long keepAliveTimeoutMs;
-    private final boolean keepAliveWithoutCalls;
-    private final boolean secure;
-    private final long idleTimeoutMs;
-    private final String authorization;
 
     private MultiConnectParam(@NonNull Builder builder) {
+        super(builder);
         this.hosts = builder.hosts;
         this.queryNodeSingleSearch = builder.queryNodeSingleSearch;
-        this.connectTimeoutMs = builder.connectTimeoutMs;
-        this.keepAliveTimeMs = builder.keepAliveTimeMs;
-        this.keepAliveTimeoutMs = builder.keepAliveTimeoutMs;
-        this.keepAliveWithoutCalls = builder.keepAliveWithoutCalls;
-        this.secure = builder.secure;
-        this.idleTimeoutMs = builder.idleTimeoutMs;
-        this.authorization = builder.authorization;
-    }
-
-    public List<ServerAddress> getHosts() {
-        return hosts;
-    }
-
-    public QueryNodeSingleSearch getQueryNodeSingleSearch() {
-        return queryNodeSingleSearch;
-    }
-
-    public long getConnectTimeoutMs() {
-        return connectTimeoutMs;
-    }
-
-    public long getKeepAliveTimeMs() {
-        return keepAliveTimeMs;
-    }
-
-    public long getKeepAliveTimeoutMs() {
-        return keepAliveTimeoutMs;
-    }
-
-    public boolean isKeepAliveWithoutCalls() {
-        return keepAliveWithoutCalls;
-    }
-
-    public boolean isSecure() {
-        return secure;
-    }
-
-    public long getIdleTimeoutMs() {
-        return idleTimeoutMs;
-    }
-
-    public String getAuthorization() {
-        return authorization;
     }
 
     public static Builder newBuilder() {
@@ -82,16 +35,9 @@ public class MultiConnectParam {
     /**
      * Builder for {@link MultiConnectParam}
      */
-    public static class Builder {
+    public static class Builder extends ConnectParam.Builder {
         private List<ServerAddress> hosts;
         private QueryNodeSingleSearch queryNodeSingleSearch;
-        private long connectTimeoutMs = 10000;
-        private long keepAliveTimeMs = Long.MAX_VALUE; // Disabling keep alive
-        private long keepAliveTimeoutMs = 20000;
-        private boolean keepAliveWithoutCalls = false;
-        private boolean secure = false;
-        private long idleTimeoutMs = TimeUnit.MILLISECONDS.convert(24, TimeUnit.HOURS);
-        private String authorization = "";
 
         private Builder() {
         }
@@ -118,6 +64,61 @@ public class MultiConnectParam {
             return this;
         }
 
+        /**
+         * Sets the host name/address.
+         *
+         * @param host host name/address
+         * @return <code>Builder</code>
+         */
+        public Builder withHost(@NonNull String host) {
+            super.withHost(host);
+            return this;
+        }
+
+        /**
+         * Sets the connection port. Port value must be greater than zero and less than 65536.
+         *
+         * @param port port value
+         * @return <code>Builder</code>
+         */
+        public Builder withPort(int port)  {
+            super.withPort(port);
+            return this;
+        }
+
+        /**
+         * Sets the database name.
+         *
+         * @param databaseName databaseName
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(@NonNull String databaseName) {
+            super.withDatabaseName(databaseName);
+            return this;
+        }
+
+        /**
+         * Sets the uri
+         *
+         * @param uri the uri of Milvus instance
+         * @return <code>Builder</code>
+         */
+        public Builder withUri(String uri) {
+            super.withUri(uri);
+            return this;
+        }
+
+        /**
+         * Sets the token
+         *
+         * @param token serving as the key for identification and authentication purposes.
+         * @return <code>Builder</code>
+         */
+        public Builder withToken(String token) {
+            super.withToken(token);
+            return this;
+        }
+
         /**
          * Sets the connection timeout value of client channel. The timeout value must be greater than zero.
          *
@@ -126,7 +127,7 @@ public class MultiConnectParam {
          * @return <code>Builder</code>
          */
         public Builder withConnectTimeout(long connectTimeout, @NonNull TimeUnit timeUnit) {
-            this.connectTimeoutMs = timeUnit.toMillis(connectTimeout);
+            super.withConnectTimeout(connectTimeout, timeUnit);
             return this;
         }
 
@@ -138,7 +139,7 @@ public class MultiConnectParam {
          * @return <code>Builder</code>
          */
         public Builder withKeepAliveTime(long keepAliveTime, @NonNull TimeUnit timeUnit) {
-            this.keepAliveTimeMs = timeUnit.toMillis(keepAliveTime);
+            super.withKeepAliveTime(keepAliveTime, timeUnit);
             return this;
         }
 
@@ -150,7 +151,7 @@ public class MultiConnectParam {
          * @return <code>Builder</code>
          */
         public Builder withKeepAliveTimeout(long keepAliveTimeout, @NonNull TimeUnit timeUnit) {
-            this.keepAliveTimeoutMs = timeUnit.toNanos(keepAliveTimeout);
+            super.withKeepAliveTimeout(keepAliveTimeout, timeUnit);
             return this;
         }
 
@@ -161,51 +162,115 @@ public class MultiConnectParam {
          * @return <code>Builder</code>
          */
         public Builder keepAliveWithoutCalls(boolean enable) {
-            keepAliveWithoutCalls = enable;
+            super.keepAliveWithoutCalls(enable);
             return this;
         }
 
         /**
-         * Enables the secure for client channel.
+         * Sets the idle timeout value of client channel. The timeout value must be larger than zero.
          *
-         * @param enable true keep-alive
+         * @param idleTimeout timeout value
+         * @param timeUnit timeout unit
          * @return <code>Builder</code>
          */
-        public Builder secure(boolean enable) {
-            secure = enable;
+        public Builder withIdleTimeout(long idleTimeout, @NonNull TimeUnit timeUnit) {
+            super.withIdleTimeout(idleTimeout, timeUnit);
             return this;
         }
 
         /**
-         * Sets secure the authorization for this connection
+         * Set a deadline for how long you are willing to wait for a reply from the server.
+         * With a deadline setting, the client will wait when encounter fast RPC fail caused by network fluctuations.
+         * The deadline value must be larger than or equal to zero. Default value is 0, deadline is disabled.
+         *
+         * @param deadline deadline value
+         * @param timeUnit deadline unit
+         * @return <code>Builder</code>
+         */
+        public Builder withRpcDeadline(long deadline, @NonNull TimeUnit timeUnit) {
+            super.withRpcDeadline(deadline, timeUnit);
+            return this;
+        }
+
+        /**
+         * Sets the username and password for this connection
+         * @param username current user
+         * @param password password
+         * @return <code>Builder</code>
+         */
+        public Builder withAuthorization(String username, String password) {
+            super.withAuthorization(username, password);
+            return this;
+        }
+
+        /**
+         * Sets secure the authorization for this connection, set to True to enable TLS
          * @param secure boolean
          * @return <code>Builder</code>
          */
         public Builder withSecure(boolean secure) {
-            this.secure = secure;
+            super.withSecure(secure);
             return this;
         }
 
         /**
-         * Sets the idle timeout value of client channel. The timeout value must be larger than zero.
-         *
-         * @param idleTimeout timeout value
-         * @param timeUnit timeout unit
+         * Sets the secure for this connection
+         * @param authorization the authorization info that has included the encoded username and password info
          * @return <code>Builder</code>
          */
-        public Builder withIdleTimeout(long idleTimeout, @NonNull TimeUnit timeUnit) {
-            this.idleTimeoutMs = timeUnit.toMillis(idleTimeout);
+        public Builder withAuthorization(@NonNull String authorization) {
+            super.withAuthorization(authorization);
             return this;
         }
 
         /**
-         * Sets the username and password for this connection
-         * @param username current user
-         * @param password password
+         * Set the client.key path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param clientKeyPath path of client.key
          * @return <code>Builder</code>
          */
-        public Builder withAuthorization(@NonNull String username, @NonNull String password) {
-            this.authorization = Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8));
+        public Builder withClientKeyPath(@NonNull String clientKeyPath) {
+            super.withClientKeyPath(clientKeyPath);
+            return this;
+        }
+
+        /**
+         * Set the client.pem path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param clientPemPath path of client.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withClientPemPath(@NonNull String clientPemPath) {
+            super.withClientPemPath(clientPemPath);
+            return this;
+        }
+
+        /**
+         * Set the ca.pem path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param caPemPath path of ca.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withCaPemPath(@NonNull String caPemPath) {
+            super.withCaPemPath(caPemPath);
+            return this;
+        }
+
+        /**
+         * Set the server.pem path for tls two-way authentication, only takes effect when "secure" is True.
+         * @param serverPemPath path of server.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withServerPemPath(@NonNull String serverPemPath) {
+            super.withServerPemPath(serverPemPath);
+            return this;
+        }
+
+        /**
+         * Set target name override for SSL host name checking, only takes effect when "secure" is True.
+         * Note: this value is passed to grpc.ssl_target_name_override
+         * @param serverName path of server.pem
+         * @return <code>Builder</code>
+         */
+        public Builder withServerName(@NonNull String serverName) {
+            super.withServerName(serverName);
             return this;
         }
 
@@ -215,6 +280,8 @@ public class MultiConnectParam {
          * @return {@link MultiConnectParam}
          */
         public MultiConnectParam build() throws ParamException {
+            super.verify();
+
             if (CollectionUtils.isEmpty(hosts)) {
                 throw new ParamException("Server addresses is empty!");
             }
@@ -241,36 +308,7 @@ public class MultiConnectParam {
             }
             this.withHosts(hostAddress);
 
-            if (keepAliveTimeMs <= 0L) {
-                throw new ParamException("Keep alive time must be positive!");
-            }
-
-            if (connectTimeoutMs <= 0L) {
-                throw new ParamException("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 MultiConnectParam(this);
         }
     }
-
-    /**
-     * Constructs a <code>String</code> by {@link ConnectParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        final StringBuffer sb = new StringBuffer("MultiConnectParam{");
-        sb.append("hosts=").append(hosts);
-        sb.append('}');
-        return sb.toString();
-    }
 }

+ 207 - 18
src/main/java/io/milvus/param/ParamUtils.java

@@ -1,7 +1,8 @@
 package io.milvus.param;
 
+import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
 import com.google.protobuf.ByteString;
-import io.grpc.StatusRuntimeException;
 import io.milvus.common.clientenum.ConsistencyLevelEnum;
 import io.milvus.common.utils.JacksonUtils;
 import io.milvus.exception.IllegalResponseException;
@@ -11,7 +12,12 @@ import io.milvus.param.collection.FieldType;
 import io.milvus.param.dml.InsertParam;
 import io.milvus.param.dml.QueryParam;
 import io.milvus.param.dml.SearchParam;
+import io.milvus.response.DescCollResponseWrapper;
+import lombok.Builder;
+import lombok.Getter;
 import lombok.NonNull;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import java.nio.ByteBuffer;
@@ -42,6 +48,10 @@ public class ParamUtils {
 
     private static void checkFieldData(FieldType fieldSchema, InsertParam.Field fieldData) {
         List<?> values = fieldData.getValues();
+        checkFieldData(fieldSchema, values);
+    }
+
+    private static void checkFieldData(FieldType fieldSchema, List<?> values) {
         HashMap<DataType, String> errMsgs = getTypeErrorMsg();
         DataType dataType = fieldSchema.getDataType();
 
@@ -133,6 +143,13 @@ public class ParamUtils {
                     }
                 }
                 break;
+            case JSON:
+                for (Object value : values) {
+                    if (!(value instanceof JSONObject)) {
+                        throw new ParamException(String.format(errMsgs.get(dataType), fieldSchema.getName()));
+                    }
+                }
+                break;
             default:
                 throw new IllegalResponseException("Unsupported data type returned by FieldData");
         }
@@ -151,6 +168,19 @@ public class ParamUtils {
         }
     }
 
+    /**
+     * Checks if a string is  null.
+     * Throws {@link ParamException} if the string is null.
+     *
+     * @param target target string
+     * @param name a name to describe this string
+     */
+    public static void CheckNullString(String target, String name) throws ParamException {
+        if (target == null) {
+            throw new ParamException(name + " cannot be null");
+        }
+    }
+
     /**
      * Checks if a metric is for float vector.
      *
@@ -170,28 +200,81 @@ public class ParamUtils {
     }
 
     /**
-     * Checks if an index type is for vector.
+     * Checks if an index type is for vector field.
      *
      * @param idx index type
      */
     public static boolean IsVectorIndex(IndexType idx) {
-        return idx != IndexType.INVALID && idx != IndexType.TRIE;
+        return idx != IndexType.INVALID && idx.getCode() < IndexType.TRIE.getCode();
+    }
+
+    /**
+     * Checks if an index type is matched with data type.
+     *
+     * @param indexType index type
+     * @param dataType data type
+     */
+    public static boolean VerifyIndexType(IndexType indexType, DataType dataType) {
+        if (dataType == DataType.FloatVector) {
+            return (IsVectorIndex(indexType) && (indexType != IndexType.BIN_FLAT) && (indexType != IndexType.BIN_IVF_FLAT));
+        } else if (dataType == DataType.BinaryVector) {
+            return indexType == IndexType.BIN_FLAT || indexType == IndexType.BIN_IVF_FLAT;
+        } else if (dataType == DataType.VarChar) {
+            return indexType == IndexType.TRIE;
+        } else {
+            return indexType == IndexType.STL_SORT;
+        }
     }
 
     public static InsertRequest convertInsertParam(@NonNull InsertParam requestParam,
-                                                   @NonNull List<FieldType> fieldTypes) {
+                                                   DescCollResponseWrapper wrapper) {
         String collectionName = requestParam.getCollectionName();
-        String partitionName = requestParam.getPartitionName();
-        List<InsertParam.Field> fields = requestParam.getFields();
 
         // 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());
+        if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+            insertBuilder.setDbName(requestParam.getDatabaseName());
+        }
+        fillFieldsData(requestParam, wrapper, insertBuilder);
 
+        // gen request
+        return insertBuilder.build();
+    }
+    private static void fillFieldsData(InsertParam requestParam, DescCollResponseWrapper wrapper, InsertRequest.Builder insertBuilder) {
+        // set partition name only when there is no partition key field
+        String partitionName = requestParam.getPartitionName();
+        boolean isPartitionKeyEnabled = false;
+        for (FieldType fieldType : wrapper.getFields()) {
+            if (fieldType.isPartitionKey()) {
+                isPartitionKeyEnabled = true;
+                break;
+            }
+        }
+        if (isPartitionKeyEnabled) {
+            if (partitionName != null && !partitionName.isEmpty()) {
+                String msg = "Collection " + requestParam.getCollectionName() + " has partition key, not allow to specify partition name";
+                throw new ParamException(msg);
+            }
+        } else if (partitionName != null) {
+            insertBuilder.setPartitionName(partitionName);
+        }
+
+        // convert insert data
+        List<InsertParam.Field> columnFields = requestParam.getFields();
+        List<JSONObject> rowFields = requestParam.getRows();
+
+        if (CollectionUtils.isNotEmpty(columnFields)) {
+            checkAndSetColumnData(requestParam, wrapper.getFields(), insertBuilder, columnFields);
+        } else {
+            checkAndSetRowData(wrapper, insertBuilder, rowFields);
+        }
+    }
+
+    private static void checkAndSetColumnData(InsertParam requestParam, List<FieldType> fieldTypes, InsertRequest.Builder insertBuilder, List<InsertParam.Field> fields) {
         // gen fieldData
         // make sure the field order must be consisted with collection schema
         for (FieldType fieldType : fieldTypes) {
@@ -215,9 +298,58 @@ public class ParamUtils {
                 throw new ParamException(msg);
             }
         }
+    }
 
-        // gen request
-        return insertBuilder.build();
+    private static void checkAndSetRowData(DescCollResponseWrapper wrapper, InsertRequest.Builder insertBuilder, List<JSONObject> rows) {
+        List<FieldType> fieldTypes = wrapper.getFields();
+
+        Map<String, InsertDataInfo> nameInsertInfo = new HashMap<>();
+        InsertDataInfo insertDynamicDataInfo = InsertDataInfo.builder().dataType(DataType.JSON).data(new LinkedList<>()).build();
+        for (JSONObject row : rows) {
+            for (FieldType fieldType : fieldTypes) {
+                String fieldName = fieldType.getName();
+                InsertDataInfo insertDataInfo = nameInsertInfo.getOrDefault(fieldName, InsertDataInfo.builder()
+                        .fieldName(fieldName).dataType(fieldType.getDataType()).data(new LinkedList<>()).build());
+
+                // check normalField
+                Object rowFieldData = row.get(fieldName);
+                if (rowFieldData != null) {
+                    if (fieldType.isAutoID()) {
+                        String msg = "The primary key: " + fieldName + " is auto generated, no need to input.";
+                        throw new ParamException(msg);
+                    }
+                    checkFieldData(fieldType, Lists.newArrayList(rowFieldData));
+
+                    insertDataInfo.getData().add(rowFieldData);
+                    nameInsertInfo.put(fieldName, insertDataInfo);
+                } else {
+                    // check if autoId
+                    if (!fieldType.isAutoID()) {
+                        String msg = "The field: " + fieldType.getName() + " is not provided.";
+                        throw new ParamException(msg);
+                    }
+                }
+            }
+
+            // deal with dynamicField
+            if (wrapper.getEnableDynamicField()) {
+                JSONObject dynamicField = new JSONObject();
+                for (String rowFieldName : row.keySet()) {
+                    if (!nameInsertInfo.containsKey(rowFieldName)) {
+                        dynamicField.put(rowFieldName, row.get(rowFieldName));
+                    }
+                }
+                insertDynamicDataInfo.getData().add(dynamicField);
+            }
+        }
+
+        for (String fieldNameKey : nameInsertInfo.keySet()) {
+            InsertDataInfo insertDataInfo = nameInsertInfo.get(fieldNameKey);
+            insertBuilder.addFieldsData(genFieldData(insertDataInfo.getFieldName(), insertDataInfo.getDataType(), insertDataInfo.getData()));
+        }
+        if (wrapper.getEnableDynamicField()) {
+            insertBuilder.addFieldsData(genFieldData(insertDynamicDataInfo.getFieldName(), insertDynamicDataInfo.getDataType(), insertDynamicDataInfo.getData(), Boolean.TRUE));
+        }
     }
 
     @SuppressWarnings("unchecked")
@@ -332,6 +464,11 @@ public class ParamUtils {
         builder.setTravelTimestamp(requestParam.getTravelTimestamp());
         builder.setGuaranteeTimestamp(guaranteeTimestamp);
 
+        // a new parameter from v2.2.9, if user didn't specify consistency level, set this parameter to true
+        if (requestParam.getConsistencyLevel() == null) {
+            builder.setUseDefaultConsistency(true);
+        }
+
         return builder.build();
     }
 
@@ -346,6 +483,11 @@ public class ParamUtils {
                 .setTravelTimestamp(requestParam.getTravelTimestamp())
                 .setGuaranteeTimestamp(guaranteeTimestamp);
 
+        // a new parameter from v2.2.9, if user didn't specify consistency level, set this parameter to true
+        if (requestParam.getConsistencyLevel() == null) {
+            builder.setUseDefaultConsistency(true);
+        }
+
         // set offset and limit value.
         // directly pass the two values, the server will verify them.
         long offset = requestParam.getOffset();
@@ -376,7 +518,7 @@ public class ParamUtils {
     private static long getGuaranteeTimestamp(ConsistencyLevelEnum consistencyLevel,
                                               long guaranteeTimestamp, Long gracefulTime){
         if(consistencyLevel == null){
-            return guaranteeTimestamp;
+            return 1L;
         }
         switch (consistencyLevel){
             case STRONG:
@@ -398,8 +540,12 @@ public class ParamUtils {
         add(DataType.BinaryVector);
     }};
 
-    @SuppressWarnings("unchecked")
     private static FieldData genFieldData(String fieldName, DataType dataType, List<?> objects) {
+        return genFieldData(fieldName, dataType, objects, Boolean.FALSE);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static FieldData genFieldData(String fieldName, DataType dataType, List<?> objects, boolean isDynamic) {
         if (objects == null) {
             throw new ParamException("Cannot generate FieldData from null object");
         }
@@ -485,6 +631,16 @@ public class ParamUtils {
                     ScalarField scalarField = ScalarField.newBuilder().setStringData(stringArray).build();
                     return builder.setFieldName(fieldName).setType(dataType).setScalars(scalarField).build();
                 }
+                case JSON: {
+                    List<ByteString> byteStrings = objects.stream().map(p -> ByteString.copyFromUtf8(((JSONObject) p).toJSONString()))
+                            .collect(Collectors.toList());
+                    JSONArray jsonArray = JSONArray.newBuilder().addAllData(byteStrings).build();
+                    ScalarField scalarField = ScalarField.newBuilder().setJsonData(jsonArray).build();
+                    if (isDynamic) {
+                        return builder.setType(dataType).setScalars(scalarField).setIsDynamic(true).build();
+                    }
+                    return builder.setFieldName(fieldName).setType(dataType).setScalars(scalarField).build();
+                }
             }
         }
 
@@ -502,8 +658,14 @@ public class ParamUtils {
                 .withName(field.getName())
                 .withDescription(field.getDescription())
                 .withPrimaryKey(field.getIsPrimaryKey())
+                .withPartitionKey(field.getIsPartitionKey())
                 .withAutoID(field.getAutoID())
-                .withDataType(field.getDataType());
+                .withDataType(field.getDataType())
+                .withIsDynamic(field.getIsDynamic());
+
+        if (field.getIsDynamic()) {
+            builder.withIsDynamic(true);
+        }
 
         List<KeyValuePair> keyValuePairs = field.getTypeParamsList();
         keyValuePairs.forEach((kv) -> builder.addTypeParam(kv.getKey(), kv.getValue()));
@@ -519,15 +681,42 @@ public class ParamUtils {
      */
     public static FieldSchema ConvertField(@NonNull FieldType field) {
         FieldSchema.Builder builder = FieldSchema.newBuilder()
-                .setIsPrimaryKey(field.isPrimaryKey())
-                .setAutoID(field.isAutoID())
                 .setName(field.getName())
                 .setDescription(field.getDescription())
-                .setDataType(field.getDataType());
-        Map<String, String> params = field.getTypeParams();
-        params.forEach((key, value) -> builder.addTypeParams(KeyValuePair.newBuilder()
-                .setKey(key).setValue(value).build()));
+                .setIsPrimaryKey(field.isPrimaryKey())
+                .setIsPartitionKey(field.isPartitionKey())
+                .setAutoID(field.isAutoID())
+                .setDataType(field.getDataType())
+                .setIsDynamic(field.isDynamic());
+
+        // assemble typeParams for CollectionSchema
+        List<KeyValuePair> typeParamsList = AssembleKvPair(field.getTypeParams());
+        if (CollectionUtils.isNotEmpty(typeParamsList)) {
+            typeParamsList.forEach(builder::addTypeParams);
+        }
 
         return builder.build();
     }
+
+    public static 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 result;
+    }
+
+    @Builder
+    @Getter
+    public static class InsertDataInfo {
+        private final String fieldName;
+        private final DataType dataType;
+        private final LinkedList<Object> data;
+    }
 }

+ 3 - 2
src/main/java/io/milvus/param/bulkinsert/ListBulkInsertTasksParam.java

@@ -46,7 +46,7 @@ public class ListBulkInsertTasksParam {
      * Builder for {@link ListBulkInsertTasksParam} class.
      */
     public static final class Builder {
-        private String collectionName;
+        private String collectionName = ""; // empty string will list all tasks in the server side
 
         // The limit count of returned tasks, list all tasks if the value is 0
         // default by 0
@@ -57,6 +57,7 @@ public class ListBulkInsertTasksParam {
 
         /**
          * Sets the target collection name, list all tasks if the name is empty.
+         * Default value is an empty string.
          *
          * @param collectionName collection name
          * @return <code>Builder</code>
@@ -68,7 +69,7 @@ public class ListBulkInsertTasksParam {
 
         /**
          * Specify limit count of returned tasks, list all tasks if the value is 0.
-         * default value is 0
+         * Default value is 0
          *
          * @param limit limit number
          * @return <code>Builder</code>

+ 1 - 1
src/main/java/io/milvus/param/collection/AlterCollectionParam.java

@@ -117,4 +117,4 @@ public class AlterCollectionParam {
                 ", properties='" + properties.toString() + '\'' +
                 '}';
     }
-}
+}

+ 70 - 22
src/main/java/io/milvus/param/collection/CreateCollectionParam.java

@@ -24,6 +24,7 @@ import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -32,19 +33,27 @@ import java.util.List;
  * Parameters for <code>createCollection</code> interface.
  */
 @Getter
+@ToString
 public class CreateCollectionParam {
     private final String collectionName;
     private final int shardsNum;
     private final String description;
     private final List<FieldType> fieldTypes;
+    private final int partitionsNum;
     private final ConsistencyLevelEnum consistencyLevel;
+    private final String databaseName;
+
+    private final boolean enableDynamicField;
 
     private CreateCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
         this.shardsNum = builder.shardsNum;
         this.description = builder.description;
         this.fieldTypes = builder.fieldTypes;
+        this.partitionsNum = builder.partitionsNum;
         this.consistencyLevel = builder.consistencyLevel;
+        this.databaseName = builder.databaseName;
+        this.enableDynamicField = builder.enableDynamicField;
     }
 
     public static Builder newBuilder() {
@@ -56,11 +65,14 @@ public class CreateCollectionParam {
      */
     public static final class Builder {
         private String collectionName;
-        private int shardsNum = 2;
+        private int shardsNum = 0; // default to 0, let server decide the value
         private String description = "";
         private final List<FieldType> fieldTypes = new ArrayList<>();
-        private ConsistencyLevelEnum consistencyLevel = ConsistencyLevelEnum.SESSION;
+        private int partitionsNum = 0;
+        private ConsistencyLevelEnum consistencyLevel = ConsistencyLevelEnum.BOUNDED;
+        private String databaseName;
 
+        private boolean enableDynamicField;
         private Builder() {
         }
 
@@ -76,7 +88,20 @@ public class CreateCollectionParam {
         }
 
         /**
-         * Sets the shards number. The number must be greater than zero. The default value is 2.
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
+        /**
+         * Sets the shards number. The number must be greater or equal to zero.
+         * The default value is 0, which means letting the server decide the value.
+         * The server set this value to 1 if user didn't specify it.
          *
          * @param shardsNum shards number to distribute insert data into multiple data nodes and query nodes.
          * @return <code>Builder</code>
@@ -86,6 +111,17 @@ public class CreateCollectionParam {
             return this;
         }
 
+        /**
+         * Sets the collection if enableDynamicField.
+         *
+         * @param enableDynamicField enableDynamicField of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withEnableDynamicField(boolean enableDynamicField) {
+            this.enableDynamicField = enableDynamicField;
+            return this;
+        }
+
         /**
          * Sets the collection description. The description can be empty. The default is "".
          *
@@ -122,7 +158,7 @@ public class CreateCollectionParam {
         }
 
         /**
-         * Sets the consistency level. The default value is {@link ConsistencyLevelEnum#SESSION}.
+         * Sets the consistency level. The default value is {@link ConsistencyLevelEnum#BOUNDED}.
          * @see ConsistencyLevelEnum
          *
          * @param consistencyLevel consistency level
@@ -133,6 +169,20 @@ public class CreateCollectionParam {
             return this;
         }
 
+        /**
+         * Sets the partitions number if there is partition key field. The number must be greater than zero.
+         * The default value is 64(defined in server side). The upper limit is 4096(defined in server side).
+         * Not allow to set this value if none of field is partition key.
+         * Only one partition key field is allowed in a collection.
+         *
+         * @param partitionsNum partitions number
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionsNum(int partitionsNum) {
+            this.partitionsNum = partitionsNum;
+            return this;
+        }
+
         /**
          * Verifies parameters and creates a new {@link CreateCollectionParam} instance.
          *
@@ -141,37 +191,35 @@ public class CreateCollectionParam {
         public CreateCollectionParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
-            if (shardsNum <= 0) {
-                throw new ParamException("ShardNum must be larger than 0");
+            if (shardsNum < 0) {
+                throw new ParamException("ShardNum must be larger or equal to 0");
             }
 
             if (fieldTypes.isEmpty()) {
                 throw new ParamException("Field numbers must be larger than 0");
             }
 
+            boolean hasPartitionKey = false;
             for (FieldType fieldType : fieldTypes) {
                 if (fieldType == null) {
                     throw new ParamException("Collection field cannot be null");
                 }
+
+                if (fieldType.isPartitionKey()) {
+                    if (hasPartitionKey) {
+                        throw new ParamException("Only one partition key field is allowed in a collection");
+                    }
+                    hasPartitionKey = true;
+                }
+            }
+
+            if (partitionsNum > 0) {
+                if (!hasPartitionKey) {
+                    throw new ParamException("None of fields is partition key, not allow to define partition number");
+                }
             }
 
             return new CreateCollectionParam(this);
         }
     }
-
-    /**
-     * Constructs a <code>String</code> by {@link CreateCollectionParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "CreateCollectionParam{" +
-                "collectionName='" + collectionName + '\'' +
-                ", shardsNum=" + shardsNum +
-                ", description='" + description + '\'' +
-                ", fields=" + fieldTypes.toString() + '\'' +
-                ", consistencyLevel=" + consistencyLevel +
-                '}';
-    }
 }

+ 85 - 0
src/main/java/io/milvus/param/collection/CreateDatabaseParam.java

@@ -0,0 +1,85 @@
+/*
+ * 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.collection;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import lombok.Getter;
+import lombok.NonNull;
+
+/**
+ * Parameters for <code>createDatabase</code> interface.
+ */
+@Getter
+public class CreateDatabaseParam {
+    private final String databaseName;
+
+    private CreateDatabaseParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link CreateDatabaseParam} class.
+     */
+    public static final class Builder {
+        private String databaseName;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the database name. Database name cannot be empty or null.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(@NonNull String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link CreateDatabaseParam} instance.
+         *
+         * @return {@link CreateDatabaseParam}
+         */
+        public CreateDatabaseParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(databaseName, "Database name");
+
+            return new CreateDatabaseParam(this);
+        }
+    }
+
+    /**
+     * Constructs a <code>String</code> by {@link CreateDatabaseParam} instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "CreateDatabaseParam{" +
+                "databaseName='" + databaseName + '\'' +
+                '}';
+    }
+}

+ 16 - 10
src/main/java/io/milvus/param/collection/DescribeCollectionParam.java

@@ -24,15 +24,19 @@ import io.milvus.param.ParamUtils;
 
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 
 /**
  * Parameters for <code>describeCollection</code> interface.
  */
 @Getter
+@ToString
 public class DescribeCollectionParam {
+    private final String databaseName;
     private final String collectionName;
 
     private DescribeCollectionParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
     }
 
@@ -44,11 +48,23 @@ public class DescribeCollectionParam {
      * Builder for {@link DescribeCollectionParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
 
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *
@@ -72,14 +88,4 @@ public class DescribeCollectionParam {
         }
     }
 
-    /**
-     * Constructs a <code>String</code> by {@link DescribeCollectionParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "DescribeCollectionParam{" +
-                "collectionName='" + collectionName + '\'' + '}';
-    }
 }

+ 14 - 0
src/main/java/io/milvus/param/collection/DropCollectionParam.java

@@ -31,9 +31,11 @@ import lombok.NonNull;
 @Getter
 public class DropCollectionParam {
     private final String collectionName;
+    private final String databaseName;
 
     private DropCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
+        this.databaseName = builder.databaseName;
     }
 
     public static Builder newBuilder() {
@@ -45,10 +47,22 @@ public class DropCollectionParam {
      */
     public static final class Builder {
         private String collectionName;
+        private String databaseName;
 
         private Builder() {
         }
 
+        /**
+         * Sets the collection name. Database name can empty or null.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *

+ 84 - 0
src/main/java/io/milvus/param/collection/DropDatabaseParam.java

@@ -0,0 +1,84 @@
+/*
+ * 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.collection;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import lombok.Getter;
+import lombok.NonNull;
+
+/**
+ * Parameters for <code>dropDatabase</code> interface.
+ */
+@Getter
+public class DropDatabaseParam {
+    private final String databaseName;
+
+    private DropDatabaseParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link DropDatabaseParam} class.
+     */
+    public static final class Builder {
+        private String databaseName;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the databaseName name. Database name cannot be empty or null.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(@NonNull String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link DropDatabaseParam} instance.
+         *
+         * @return {@link DropDatabaseParam}
+         */
+        public DropDatabaseParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(databaseName, "Database name");
+
+            return new DropDatabaseParam(this);
+        }
+    }
+
+    /**
+     * Constructs a <code>String</code> by {@link DropDatabaseParam} instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "DropDatabaseParam{" +
+                "databaseName='" + databaseName + '\'' + '}';
+    }
+}

+ 44 - 1
src/main/java/io/milvus/param/collection/FieldType.java

@@ -41,6 +41,8 @@ public class FieldType {
     private final DataType dataType;
     private final Map<String,String> typeParams;
     private final boolean autoID;
+    private final boolean partitionKey;
+    private final boolean isDynamic;
 
     private FieldType(@NonNull Builder builder){
         this.name = builder.name;
@@ -49,6 +51,8 @@ public class FieldType {
         this.dataType = builder.dataType;
         this.typeParams = builder.typeParams;
         this.autoID = builder.autoID;
+        this.partitionKey = builder.partitionKey;
+        this.isDynamic = builder.isDynamic;
     }
 
     public int getDimension() {
@@ -81,6 +85,8 @@ public class FieldType {
         private DataType dataType;
         private final Map<String,String> typeParams = new HashMap<>();
         private boolean autoID = false;
+        private boolean partitionKey = false;
+        private boolean isDynamic = false;
 
         private Builder() {
         }
@@ -90,6 +96,17 @@ public class FieldType {
             return this;
         }
 
+        /**
+         * Sets the isDynamic of a field.
+         *
+         * @param isDynamic of a field
+         * @return <code>Builder</code>
+         */
+        public Builder withIsDynamic(boolean isDynamic) {
+            this.isDynamic = isDynamic;
+            return this;
+        }
+
         /**
          * Sets the field as the primary key field.
          * Note that the current release of Milvus only support <code>Long</code> data type as primary key.
@@ -184,6 +201,20 @@ public class FieldType {
             return this;
         }
 
+        /**
+         * Sets the field to be partition key.
+         * A partition key field's values are hashed and distributed to different logic partitions.
+         * Only int64 and varchar type field can be partition key.
+         * Primary key field cannot be partition key.
+         *
+         * @param partitionKey true is partition key, false is not
+         * @return <code>Builder</code>
+         */
+        public Builder withPartitionKey(boolean partitionKey) {
+            this.partitionKey = partitionKey;
+            return this;
+        }
+
         /**
          * Verifies parameters and creates a new {@link FieldType} instance.
          *
@@ -197,7 +228,7 @@ public class FieldType {
             }
 
             if (dataType == DataType.String) {
-                throw new ParamException("String type is not supported, use VarChar instead");
+                throw new ParamException("String type is not supported, use Varchar instead");
             }
 
             if (dataType == DataType.FloatVector || dataType == DataType.BinaryVector) {
@@ -230,6 +261,17 @@ public class FieldType {
                 }
             }
 
+            // verify partition key
+            if (partitionKey) {
+                if (primaryKey) {
+                    throw new ParamException("Primary key field can not be partition key");
+                }
+                if (dataType != DataType.Int64 && dataType != DataType.VarChar) {
+                    throw new ParamException("Only Int64 and Varchar type field can be partition key");
+                }
+            }
+
+
             return new FieldType(this);
         }
     }
@@ -245,6 +287,7 @@ public class FieldType {
                 "name='" + name + '\'' +
                 ", type='" + dataType.name() + '\'' +
                 ", primaryKey=" + primaryKey +
+                ", partitionKey=" + partitionKey +
                 ", autoID=" + autoID +
                 ", params=" + typeParams +
                 '}';

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

@@ -16,12 +16,14 @@ import java.util.Objects;
  */
 @Getter
 public class FlushParam {
+    private final String databaseName;
     private final List<String> collectionNames;
     private final Boolean syncFlush;
     private final long syncFlushWaitingInterval;
     private final long syncFlushWaitingTimeout;
 
     private FlushParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionNames = builder.collectionNames;
         this.syncFlush = builder.syncFlush;
         this.syncFlushWaitingInterval = builder.syncFlushWaitingInterval;
@@ -36,6 +38,7 @@ public class FlushParam {
      * Builder for {@link FlushParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private final List<String> collectionNames = new ArrayList<>();
 
         // syncFlush:
@@ -55,6 +58,17 @@ public class FlushParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets a list of collections to be flushed.
          *

+ 14 - 0
src/main/java/io/milvus/param/collection/GetCollectionStatisticsParam.java

@@ -30,10 +30,12 @@ import lombok.NonNull;
  */
 @Getter
 public class GetCollectionStatisticsParam {
+    private final String databaseName;
     private final String collectionName;
     private final boolean flushCollection;
 
     private GetCollectionStatisticsParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.flushCollection = builder.flushCollection;
     }
@@ -46,6 +48,7 @@ public class GetCollectionStatisticsParam {
      * Builder for {@link GetCollectionStatisticsParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
 
         // if flushCollection is true, getCollectionStatistics() firstly call flush() and wait flush() finish
@@ -55,6 +58,17 @@ public class GetCollectionStatisticsParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *

+ 16 - 13
src/main/java/io/milvus/param/collection/GetLoadStateParam.java

@@ -24,6 +24,7 @@ import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 
 import java.util.List;
 
@@ -31,11 +32,14 @@ import java.util.List;
  * Parameters for <code>getLoadState</code> interface.
  */
 @Getter
+@ToString
 public class GetLoadStateParam {
+    private final String databaseName;
     private final String collectionName;
     private final List<String> partitionNames;
 
     public GetLoadStateParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
     }
@@ -48,6 +52,7 @@ public class GetLoadStateParam {
      * Builder for {@link GetLoadStateParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
 
         private final List<String> partitionNames = Lists.newArrayList();
@@ -55,6 +60,17 @@ public class GetLoadStateParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *
@@ -101,17 +117,4 @@ public class GetLoadStateParam {
             return new GetLoadStateParam(this);
         }
     }
-
-    /**
-     * Constructs a <code>String</code> by {@link GetLoadStateParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "GetLoadStateParam{" +
-                "collectionName='" + collectionName + '\'' +
-                ", partitionNames='" + partitionNames.toString() + '\'' +
-                '}';
-    }
 }

+ 16 - 10
src/main/java/io/milvus/param/collection/HasCollectionParam.java

@@ -24,16 +24,20 @@ import io.milvus.param.ParamUtils;
 
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 
 /**
  * Parameters for <code>hasCollection</code> interface.
  */
 @Getter
+@ToString
 public class HasCollectionParam {
     private final String collectionName;
+    private final String databaseName;
 
     private HasCollectionParam(@NonNull Builder builder) {
         this.collectionName = builder.collectionName;
+        this.databaseName = builder.databaseName;
     }
 
     public static Builder newBuilder() {
@@ -45,10 +49,22 @@ public class HasCollectionParam {
      */
     public static final class Builder {
         private String collectionName;
+        private String databaseName;
 
         private Builder() {
         }
 
+        /**
+         * Sets the database name. Database name can be empty.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(@NonNull String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *
@@ -72,14 +88,4 @@ public class HasCollectionParam {
         }
     }
 
-    /**
-     * Constructs a <code>String</code> by {@link HasCollectionParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "HasCollectionParam{" +
-                "collectionName='" + collectionName + '\'' + '}';
-    }
 }

+ 16 - 15
src/main/java/io/milvus/param/collection/LoadCollectionParam.java

@@ -25,6 +25,7 @@ import io.milvus.param.ParamUtils;
 
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 
 import java.util.Objects;
 
@@ -32,7 +33,9 @@ import java.util.Objects;
  * Parameters for <code>loadCollection</code> interface.
  */
 @Getter
+@ToString
 public class LoadCollectionParam {
+    private final String databaseName;
     private final String collectionName;
     private final boolean syncLoad;
     private final long syncLoadWaitingInterval;
@@ -41,6 +44,7 @@ public class LoadCollectionParam {
     private final boolean refresh;
 
     public LoadCollectionParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.syncLoad = builder.syncLoad;
         this.syncLoadWaitingInterval = builder.syncLoadWaitingInterval;
@@ -57,6 +61,7 @@ public class LoadCollectionParam {
      * Builder for {@link LoadCollectionParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
 
         // syncLoad:
@@ -86,6 +91,17 @@ public class LoadCollectionParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *
@@ -191,19 +207,4 @@ public class LoadCollectionParam {
         }
     }
 
-    /**
-     * Constructs a <code>String</code> by {@link LoadCollectionParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "LoadCollectionParam{" +
-                "collectionName='" + collectionName + '\'' +
-                ", syncLoad=" + syncLoad + '\'' +
-                ", syncLoadWaitingInterval=" + syncLoadWaitingInterval + '\'' +
-                ", syncLoadWaitingTimeout=" + syncLoadWaitingTimeout + '\'' +
-                ", replicaNumber=" + replicaNumber + '\'' +
-                '}';
-    }
 }

+ 105 - 0
src/main/java/io/milvus/param/collection/RenameCollectionParam.java

@@ -0,0 +1,105 @@
+/*
+ * 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.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.Objects;
+
+/**
+ * Parameters for <code>renameCollection</code> interface.
+ */
+@Getter
+public class RenameCollectionParam {
+    private final String oldCollectionName;
+    private final String newCollectionName;
+
+    public RenameCollectionParam(@NonNull Builder builder) {
+        this.oldCollectionName = builder.oldCollectionName;
+        this.newCollectionName = builder.newCollectionName;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link RenameCollectionParam} class.
+     */
+    public static final class Builder {
+        private String oldCollectionName;
+
+        private String newCollectionName;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the old collection name. Old collection name cannot be empty or null.
+         *
+         * @param oldCollectionName old collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withOldCollectionName(@NonNull String oldCollectionName) {
+            this.oldCollectionName = oldCollectionName;
+            return this;
+        }
+
+        /**
+         * Sets the new collection name. New collection name cannot be empty or null.
+         *
+         * @param newCollectionName new collection name
+         * @return <code>Builder</code>
+         */
+        public Builder withNewCollectionName(@NonNull String newCollectionName) {
+            this.newCollectionName = newCollectionName;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link RenameCollectionParam} instance.
+         *
+         * @return {@link RenameCollectionParam}
+         */
+        public RenameCollectionParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(oldCollectionName, "Old collection name");
+            ParamUtils.CheckNullEmptyString(newCollectionName, "New collection name");
+
+            return new RenameCollectionParam(this);
+        }
+    }
+
+    /**
+     * Constructs a <code>String</code> by {@link RenameCollectionParam} instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "RenameCollectionParam{" +
+                "oldCollectionName='" + oldCollectionName + '\'' +
+                ", NewCollectionName=" + newCollectionName + '\'' +
+                '}';
+    }
+}

+ 8 - 0
src/main/java/io/milvus/param/collection/ShowCollectionsParam.java

@@ -35,10 +35,12 @@ import java.util.List;
 public class ShowCollectionsParam {
     private final List<String> collectionNames;
     private final ShowType showType;
+    private final String databaseName;
 
     private ShowCollectionsParam(@NonNull Builder builder) {
         this.collectionNames = builder.collectionNames;
         this.showType = builder.showType;
+        this.databaseName = builder.databaseName;
     }
 
     public static Builder newBuilder() {
@@ -54,10 +56,16 @@ public class ShowCollectionsParam {
         //   default showType = ShowType.All
         //   if collectionNames is not empty, set showType = ShowType.InMemory
         private ShowType showType = ShowType.All;
+        private String databaseName;
 
         private Builder() {
         }
 
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets a list of collection names. Collection name cannot be empty or null.
          *

+ 61 - 0
src/main/java/io/milvus/param/control/GetFlushAllStateParam.java

@@ -0,0 +1,61 @@
+package io.milvus.param.control;
+
+import io.milvus.exception.ParamException;
+import lombok.Getter;
+import lombok.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parameters for <code>getFlushAllState</code> interface.
+ */
+@Getter
+public class GetFlushAllStateParam {
+    private final long flushAllTs;
+
+    private GetFlushAllStateParam(@NonNull Builder builder) {
+        this.flushAllTs = builder.flushAllTs;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link GetFlushAllStateParam} class.
+     */
+    public static final class Builder {
+        private Long flushAllTs;
+
+        private Builder() {
+        }
+
+        public Builder withFlushAllTs(@NonNull Long flushAllTs) {
+            this.flushAllTs = flushAllTs;
+            return this;
+        }
+
+
+        /**
+         * Verifies parameters and creates a new {@link GetFlushAllStateParam} instance.
+         *
+         * @return {@link GetFlushAllStateParam}
+         */
+        public GetFlushAllStateParam build() throws ParamException {
+            return new GetFlushAllStateParam(this);
+        }
+    }
+
+    /**
+     * Constructs a <code>String</code> by {@link GetFlushAllStateParam} instance.
+     *
+     * @return <code>String</code>
+     */
+    @Override
+    public String toString() {
+        return "GetFlushAllStateParam{" +
+                "flushAllTs=" + flushAllTs +
+                '}';
+    }
+}

+ 1 - 1
src/main/java/io/milvus/param/credential/UpdateCredentialParam.java

@@ -72,7 +72,7 @@ public class UpdateCredentialParam {
          */
         public UpdateCredentialParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(username, "Username");
-            ParamUtils.CheckNullEmptyString(oldPassword, "OldPassword");
+            ParamUtils.CheckNullString(oldPassword, "OldPassword");
             ParamUtils.CheckNullEmptyString(newPassword, "NewPassword");
 
             return new UpdateCredentialParam(this);

+ 93 - 16
src/main/java/io/milvus/param/dml/InsertParam.java

@@ -19,29 +19,38 @@
 
 package io.milvus.param.dml;
 
+import com.alibaba.fastjson.JSONObject;
 import io.milvus.exception.ParamException;
 import io.milvus.param.ParamUtils;
 
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.collections4.CollectionUtils;
+
 import java.util.List;
 
 /**
  * Parameters for <code>insert</code> interface.
  */
 @Getter
+@ToString
 public class InsertParam {
     private final List<Field> fields;
+    private final List<JSONObject> rows;
 
+    private final String databaseName;
     private final String collectionName;
     private final String partitionName;
     private final int rowCount;
 
     private InsertParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.partitionName = builder.partitionName;
         this.fields = builder.fields;
         this.rowCount = builder.rowCount;
+        this.rows = builder.rows;
     }
 
     public static Builder newBuilder() {
@@ -52,14 +61,27 @@ public class InsertParam {
      * Builder for {@link InsertParam} class.
      */
     public static class Builder {
+        private String databaseName;
         private String collectionName;
-        private String partitionName = "_default";
+        private String partitionName = "";
         private List<InsertParam.Field> fields;
+        private List<JSONObject> rows;
         private int rowCount;
 
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *
@@ -73,6 +95,7 @@ public class InsertParam {
 
         /**
          * Set partition name (Optional).
+         * This partition name will be ignored if the collection has a partition key field.
          *
          * @param partitionName partition name
          * @return <code>Builder</code>
@@ -83,17 +106,29 @@ public class InsertParam {
         }
 
         /**
-         * Sets the data to insert. The field list cannot be empty.
-         * @see InsertParam.Field
+         * Sets the column data to insert. The field list cannot be empty.
          *
-         * @param fields insert data
+         * @param fields insert column data
          * @return <code>Builder</code>
+         * @see InsertParam.Field
          */
         public Builder withFields(@NonNull List<InsertParam.Field> fields) {
             this.fields = fields;
             return this;
         }
 
+        /**
+         * Sets the row data to insert. The rows list cannot be empty.
+         *
+         * @param rows insert row data
+         * @return <code>Builder</code>
+         * @see JSONObject
+         */
+        public Builder withRows(@NonNull List<JSONObject> rows) {
+            this.rows = rows;
+            return this;
+        }
+
         /**
          * Verifies parameters and creates a new {@link InsertParam} instance.
          *
@@ -102,10 +137,37 @@ public class InsertParam {
         public InsertParam build() throws ParamException {
             ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
 
-            if (fields.isEmpty()) {
+            if (CollectionUtils.isEmpty(fields) && CollectionUtils.isEmpty(rows)) {
                 throw new ParamException("Fields cannot be empty");
             }
+            if (CollectionUtils.isNotEmpty(fields) && CollectionUtils.isNotEmpty(rows)) {
+                throw new ParamException("Only one of Fields and Rows is allowed to be non-empty.");
+            }
+
+            int count = 0;
+            if (CollectionUtils.isNotEmpty(fields)) {
+                if (fields.get(0) == null) {
+                    throw new ParamException("Field cannot be null." +
+                            " If the field is auto-id, just ignore it from withFields()");
+                }
+                count = fields.get(0).getValues().size();
+                checkFields(count);
+            } else {
+                count = rows.size();
+                checkRows();
+            }
 
+            this.rowCount = count;
+
+            if (count == 0) {
+                throw new ParamException("Zero row count is not allowed");
+            }
+
+            // this method doesn't check data type, the insert() api will do this work
+            return new InsertParam(this);
+        }
+
+        private void checkFields(int count) {
             for (InsertParam.Field field : fields) {
                 if (field == null) {
                     throw new ParamException("Field cannot be null." +
@@ -121,20 +183,29 @@ public class InsertParam {
             }
 
             // check row count
-            int count = fields.get(0).getValues().size();
             for (InsertParam.Field field : fields) {
                 if (field.getValues().size() != count) {
                     throw new ParamException("Row count of fields must be equal");
                 }
             }
-            this.rowCount = count;
+        }
 
-            if (count == 0) {
-                throw new ParamException("Zero row count is not allowed");
-            }
+        private void checkRows() {
+            for (JSONObject row : rows) {
+                if (row == null) {
+                    throw new ParamException("Row cannot be null." +
+                            " If the field is auto-id, just ignore it from withRows()");
+                }
 
-            // this method doesn't check data type, the insert() api will do this work
-            return new InsertParam(this);
+                for (String rowFieldName : row.keySet()) {
+                    ParamUtils.CheckNullEmptyString(rowFieldName, "Field name");
+
+                    if (row.get(rowFieldName) == null) {
+                        throw new ParamException("Field value cannot be empty." +
+                                " If the field is auto-id, just ignore it from withRows()");
+                    }
+                }
+            }
         }
     }
 
@@ -145,12 +216,17 @@ public class InsertParam {
      */
     @Override
     public String toString() {
-        return "InsertParam{" +
+        String baseStr = "InsertParam{" +
                 "collectionName='" + collectionName + '\'' +
                 ", partitionName='" + partitionName + '\'' +
-                ", row_count=" + rowCount +
-                ", fields=" + fields +
-                '}';
+                ", rowCount=" + rowCount;
+        if (!CollectionUtils.isEmpty(fields)) {
+            return baseStr +
+                    ", columnFields+" + fields +
+                    '}';
+        } else {
+            return baseStr + '}';
+        }
     }
 
     /**
@@ -168,6 +244,7 @@ public class InsertParam {
      * (why? because the rpc proto only support int32/int64 type, actually Int8/Int16/Int32 use int32 type to encode/decode)
      *
      */
+    @lombok.Builder
     public static class Field {
         private final String name;
         private final List<?> values;

+ 12 - 1
src/main/java/io/milvus/param/dml/QueryParam.java

@@ -76,7 +76,7 @@ public class QueryParam {
         private Long travelTimestamp = 0L;
         private Long gracefulTime = 5000L;
         private Long guaranteeTimestamp = Constant.GUARANTEE_EVENTUALLY_TS;
-        private ConsistencyLevelEnum consistencyLevel;
+        private ConsistencyLevelEnum consistencyLevel = null;
         private Long offset = 0L;
         private Long limit = 0L;
         private Boolean ignoreGrowing = Boolean.FALSE;
@@ -120,9 +120,14 @@ public class QueryParam {
         /**
          *  Graceful time for BOUNDED Consistency Level
          *
+         * Note: This parameter is deprecated from Milvus v2.2.9, user only input consistency level to search.
+         *       The time settings of different consistency levels are determined by the server side.
+         *       For this reason, this method is marked as Deprecated in Java SDK v2.2.11
+         *
          * @param gracefulTime graceful time
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withGracefulTime(Long gracefulTime) {
             this.gracefulTime = gracefulTime;
             return this;
@@ -184,6 +189,7 @@ public class QueryParam {
          * @param ts a timestamp value
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withTravelTimestamp(@NonNull Long ts) {
             this.travelTimestamp = ts;
             return this;
@@ -200,9 +206,14 @@ public class QueryParam {
          *
          * Default value is GUARANTEE_EVENTUALLY_TS, query executes query immediately.
          *
+         * Note: This parameter is deprecated from Milvus v2.2.9, user only input consistency level to search.
+         *       The time settings of different consistency levels are determined by the server side.
+         *       For this reason, this method is marked as Deprecated in Java SDK v2.2.11
+         *
          * @param ts a timestamp value
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withGuaranteeTimestamp(@NonNull Long ts) {
             this.guaranteeTimestamp = ts;
             return this;

+ 13 - 2
src/main/java/io/milvus/param/dml/SearchParam.java

@@ -94,7 +94,7 @@ public class SearchParam {
         private Long travelTimestamp = 0L;
         private Long guaranteeTimestamp = Constant.GUARANTEE_EVENTUALLY_TS;
         private Long gracefulTime = 5000L;
-        private ConsistencyLevelEnum consistencyLevel;
+        private ConsistencyLevelEnum consistencyLevel = null;
         private Boolean ignoreGrowing = Boolean.FALSE;
 
        Builder() {
@@ -136,9 +136,14 @@ public class SearchParam {
         /**
          *  Graceful time for BOUNDED Consistency Level
          *
+         * Note: This parameter is deprecated from Milvus v2.2.9, user only input consistency level to search.
+         *       The time settings of different consistency levels are determined by the server side.
+         *       For this reason, this method is marked as Deprecated in Java SDK v2.2.11
+         *
          * @param gracefulTime graceful time
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withGracefulTime(Long gracefulTime) {
             this.gracefulTime = gracefulTime;
             return this;
@@ -272,6 +277,7 @@ public class SearchParam {
          * @param ts a timestamp value
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withTravelTimestamp(@NonNull Long ts) {
             this.travelTimestamp = ts;
             return this;
@@ -288,9 +294,14 @@ public class SearchParam {
          *
          * Default value is GUARANTEE_EVENTUALLY_TS, server executes search immediately.
          *
+         * Note: This parameter is deprecated from Milvus v2.2.9, user only input consistency level to search.
+         *       The time settings of different consistency levels are determined by the server side.
+         *       For this reason, this method is marked as Deprecated in Java SDK v2.2.11
+         *
          * @param ts a timestamp value
          * @return <code>Builder</code>
          */
+        @Deprecated
         public Builder withGuaranteeTimestamp(@NonNull Long ts) {
             this.guaranteeTimestamp = ts;
             return this;
@@ -372,7 +383,7 @@ public class SearchParam {
                     throw new ParamException("Target vector is binary but metric type is incorrect");
                 }
             } else {
-                throw new ParamException("Target vector type must be Lst<Float> or ByteBuffer");
+                throw new ParamException("Target vector type must be List<Float> or ByteBuffer");
             }
 
             return new SearchParam(this);

+ 271 - 0
src/main/java/io/milvus/param/highlevel/collection/CreateSimpleCollectionParam.java

@@ -0,0 +1,271 @@
+/*
+ * 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.highlevel.collection;
+
+import com.google.common.collect.Lists;
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.DataType;
+import io.milvus.param.Constant;
+import io.milvus.param.IndexType;
+import io.milvus.param.MetricType;
+import io.milvus.param.ParamUtils;
+import io.milvus.param.collection.CreateCollectionParam;
+import io.milvus.param.collection.FieldType;
+import io.milvus.param.collection.LoadCollectionParam;
+import io.milvus.param.index.CreateIndexParam;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parameters for <code>createCollection</code> interface.
+ */
+@Getter
+@ToString
+public class CreateSimpleCollectionParam {
+    private final CreateCollectionParam createCollectionParam;
+    private final CreateIndexParam createIndexParam;
+    private final LoadCollectionParam loadCollectionParam;
+
+    private CreateSimpleCollectionParam(CreateCollectionParam createCollectionParam, CreateIndexParam createIndexParam, LoadCollectionParam loadCollectionParam) {
+        this.createCollectionParam = createCollectionParam;
+        this.createIndexParam = createIndexParam;
+        this.loadCollectionParam = loadCollectionParam;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link CreateSimpleCollectionParam} class.
+     */
+    public static final class Builder {
+        private String collectionName;
+        private int dimension;
+        private MetricType metricType = MetricType.L2;
+        private String description = Strings.EMPTY;
+        private String primaryField;
+        private String vectorField;
+        private boolean autoId = Boolean.FALSE;
+        private boolean syncLoad = Boolean.TRUE;
+
+        private ConsistencyLevelEnum consistencyLevel = ConsistencyLevelEnum.BOUNDED;
+
+        private DataType primaryFieldType = DataType.Int64;
+
+        private Integer maxLength;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the 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;
+        }
+
+
+        /**
+         * Sets the collection vector dimension. Dimension value must be greater than zero and less than 32768.
+         *
+         * @param dimension collection vector dimension
+         * @return <code>Builder</code>
+         */
+        public Builder withDimension(int dimension) {
+            this.dimension = dimension;
+            return this;
+        }
+
+        /**
+         * Sets the metricType of vectorField. The distance metric used for the collection.
+         *
+         * @param metricType metricType of vectorField
+         * @return <code>Builder</code>
+         */
+        public Builder withMetricType(@NonNull MetricType metricType) {
+            this.metricType = metricType;
+            return this;
+        }
+
+        /**
+         * Sets the collection description. The description can be empty. The default is "".
+         *
+         * @param description description of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withDescription(@NonNull String description) {
+            this.description = description;
+            return this;
+        }
+
+        /**
+         * Sets the primaryFiled name. The primaryField cannot be empty or null. The default is "id".
+         *
+         * @param primaryField primaryFiled name of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withPrimaryField(@NonNull String primaryField) {
+            this.primaryField = primaryField;
+            return this;
+        }
+
+        /**
+         * Sets the vectorField name. The vectorField cannot be empty or null. The default is "vector".
+         *
+         * @param vectorField vectorField name of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withVectorField(@NonNull String vectorField) {
+            this.vectorField = vectorField;
+            return this;
+        }
+
+        /**
+         * Sets the autoId. The vectorField cannot be null. The default is Boolean.False.
+         *
+         * @param autoId if open autoId
+         * @return <code>Builder</code>
+         */
+        public Builder withAutoId(boolean autoId) {
+            this.autoId = autoId;
+            return this;
+        }
+
+        /**
+         * Sets the SyncLoad when loadCollection
+         */
+        public Builder withSyncLoad(boolean syncLoad) {
+            this.syncLoad = syncLoad;
+            return this;
+        }
+
+        /**
+         * Sets the consistency level. The default value is {@link ConsistencyLevelEnum#BOUNDED}.
+         * @see ConsistencyLevelEnum
+         *
+         * @param consistencyLevel consistency level
+         * @return <code>Builder</code>
+         */
+        public Builder withConsistencyLevel(@NonNull ConsistencyLevelEnum consistencyLevel) {
+            this.consistencyLevel = consistencyLevel;
+            return this;
+        }
+
+        /**
+         * Sets the primaryFiled type. The primaryField type cannot be empty or null. The default is "DataType.Int64".
+         *
+         * @param primaryFieldType primaryFiled type of the collection
+         * @return <code>Builder</code>
+         */
+        public Builder withPrimaryFieldType(@NonNull DataType primaryFieldType) {
+            this.primaryFieldType = primaryFieldType;
+            return this;
+        }
+
+        /**
+         * Sets the primaryFiled maxLength.
+         * If primaryFiled is specified as varchar, this parameter maxLength needs to be specified
+         *
+         * @param maxLength maxLength of the primary field
+         * @return <code>Builder</code>
+         */
+        public Builder withMaxLength(@NonNull Integer maxLength) {
+            this.maxLength =  maxLength;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link CreateSimpleCollectionParam} instance.
+         *
+         * @return {@link CreateSimpleCollectionParam}
+         */
+        public CreateSimpleCollectionParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+            if (dimension <= 0) {
+                throw new ParamException("Dimension must be larger than 0");
+            }
+
+            if (primaryFieldType != DataType.Int64 && primaryFieldType != DataType.VarChar) {
+                throw new ParamException("PrimaryFieldType only supports DataType.Int64 or DataType.VarChar");
+            }
+
+            Map<String, String> primaryTypeParams = new HashMap<>();
+            if (primaryFieldType == DataType.VarChar) {
+                if (maxLength == null) {
+                    throw new ParamException("PrimaryField is of varchar type, you need to specify the size of maxLength");
+                }
+                if (maxLength <= 0) {
+                    throw new ParamException("Varchar field max length must be larger than zero");
+                }
+
+                if (autoId) {
+                    throw new ParamException("AutoID is not supported when the VarChar field is the primary key");
+                }
+                primaryTypeParams.put(Constant.VARCHAR_MAX_LENGTH, String.valueOf(maxLength));
+            }
+
+            String primaryFieldName = StringUtils.defaultIfEmpty(primaryField, Constant.PRIMARY_FIELD_NAME_DEFAULT);
+            String vectorFieldName = StringUtils.defaultString(vectorField, Constant.VECTOR_FIELD_NAME_DEFAULT);
+
+            Map<String, String> floatTypeParams = new HashMap<>();
+            floatTypeParams.put(Constant.VECTOR_DIM, String.valueOf(dimension));
+            List<FieldType> fieldTypes = Lists.newArrayList(
+                    FieldType.newBuilder().withName(primaryFieldName).withDataType(primaryFieldType).withPrimaryKey(Boolean.TRUE).withAutoID(autoId).withTypeParams(primaryTypeParams).build(),
+                    FieldType.newBuilder().withName(vectorFieldName).withDataType(DataType.FloatVector).withTypeParams(floatTypeParams).build()
+            );
+            CreateCollectionParam createCollectionParam = CreateCollectionParam.newBuilder()
+                    .withCollectionName(collectionName)
+                    .withDescription(description)
+                    .withFieldTypes(fieldTypes)
+                    .withConsistencyLevel(consistencyLevel)
+                    .withEnableDynamicField(Boolean.TRUE)
+                    .build();
+
+            CreateIndexParam createIndexParam = CreateIndexParam.newBuilder()
+                    .withCollectionName(collectionName)
+                    .withFieldName(vectorFieldName)
+                    .withIndexName(Constant.VECTOR_INDEX_NAME_DEFAULT)
+                    .withMetricType(metricType)
+                    .withIndexType(IndexType.AUTOINDEX)
+                    .build();
+
+            LoadCollectionParam loadCollectionParam = LoadCollectionParam.newBuilder()
+                    .withCollectionName(collectionName)
+                    .withSyncLoad(syncLoad)
+                    .build();
+
+            return new CreateSimpleCollectionParam(createCollectionParam, createIndexParam, loadCollectionParam);
+        }
+    }
+}

+ 63 - 0
src/main/java/io/milvus/param/highlevel/collection/ListCollectionsParam.java

@@ -0,0 +1,63 @@
+/*
+ * 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.highlevel.collection;
+
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.ShowType;
+import io.milvus.param.collection.ShowCollectionsParam;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ * Parameters for <code>listCollections</code> interface.
+ */
+@Getter
+@ToString
+public class ListCollectionsParam {
+    private final ShowCollectionsParam showCollectionsParam;
+
+    private ListCollectionsParam(ShowCollectionsParam showCollectionsParam) {
+        this.showCollectionsParam = showCollectionsParam;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link ListCollectionsParam} class.
+     */
+    public static final class Builder {
+        private Builder() {
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link ListCollectionsParam} instance.
+         *
+         * @return {@link ListCollectionsParam}
+         */
+        public ListCollectionsParam build() throws ParamException {
+            ShowCollectionsParam showCollectionsParam = ShowCollectionsParam.newBuilder()
+                    .withShowType(ShowType.All)
+                    .build();
+            return new ListCollectionsParam(showCollectionsParam);
+        }
+    }
+}

+ 34 - 0
src/main/java/io/milvus/param/highlevel/collection/response/ListCollectionsResponse.java

@@ -0,0 +1,34 @@
+/*
+ * 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.highlevel.collection.response;
+
+import lombok.Builder;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>showCollections</code> interface.
+ */
+@Builder
+@ToString
+public class ListCollectionsResponse {
+    public List<String> collectionNames;
+}

+ 108 - 0
src/main/java/io/milvus/param/highlevel/dml/DeleteIdsParam.java

@@ -0,0 +1,108 @@
+/*
+ * 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.highlevel.dml;
+
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.logging.log4j.util.Strings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parameters for <code>delete</code> interface.
+ */
+@Getter
+@ToString
+public class DeleteIdsParam {
+
+    private final String collectionName;
+    private final List<?> primaryIds;
+
+    private DeleteIdsParam(@NonNull Builder builder) {
+        this.collectionName = builder.collectionName;
+        this.primaryIds = builder.primaryIds;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link DeleteIdsParam} class.
+     */
+    public static class Builder<T> {
+        private String collectionName;
+        private List<T> primaryIds = new ArrayList<>();
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the 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;
+        }
+
+        /**
+         * Specifies primaryId fields. PrimaryIds cannot be empty or null.
+         *
+         * @param primaryIds input primary key list
+         * @return <code>Builder</code>
+         */
+        public Builder withPrimaryIds(@NonNull List<T> primaryIds) {
+            this.primaryIds.addAll(primaryIds);
+            return this;
+        }
+
+        /**
+         * Specifies primaryId field. PrimaryId cannot be empty or null.
+         *
+         * @param primaryId input primary key id
+         * @return <code>Builder</code>
+         */
+        public Builder addPrimaryId(@NonNull T primaryId) {
+            this.primaryIds.add(primaryId);
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link DeleteIdsParam} instance.
+         *
+         * @return {@link DeleteIdsParam}
+         */
+        public DeleteIdsParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+            if (CollectionUtils.isEmpty(primaryIds)) {
+                throw new ParamException("PrimaryIds cannot be empty");
+            }
+            return new DeleteIdsParam(this);
+        }
+    }
+}

+ 137 - 0
src/main/java/io/milvus/param/highlevel/dml/GetIdsParam.java

@@ -0,0 +1,137 @@
+/*
+ * 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.highlevel.dml;
+
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parameters for <code>get</code> interface.
+ */
+@Getter
+@ToString
+public class GetIdsParam {
+    private final String collectionName;
+    private final List<?> primaryIds;
+    private final List<String> outputFields;
+    private final ConsistencyLevelEnum consistencyLevel;
+
+    private GetIdsParam(@NonNull Builder builder) {
+        this.collectionName = builder.collectionName;
+        this.primaryIds = builder.primaryIds;
+        this.outputFields = builder.outputFields;
+        this.consistencyLevel = builder.consistencyLevel;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link GetIdsParam} class.
+     */
+    public static class Builder<T> {
+        private String collectionName;
+        private final List<T> primaryIds = new ArrayList<>();
+        private final List<String> outputFields = new ArrayList<>();
+
+        private ConsistencyLevelEnum consistencyLevel = null;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the 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;
+        }
+
+        /**
+         * Specifies output fields (Optional).
+         *
+         * @param outputFields output fields
+         * @return <code>Builder</code>
+         */
+        public Builder withOutputFields(@NonNull List<String> outputFields) {
+            this.outputFields.addAll(outputFields);
+            return this;
+        }
+
+        /**
+         * Specifies primaryIds fields. PrimaryIds cannot be empty or null.
+         *
+         * @param primaryIds input primary key list
+         * @return <code>Builder</code>
+         */
+        public Builder withPrimaryIds(@NonNull List<T> primaryIds) {
+            this.primaryIds.addAll(primaryIds);
+            return this;
+        }
+
+        /**
+         * Specifies primaryId field. PrimaryId cannot be empty or null.
+         *
+         * @param primaryId input primary key id
+         * @return <code>Builder</code>
+         */
+        public Builder addPrimaryId(@NonNull T primaryId) {
+            this.primaryIds.add(primaryId);
+            return this;
+        }
+
+        /**
+         * ConsistencyLevel of consistency level.
+         *
+         * @param consistencyLevel consistency level
+         * @return <code>Builder</code>
+         */
+        public Builder withConsistencyLevel(ConsistencyLevelEnum consistencyLevel) {
+            this.consistencyLevel = consistencyLevel;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link GetIdsParam} instance.
+         *
+         * @return {@link GetIdsParam}
+         */
+        public GetIdsParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+            if (CollectionUtils.isEmpty(primaryIds)) {
+                throw new ParamException("PrimaryIds cannot be empty");
+            }
+
+            return new GetIdsParam(this);
+        }
+    }
+}

+ 103 - 0
src/main/java/io/milvus/param/highlevel/dml/InsertRowsParam.java

@@ -0,0 +1,103 @@
+/*
+ * 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.highlevel.dml;
+
+import com.alibaba.fastjson.JSONObject;
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import io.milvus.param.dml.InsertParam;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.collections4.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>insert</code> interface.
+ */
+@Getter
+@ToString
+public class InsertRowsParam {
+
+    private final InsertParam insertParam;
+    private InsertRowsParam(InsertParam insertParam) {
+        this.insertParam = insertParam;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link InsertRowsParam} class.
+     */
+    public static class Builder {
+        private String collectionName;
+        private List<JSONObject> rows;
+
+        private Builder() {
+        }
+
+
+        /**
+         * Sets the 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;
+        }
+
+        /**
+         * Sets the row data to insert. The rows list cannot be empty.
+         *
+         * @param rows insert row data
+         * @return <code>Builder</code>
+         * @see JSONObject
+         */
+        public Builder withRows(@NonNull List<JSONObject> rows) {
+            this.rows = rows;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link InsertRowsParam} instance.
+         *
+         * @return {@link InsertRowsParam}
+         */
+        public InsertRowsParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+
+            if (CollectionUtils.isEmpty(rows)) {
+                throw new ParamException("Rows cannot be empty");
+            }
+
+            // this method doesn't check data type, the insert() api will do this work
+            InsertParam insertParam = InsertParam.newBuilder()
+                    .withCollectionName(collectionName)
+                    .withRows(rows)
+                    .build();
+            return new InsertRowsParam(insertParam);
+        }
+    }
+}

+ 174 - 0
src/main/java/io/milvus/param/highlevel/dml/QuerySimpleParam.java

@@ -0,0 +1,174 @@
+/*
+ * 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.highlevel.dml;
+
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.exception.ParamException;
+import io.milvus.param.ParamUtils;
+import io.milvus.param.dml.QueryParam;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Parameters for <code>query</code> interface.
+ */
+@Getter
+@ToString
+public class QuerySimpleParam {
+    private final String collectionName;
+    private final List<String> outputFields;
+    private final String filter;
+    private final Long offset;
+    private final Long limit;
+    private final ConsistencyLevelEnum consistencyLevel;
+
+    private QuerySimpleParam(@NotNull Builder builder) {
+        this.collectionName = builder.collectionName;
+        this.outputFields = builder.outputFields;
+        this.filter = builder.filter;
+        this.offset = builder.offset;
+        this.limit = builder.limit;
+        this.consistencyLevel = builder.consistencyLevel;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link QuerySimpleParam} class.
+     */
+    public static class Builder {
+        private String collectionName;
+        private final List<String> outputFields = new ArrayList<>();
+        private String filter = "";
+        private Long offset = 0L;
+        private Long limit = 0L;
+        private ConsistencyLevelEnum consistencyLevel = null;
+
+        private Builder() {
+        }
+
+        /**
+         * Sets the 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;
+        }
+
+        /**
+         * Specifies output fields (Optional).
+         *
+         * @param outFields output fields
+         * @return <code>Builder</code>
+         */
+        @Deprecated
+        public Builder withOutFields(@NonNull List<String> outFields) {
+            this.outputFields.addAll(outFields);
+            return this;
+        }
+
+        /**
+         * Specifies output fields (Optional).
+         *
+         * @param outputFields output fields
+         * @return <code>Builder</code>
+         */
+        public Builder withOutputFields(@NonNull List<String> outputFields) {
+            this.outputFields.addAll(outputFields);
+            return this;
+        }
+
+        /**
+         * Sets the expression to query entities.
+         * @see <a href="https://milvus.io/docs/v2.0.0/boolean.md">Boolean Expression Rules</a>
+         *
+         * @param filter filtering expression
+         * @return <code>Builder</code>
+         */
+        public Builder withFilter(@NonNull String filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        /**
+         * Specify a position to return results. Only take effect when the 'limit' value is specified.
+         * Default value is 0, start from begin.
+         *
+         * @param offset a value to define the position
+         * @return <code>Builder</code>
+         */
+        public Builder withOffset(@NonNull Long offset) {
+            this.offset = offset;
+            return this;
+        }
+
+        /**
+         * Specify a value to control the returned number of entities. Must be a positive value.
+         * Default value is 0, will return without limit.
+         *
+         * @param limit a value to define the limit of returned entities
+         * @return <code>Builder</code>
+         */
+        public Builder withLimit(@NonNull Long limit) {
+            this.limit = limit;
+            return this;
+        }
+
+        /**
+         * ConsistencyLevel of consistency level.
+         *
+         * @param consistencyLevel consistency level
+         * @return <code>Builder</code>
+         */
+        public Builder withConsistencyLevel(ConsistencyLevelEnum consistencyLevel) {
+            this.consistencyLevel = consistencyLevel;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link QuerySimpleParam} instance.
+         *
+         * @return {@link QuerySimpleParam}
+         */
+        public QuerySimpleParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+            ParamUtils.CheckNullEmptyString(filter, "Filter");
+
+            if (offset < 0) {
+                throw new ParamException("The offset value cannot be less than 0");
+            }
+
+            if (limit < 0) {
+                throw new ParamException("The limit value cannot be less than 0");
+            }
+            return new QuerySimpleParam(this);
+        }
+    }
+}

+ 205 - 0
src/main/java/io/milvus/param/highlevel/dml/SearchSimpleParam.java

@@ -0,0 +1,205 @@
+/*
+ * 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.highlevel.dml;
+
+import com.google.common.collect.Lists;
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.exception.ParamException;
+import io.milvus.param.Constant;
+import io.milvus.param.ParamUtils;
+import io.milvus.param.dml.SearchParam;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import org.apache.commons.collections4.CollectionUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parameters for <code>search</code> interface.
+ */
+@Getter
+@ToString
+public class SearchSimpleParam {
+    private final String collectionName;
+    private final List<?> vectors;
+    private final List<String> outputFields;
+    private final String filter;
+    private final Long offset;
+    private final int limit;
+
+    private final Map<String, Object> params;
+    private final ConsistencyLevelEnum consistencyLevel;
+
+    private SearchSimpleParam(@NotNull Builder builder) {
+        this.collectionName = builder.collectionName;
+        this.vectors = builder.vectors;
+        this.outputFields = builder.outputFields;
+        this.filter = builder.filter;
+        this.offset = builder.offset;
+        this.limit = builder.limit;
+        this.params = builder.params;
+        this.consistencyLevel = builder.consistencyLevel;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for {@link SearchSimpleParam} class.
+     */
+    public static class Builder {
+        private String collectionName;
+        private List<?> vectors;
+        private final List<String> outputFields = Lists.newArrayList();
+        private String filter = "";
+        private Long offset = 0L;
+        private int limit = 10;
+        private ConsistencyLevelEnum consistencyLevel = null;
+
+        private final Map<String, Object> params = new HashMap<>();
+
+        Builder() {
+        }
+
+        /**
+         * Sets the 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;
+        }
+
+        /**
+         * Sets expression to filter out entities before searching (Optional).
+         *
+         * @param filter filtering expression
+         * @return <code>Builder</code>
+         * @see <a href="https://milvus.io/docs/v2.0.0/boolean.md">Boolean Expression Rules</a>
+         */
+        public Builder withFilter(@NonNull String filter) {
+            this.filter = filter;
+            return this;
+        }
+
+        /**
+         * Specifies output fields (Optional).
+         *
+         * @param outputFields output fields
+         * @return <code>Builder</code>
+         */
+        public Builder withOutputFields(@NonNull List<String> outputFields) {
+            this.outputFields.addAll(outputFields);
+            return this;
+        }
+
+        /**
+         * Sets the target vectors.
+         *
+         * @param vectors list of target vectors:
+         *               if vector type is FloatVector, vectors is List of List Float;
+         *               if vector type is BinaryVector, vectors is List of ByteBuffer;
+         * @return <code>Builder</code>
+         */
+        public Builder withVectors(@NonNull List<?> vectors) {
+            this.vectors = vectors;
+            return this;
+        }
+
+        /**
+         * Specify a position to return results. Only take effect when the 'limit' value is specified.
+         * Default value is 0, start from begin.
+         *
+         * @param offset a value to define the position
+         * @return <code>Builder</code>
+         */
+        public Builder withOffset(@NonNull Long offset) {
+            this.offset = offset;
+            return this;
+        }
+
+        /**
+         * Specify a value to control the returned number of entities. Must be a positive value.
+         * Default value is 10, will return without limit.
+         *
+         * @param limit a value to define the limit of returned entities
+         * @return <code>Builder</code>
+         */
+        @Deprecated
+        public Builder withLimit(int limit) {
+            this.limit = limit;
+            return this;
+        }
+
+        /**
+         * Specify a value to control the returned number of entities. Must be a positive value.
+         * Default value is 10, will return without limit.
+         * To maintain consistency with the parameter type of the query interface, the field is declared as Long. In reality, the field is of type int.
+         *
+         * @param limit a value to define the limit of returned entities
+         * @return <code>Builder</code>
+         */
+        public Builder withLimit(@NonNull Long limit) {
+            this.limit = Math.toIntExact(limit);
+            return this;
+        }
+
+        /**
+         * ConsistencyLevel of consistency level.
+         *
+         * @param consistencyLevel consistency level
+         * @return <code>Builder</code>
+         */
+        public Builder withConsistencyLevel(ConsistencyLevelEnum consistencyLevel) {
+            this.consistencyLevel = consistencyLevel;
+            return this;
+        }
+
+        /**
+         * Verifies parameters and creates a new {@link SearchSimpleParam} instance.
+         *
+         * @return {@link SearchSimpleParam}
+         */
+        public SearchSimpleParam build() throws ParamException {
+            ParamUtils.CheckNullEmptyString(collectionName, "Collection name");
+            if (CollectionUtils.isEmpty(vectors)) {
+                throw new ParamException("vector cannot be empty");
+            }
+
+            if (offset < 0) {
+                throw new ParamException("The offset value cannot be less than 0");
+            }
+
+            if (limit < 0) {
+                throw new ParamException("The limit value cannot be less than 0");
+            }
+
+            params.put(Constant.OFFSET, offset);
+            return new SearchSimpleParam(this);
+        }
+    }
+}

+ 36 - 0
src/main/java/io/milvus/param/highlevel/dml/response/DeleteResponse.java

@@ -0,0 +1,36 @@
+/*
+ * 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.highlevel.dml.response;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>delete</code> interface.
+ */
+@Builder
+@Getter
+@ToString
+public class DeleteResponse {
+    public List<?> deleteIds;
+}

+ 35 - 0
src/main/java/io/milvus/param/highlevel/dml/response/GetResponse.java

@@ -0,0 +1,35 @@
+/*
+ * 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.highlevel.dml.response;
+
+import io.milvus.response.QueryResultsWrapper;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>get</code> interface.
+ */
+@Builder
+@Getter
+public class GetResponse {
+    public List<QueryResultsWrapper.RowRecord> rowRecords;
+}

+ 35 - 0
src/main/java/io/milvus/param/highlevel/dml/response/InsertResponse.java

@@ -0,0 +1,35 @@
+/*
+ * 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.highlevel.dml.response;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>insert</code> interface.
+ */
+@Builder
+@Getter
+public class InsertResponse {
+    private Long insertCount;
+    public List<?> insertIds;
+}

+ 35 - 0
src/main/java/io/milvus/param/highlevel/dml/response/QueryResponse.java

@@ -0,0 +1,35 @@
+/*
+ * 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.highlevel.dml.response;
+
+import io.milvus.response.QueryResultsWrapper;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>query</code> interface.
+ */
+@Builder
+@Getter
+public class QueryResponse {
+    public List<QueryResultsWrapper.RowRecord> rowRecords;
+}

+ 35 - 0
src/main/java/io/milvus/param/highlevel/dml/response/SearchResponse.java

@@ -0,0 +1,35 @@
+/*
+ * 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.highlevel.dml.response;
+
+import io.milvus.response.QueryResultsWrapper;
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+/**
+ * Parameters for <code>search</code> interface.
+ */
+@Builder
+@Getter
+public class SearchResponse {
+    public List<QueryResultsWrapper.RowRecord> rowRecords;
+}

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

@@ -26,6 +26,7 @@ import io.milvus.param.MetricType;
 import io.milvus.param.ParamUtils;
 import lombok.Getter;
 import lombok.NonNull;
+import lombok.ToString;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.HashMap;
@@ -36,21 +37,26 @@ import java.util.Objects;
  * Parameters for <code>createIndex</code> interface.
  */
 @Getter
+@ToString
 public class CreateIndexParam {
+    private final String databaseName;
     private final String collectionName;
     private final String fieldName;
     private final String indexName;
+    private final IndexType indexType; // for easily get to check with field type
     private final Map<String, String> extraParam = new HashMap<>();
     private final boolean syncMode;
     private final long syncWaitingInterval;
     private final long syncWaitingTimeout;
 
     private CreateIndexParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.fieldName = builder.fieldName;
         this.indexName = builder.indexName;
+        this.indexType = builder.indexType;
         if (builder.indexType != IndexType.INVALID) {
-            this.extraParam.put(Constant.INDEX_TYPE, builder.indexType.name());
+            this.extraParam.put(Constant.INDEX_TYPE, builder.indexType.getName());
         }
         if (builder.metricType != MetricType.INVALID) {
             this.extraParam.put(Constant.METRIC_TYPE, builder.metricType.name());
@@ -71,6 +77,7 @@ public class CreateIndexParam {
      * Builder for {@link CreateIndexParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
         private String fieldName;
         private IndexType indexType = IndexType.INVALID;
@@ -95,6 +102,17 @@ public class CreateIndexParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Set the collection name. Collection name cannot be empty or null.
          *
@@ -247,22 +265,4 @@ public class CreateIndexParam {
             return new CreateIndexParam(this);
         }
     }
-
-    /**
-     * Constructs a <code>String</code> by {@link CreateIndexParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "CreateIndexParam{" +
-                "collectionName='" + collectionName + '\'' +
-                ", fieldName='" + fieldName + '\'' +
-                ", indexName='" + indexName + '\'' +
-                ", params='" + extraParam.toString() + '\'' +
-                ", syncMode=" + syncMode +
-                ", syncWaitingInterval=" + syncWaitingInterval +
-                ", syncWaitingTimeout=" + syncWaitingTimeout +
-                '}';
-    }
 }

+ 14 - 0
src/main/java/io/milvus/param/index/DescribeIndexParam.java

@@ -32,10 +32,12 @@ import org.apache.commons.lang3.StringUtils;
  */
 @Getter
 public class DescribeIndexParam {
+    private final String databaseName;
     private final String collectionName;
     private final String indexName;
 
     private DescribeIndexParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.indexName = builder.indexName;
     }
@@ -48,12 +50,24 @@ public class DescribeIndexParam {
      * Builder for {@link DescribeIndexParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
         private String indexName = "";
 
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *

+ 14 - 0
src/main/java/io/milvus/param/partition/LoadPartitionsParam.java

@@ -35,6 +35,7 @@ import java.util.Objects;
  */
 @Getter
 public class LoadPartitionsParam {
+    private final String databaseName;
     private final String collectionName;
     private final List<String> partitionNames;
     private final boolean syncLoad;
@@ -44,6 +45,7 @@ public class LoadPartitionsParam {
     private final boolean refresh;
 
     private LoadPartitionsParam(@NonNull Builder builder) {
+        this.databaseName = builder.databaseName;
         this.collectionName = builder.collectionName;
         this.partitionNames = builder.partitionNames;
         this.syncLoad = builder.syncLoad;
@@ -61,6 +63,7 @@ public class LoadPartitionsParam {
      * Builder for {@link LoadPartitionsParam} class.
      */
     public static final class Builder {
+        private String databaseName;
         private String collectionName;
         private final List<String> partitionNames = new ArrayList<>();
 
@@ -91,6 +94,17 @@ public class LoadPartitionsParam {
         private Builder() {
         }
 
+        /**
+         * Sets the database name. database name can be nil.
+         *
+         * @param databaseName database name
+         * @return <code>Builder</code>
+         */
+        public Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the collection name. Collection name cannot be empty or null.
          *

+ 15 - 1
src/main/java/io/milvus/param/role/GrantRolePrivilegeParam.java

@@ -18,11 +18,14 @@ public class GrantRolePrivilegeParam {
 
     private final String privilege;
 
+    private final String databaseName;
+
     private GrantRolePrivilegeParam(@NonNull GrantRolePrivilegeParam.Builder builder) {
         this.roleName = builder.roleName;
         this.object = builder.object;
         this.objectName = builder.objectName;
         this.privilege = builder.privilege;
+        this.databaseName = builder.databaseName;
     }
 
     public static GrantRolePrivilegeParam.Builder newBuilder() {
@@ -37,11 +40,22 @@ public class GrantRolePrivilegeParam {
         private String object;
         private String objectName;
         private String privilege;
-
+        private String databaseName;
 
         private Builder() {
         }
 
+        /**
+         * Sets the databaseName. databaseName cannot be null.
+         *
+         * @param databaseName databaseName
+         * @return <code>Builder</code>
+         */
+        public GrantRolePrivilegeParam.Builder withDatabaseName(String databaseName) {
+            this.databaseName = databaseName;
+            return this;
+        }
+
         /**
          * Sets the roleName. RoleName cannot be empty or null.
          *

+ 72 - 0
src/main/java/io/milvus/response/DescCollResponseWrapper.java

@@ -1,6 +1,8 @@
 package io.milvus.response;
 
+import io.milvus.exception.ParamException;
 import io.milvus.grpc.CollectionSchema;
+import io.milvus.grpc.DataType;
 import io.milvus.grpc.DescribeCollectionResponse;
 import io.milvus.grpc.FieldSchema;
 import io.milvus.param.ParamUtils;
@@ -20,6 +22,11 @@ public class DescCollResponseWrapper {
         this.response = response;
     }
 
+    public boolean getEnableDynamicField() {
+        CollectionSchema schema = response.getSchema();
+        return schema.getEnableDynamicField();
+    }
+
     /**
      * Get name of the collection.
      *
@@ -114,6 +121,70 @@ public class DescCollResponseWrapper {
         return null;
     }
 
+    /**
+     * Get whether the collection dynamic field is enabled
+     *
+     * @return boolean
+     */
+    public boolean isDynamicFieldEnabled() {
+        CollectionSchema schema = response.getSchema();
+        return schema.getEnableDynamicField();
+    }
+
+    /**
+     * Get the partition key field.
+     * Return null if the partition key field doesn't exist.
+     *
+     * @return {@link FieldType} schema of the partition key field
+     */
+    public FieldType getPartitionKeyField() {
+        CollectionSchema schema = response.getSchema();
+        for (int i = 0; i < schema.getFieldsCount(); ++i) {
+            FieldSchema field = schema.getFields(i);
+            if (field.getIsPartitionKey()) {
+                return ParamUtils.ConvertField(field);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the primary key field.
+     * throw ParamException if the primary key field doesn't exist.
+     *
+     * @return {@link FieldType} schema of the primary key field
+     */
+    public FieldType getPrimaryField() {
+        CollectionSchema schema = response.getSchema();
+        for (int i = 0; i < schema.getFieldsCount(); ++i) {
+            FieldSchema field = schema.getFields(i);
+            if (field.getIsPrimaryKey()) {
+                return ParamUtils.ConvertField(field);
+            }
+        }
+
+        throw new ParamException("No primary key found.");
+    }
+
+    /**
+     * Get the vector key field.
+     * throw ParamException if the vector key field doesn't exist.
+     *
+     * @return {@link FieldType} schema of the vector key field
+     */
+    public FieldType getVectorField() {
+        CollectionSchema schema = response.getSchema();
+        for (int i = 0; i < schema.getFieldsCount(); ++i) {
+            FieldSchema field = schema.getFields(i);
+            if (field.getDataType() == DataType.FloatVector || field.getDataType() == DataType.BinaryVector) {
+                return ParamUtils.ConvertField(field);
+            }
+        }
+
+        throw new ParamException("No vector key found.");
+    }
+
     /**
      * Construct a <code>String</code> by {@link DescCollResponseWrapper} instance.
      *
@@ -129,6 +200,7 @@ public class DescCollResponseWrapper {
                 ", createdUtcTimestamp:" + getCreatedUtcTimestamp() +
                 ", aliases:" + getAliases().toString() +
                 ", fields:" + getFields().toString() +
+                ", isDynamicFieldEnabled:" + isDynamicFieldEnabled() +
                 '}';
     }
 }

+ 70 - 0
src/main/java/io/milvus/response/FieldDataWrapper.java

@@ -1,6 +1,8 @@
 package io.milvus.response;
 
+import com.alibaba.fastjson.JSONObject;
 import com.google.protobuf.ProtocolStringList;
+import io.milvus.exception.ParamException;
 import io.milvus.grpc.DataType;
 import io.milvus.grpc.FieldData;
 import io.milvus.exception.IllegalResponseException;
@@ -10,9 +12,12 @@ import lombok.NonNull;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import com.google.protobuf.ByteString;
 
+import static io.milvus.grpc.DataType.JSON;
+
 /**
  * Utility class to wrap response of <code>query/search</code> interface.
  */
@@ -27,6 +32,14 @@ public class FieldDataWrapper {
         return fieldData.getType() == DataType.FloatVector || fieldData.getType() == DataType.BinaryVector;
     }
 
+    public boolean isJsonField() {
+        return fieldData.getType() == JSON;
+    }
+
+    public boolean isDynamicField() {
+        return fieldData.getType() == JSON && fieldData.getIsDynamic();
+    }
+
     /**
      * Gets the dimension value of a vector field.
      * Throw {@link IllegalResponseException} if the field is not a vector filed.
@@ -82,6 +95,8 @@ public class FieldDataWrapper {
             case VarChar:
             case String:
                 return fieldData.getScalars().getStringData().getDataList().size();
+            case JSON:
+                return fieldData.getScalars().getJsonData().getDataList().size();
             default:
                 throw new IllegalResponseException("Unsupported data type returned by FieldData");
         }
@@ -152,8 +167,63 @@ public class FieldDataWrapper {
             case String:
                 ProtocolStringList protoStrList = fieldData.getScalars().getStringData().getDataList();
                 return protoStrList.subList(0, protoStrList.size());
+            case JSON:
+                List<ByteString> dataList = fieldData.getScalars().getJsonData().getDataList();
+                return dataList.stream().map(ByteString::toByteArray).collect(Collectors.toList());
             default:
                 throw new IllegalResponseException("Unsupported data type returned by FieldData");
         }
     }
+
+    public Integer getAsInt(int index, String paramName) throws IllegalResponseException {
+        if (isJsonField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Integer.parseInt(result);
+        }
+        throw new IllegalResponseException("Only JSON type support this operation");
+    }
+
+    public String getAsString(int index, String paramName) throws IllegalResponseException {
+        if (isJsonField()) {
+            JSONObject jsonObject = parseObjectData(index);
+            return jsonObject.getString(paramName);
+        }
+        throw new IllegalResponseException("Only JSON type support this operation");
+    }
+
+    public Boolean getAsBool(int index, String paramName) throws IllegalResponseException {
+        if (isJsonField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Boolean.parseBoolean(result);
+        }
+        throw new IllegalResponseException("Only JSON type support this operation");
+    }
+
+    public Double getAsDouble(int index, String paramName) throws IllegalResponseException {
+        if (isJsonField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Double.parseDouble(result);
+        }
+        throw new IllegalResponseException("Only JSON type support this operation");
+    }
+
+    public Object get(int index, String paramName) throws IllegalResponseException {
+        if (isJsonField()) {
+            JSONObject jsonObject = parseObjectData(index);
+            return jsonObject.get(paramName);
+        }
+        throw new IllegalResponseException("Only JSON type support this operation");
+    }
+
+    public Object valueByIdx(int index) throws ParamException {
+        if (index < 0 || index >= getFieldData().size()) {
+            throw new ParamException("index out of range");
+        }
+        return getFieldData().get(index);
+    }
+
+    private JSONObject parseObjectData(int index) {
+        Object object = valueByIdx(index);
+        return JSONObject.parseObject(new String((byte[])object));
+    }
 }

+ 2 - 2
src/main/java/io/milvus/response/GetBulkInsertStateWrapper.java

@@ -37,8 +37,8 @@ public class GetBulkInsertStateWrapper {
      */
     public List<Long> getAutoGeneratedIDs() {
         // the id list of response is id ranges
-        // for example, if the response return [1, 100, 200, 250]
-        // the full id list should be [1, 2, 3 ... , 99, 100, 200, 201, 202 ... , 249, 250]
+        // for example, if the response return [1, 100, 200, 250], the id ranges are [1, 100), [200, 250)
+        // the full id list should be [1, 2, 3 ... , 99, 200, 201, 202 ... , 249]
         List<Long> ranges = response.getIdListList();
         if (ranges.size()%2 != 0) {
             throw new IllegalResponseException("The bulk insert state response id range list is illegal");

+ 15 - 0
src/main/java/io/milvus/response/MutationResultWrapper.java

@@ -55,6 +55,21 @@ public class MutationResultWrapper {
         }
     }
 
+    /**
+     * Gets the ID array from returned by insert interface.
+     *
+     * @return List of Ids, ID array returned by insert interface
+     */
+    public List<?> getInsertIDs() {
+        if (result.getIDs().hasIntId()) {
+            return result.getIDs().getIntId().getDataList();
+        } else if (result.getIDs().hasStrId()) {
+            return result.getIDs().getStrId().getDataList();
+        } else {
+            throw new ParamException("No found insertIds, please check your requests");
+        }
+    }
+
     /**
      * Gets the row count of the deleted entities. Currently, this value is always equal to input row count
      *

+ 121 - 2
src/main/java/io/milvus/response/QueryResultsWrapper.java

@@ -1,16 +1,19 @@
 package io.milvus.response;
 
+import com.alibaba.fastjson.JSONObject;
 import io.milvus.exception.ParamException;
 import io.milvus.grpc.*;
 
+import io.milvus.response.basic.RowRecordWrapper;
+import lombok.Getter;
 import lombok.NonNull;
 
-import java.util.List;
+import java.util.*;
 
 /**
  * Utility class to wrap response of <code>query</code> interface.
  */
-public class QueryResultsWrapper {
+public class QueryResultsWrapper extends RowRecordWrapper {
     private final QueryResults results;
 
     public QueryResultsWrapper(@NonNull QueryResults results) {
@@ -34,4 +37,120 @@ public class QueryResultsWrapper {
 
         throw new ParamException("The field name doesn't exist");
     }
+
+    /**
+     * Gets row records list from query result.
+     *
+     * @return <code>List<RowRecord></code> a row records list of the query result
+     */
+    @Override
+    public List<QueryResultsWrapper.RowRecord> getRowRecords() {
+        long rowCount = getRowCount();
+        List<QueryResultsWrapper.RowRecord> records = new ArrayList<>();
+        for (long i = 0; i < rowCount; i++) {
+            QueryResultsWrapper.RowRecord record = buildRowRecord(i);
+            records.add(record);
+        }
+
+        return records;
+    }
+
+    /**
+     * Gets a row record from result.
+     *  Throws {@link ParamException} if the index is illegal.
+     *
+     * @return <code>RowRecord</code> a row record of the result
+     */
+    protected QueryResultsWrapper.RowRecord buildRowRecord(long index) {
+        QueryResultsWrapper.RowRecord record = new QueryResultsWrapper.RowRecord();
+        buildRowRecord(record, index);
+        return record;
+    }
+
+    /**
+     * Gets the row count of the result.
+     *
+     * @return <code>long</code> row count of the result
+     */
+    public long getRowCount() {
+        List<FieldData> fields = results.getFieldsDataList();
+        for (FieldData field : fields) {
+            FieldDataWrapper wrapper = new FieldDataWrapper(field);
+            return wrapper.getRowCount();
+        }
+
+        return 0L;
+    }
+
+    @Override
+    protected List<FieldData> getFieldDataList() {
+        return results.getFieldsDataList();
+    }
+
+    protected List<String> getOutputFields() {
+        return results.getOutputFieldsList();
+    }
+
+    /**
+     * Internal-use class to wrap response of <code>query</code> interface.
+     */
+    @Getter
+    public static final class RowRecord {
+        Map<String, Object> fieldValues = new HashMap<>();
+
+        public RowRecord() {
+        }
+
+        public boolean put(String keyName, Object obj) {
+            if (fieldValues.containsKey(keyName)) {
+                return false;
+            }
+            fieldValues.put(keyName, obj);
+
+            return true;
+        }
+
+        /**
+         * Get a value by a key name. If the key name is a field name, return the value of this field.
+         * If the key name is in dynamic field, return the value from the dynamic field.
+         * Throws {@link ParamException} if the key name doesn't exist.
+         *
+         * @return {@link FieldDataWrapper}
+         */
+        public Object get(String keyName) throws ParamException {
+            if (fieldValues.isEmpty()) {
+                throw new ParamException("This record is empty");
+            }
+
+            Object obj = fieldValues.get(keyName);
+            if (obj == null) {
+                // find the value from dynamic field
+                Object meta = fieldValues.get("$meta");
+                if (meta != null) {
+                    JSONObject jsonMata = (JSONObject)meta;
+                    Object innerObj = jsonMata.get(keyName);
+                    if (innerObj != null) {
+                        return innerObj;
+                    }
+                }
+                throw new ParamException("The key name is not found");
+            }
+
+            return obj;
+        }
+
+        /**
+         * Constructs a <code>String</code> by {@link QueryResultsWrapper.RowRecord} instance.
+         *
+         * @return <code>String</code>
+         */
+        @Override
+        public String toString() {
+            List<String> pairs = new ArrayList<>();
+            fieldValues.forEach((keyName, fieldValue) -> {
+                pairs.add(keyName + ":" + fieldValue.toString());
+            });
+            return pairs.toString();
+        }
+    }
 }

+ 154 - 7
src/main/java/io/milvus/response/SearchResultsWrapper.java

@@ -1,24 +1,83 @@
 package io.milvus.response;
 
+import com.alibaba.fastjson.JSONObject;
 import io.milvus.exception.IllegalResponseException;
 import io.milvus.exception.ParamException;
 import io.milvus.grpc.*;
+import io.milvus.response.basic.RowRecordWrapper;
 import lombok.Getter;
 import lombok.NonNull;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Utility class to wrap response of <code>search</code> interface.
  */
-public class SearchResultsWrapper {
+public class SearchResultsWrapper extends RowRecordWrapper {
     private final SearchResultData results;
 
     public SearchResultsWrapper(@NonNull SearchResultData results) {
         this.results = results;
     }
 
+    /**
+     * Gets {@link FieldDataWrapper} for a field.
+     * Throws {@link ParamException} if the field doesn't exist.
+     *
+     * @param fieldName field name to get output data
+     * @return {@link FieldDataWrapper}
+     */
+    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");
+    }
+
+    @Override
+    public List<QueryResultsWrapper.RowRecord> getRowRecords() {
+        List<QueryResultsWrapper.RowRecord> records = new ArrayList<>();
+        long topK = results.getTopK();
+        for (int i = 0; i < topK; ++i) {
+            QueryResultsWrapper.RowRecord rowRecord = buildRowRecord(i);
+            records.add(rowRecord);
+        }
+        return records;
+    }
+
+    /**
+     * Gets a row record from result.
+     *  Throws {@link ParamException} if the index is illegal.
+     *
+     * @return <code>RowRecord</code> a row record of the result
+     */
+    protected QueryResultsWrapper.RowRecord buildRowRecord(long index) {
+        QueryResultsWrapper.RowRecord record = new QueryResultsWrapper.RowRecord();
+
+        List<IDScore> idScore = getIDScore(0);
+        record.put("id", idScore.get((int) index).getLongID());
+        record.put("distance", idScore.get((int)index).getScore());
+
+        buildRowRecord(record, index);
+        return record;
+    }
+
+    @Override
+    protected List<FieldData> getFieldDataList() {
+        return results.getFieldsDataList();
+    }
+
+    protected List<String> getOutputFields() {
+        return results.getOutputFieldsList();
+    }
+
     /**
      * Gets data for an output field which is specified by search request.
      * Throws {@link ParamException} if the field doesn't exist.
@@ -70,8 +129,9 @@ public class SearchResultsWrapper {
             throw new IllegalResponseException("Result scores count is wrong");
         }
 
-        List<IDScore> idScore = new ArrayList<>();
+        List<IDScore> idScores = new ArrayList<>();
 
+        // set id and distance
         IDs ids = results.getIds();
         if (ids.hasIntId()) {
             LongArray longIDs = ids.getIntId();
@@ -80,7 +140,7 @@ public class SearchResultsWrapper {
             }
 
             for (int n = 0; n < k; ++n) {
-                idScore.add(new IDScore("", longIDs.getData((int)offset + n), results.getScores((int)offset + n)));
+                idScores.add(new IDScore("", longIDs.getData((int)offset + n), results.getScores((int)offset + n)));
             }
         } else if (ids.hasStrId()) {
             StringArray strIDs = ids.getStrId();
@@ -89,13 +149,57 @@ public class SearchResultsWrapper {
             }
 
             for (int n = 0; n < k; ++n) {
-                idScore.add(new IDScore(strIDs.getData((int)offset + n), 0, results.getScores((int)offset + n)));
+                idScores.add(new IDScore(strIDs.getData((int)offset + n), 0, results.getScores((int)offset + n)));
             }
         } else {
             throw new IllegalResponseException("Result ids is illegal");
         }
 
-        return idScore;
+        // set output fields
+        List<String> outputFields = results.getOutputFieldsList();
+        List<FieldData> fields = results.getFieldsDataList();
+        if (fields.isEmpty()) {
+            return idScores;
+        }
+
+        for (String outputKey : outputFields) {
+            boolean isField = false;
+            FieldDataWrapper dynamicField = null;
+            for (FieldData field : fields) {
+                if (field.getIsDynamic()) {
+                    dynamicField = new FieldDataWrapper(field);
+                }
+                if (outputKey.equals(field.getFieldName())) {
+                    FieldDataWrapper wrapper = new FieldDataWrapper(field);
+                    for (int n = 0; n < k; ++n) {
+                        if ((offset + n) >= wrapper.getRowCount()) {
+                            throw new ParamException("Illegal values length of output fields");
+                        }
+
+                        Object value = wrapper.valueByIdx((int)offset + n);
+                        if (wrapper.isJsonField()) {
+                            idScores.get(n).put(field.getFieldName(), JSONObject.parseObject(new String((byte[])value)));
+                        } else {
+                            idScores.get(n).put(field.getFieldName(), value);
+                        }
+                    }
+
+                    isField = true;
+                    break;
+                }
+            }
+
+            // if the output field is not a field name, fetch it from dynamic field
+            if (!isField && dynamicField != null) {
+                for (int n = 0; n < k; ++n) {
+                    Object obj = dynamicField.get((int)offset + n, outputKey);
+                    if (obj != null) {
+                        idScores.get(n).put(outputKey, obj);
+                    }
+                }
+            }
+        }
+        return idScores;
     }
 
     @Getter
@@ -140,6 +244,7 @@ public class SearchResultsWrapper {
         private final String strID;
         private final long longID;
         private final float score;
+        Map<String, Object> fieldValues = new HashMap<>();
 
         public IDScore(String strID, long longID, float score) {
             this.strID = strID;
@@ -147,12 +252,54 @@ public class SearchResultsWrapper {
             this.score = score;
         }
 
+        public boolean put(String keyName, Object obj) {
+            if (fieldValues.containsKey(keyName)) {
+                return false;
+            }
+            fieldValues.put(keyName, obj);
+
+            return true;
+        }
+
+        /**
+         * Get a value by a key name. If the key name is a field name, return the value of this field.
+         * If the key name is in dynamic field, return the value from the dynamic field.
+         * Throws {@link ParamException} if the key name doesn't exist.
+         *
+         * @return {@link FieldDataWrapper}
+         */
+        public Object get(String keyName) throws ParamException {
+            if (fieldValues.isEmpty()) {
+                throw new ParamException("This record is empty");
+            }
+
+            Object obj = fieldValues.get(keyName);
+            if (obj == null) {
+                // find the value from dynamic field
+                Object meta = fieldValues.get("$meta");
+                if (meta != null) {
+                    JSONObject jsonMata = (JSONObject)meta;
+                    Object innerObj = jsonMata.get(keyName);
+                    if (innerObj != null) {
+                        return innerObj;
+                    }
+                }
+            }
+
+            return obj;
+        }
+
         @Override
         public String toString() {
+            List<String> pairs = new ArrayList<>();
+            fieldValues.forEach((keyName, fieldValue) -> {
+                pairs.add(keyName + ":" + fieldValue.toString());
+            });
+
             if (strID.isEmpty()) {
-                return "(ID: " + getLongID() + " Score: " + getScore() + ")";
+                return "(ID: " + getLongID() + " Score: " + getScore() + " OutputFields: " + pairs + ")";
             } else {
-                return "(ID: '" + getStrID() + "' Score: " + getScore() + ")";
+                return "(ID: '" + getStrID() + "' Score: " + getScore()+ " OutputFields: " + pairs + ")";
             }
         }
     }

+ 4 - 0
src/main/java/io/milvus/response/ShowCollResponseWrapper.java

@@ -19,6 +19,10 @@ public class ShowCollResponseWrapper {
         this.response = response;
     }
 
+    public List<String> getCollectionNames() {
+        return response.getCollectionNamesList();
+    }
+
     /**
      * Get information of the collections.
      *

+ 81 - 0
src/main/java/io/milvus/response/basic/RowRecordWrapper.java

@@ -0,0 +1,81 @@
+package io.milvus.response.basic;
+
+import com.alibaba.fastjson.JSONObject;
+import io.milvus.exception.ParamException;
+import io.milvus.grpc.FieldData;
+import io.milvus.response.FieldDataWrapper;
+import io.milvus.response.QueryResultsWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class RowRecordWrapper {
+
+    public abstract List<QueryResultsWrapper.RowRecord> getRowRecords();
+
+    /**
+     * Get the dynamic field. Only available when a collection's dynamic field is enabled.
+     * Throws {@link ParamException} if the dynamic field doesn't exist.
+     *
+     * @return {@link FieldDataWrapper}
+     */
+    public FieldDataWrapper getDynamicWrapper() throws ParamException {
+        List<FieldData> fields = getFieldDataList();
+        for (FieldData field : fields) {
+            if (field.getIsDynamic()) {
+                return new FieldDataWrapper(field);
+            }
+        }
+
+        throw new ParamException("The dynamic field doesn't exist");
+    }
+
+    /**
+     * Gets a row record from result.
+     *  Throws {@link ParamException} if the index is illegal.
+     *
+     * @return <code>RowRecord</code> a row record of the result
+     */
+    protected QueryResultsWrapper.RowRecord buildRowRecord(QueryResultsWrapper.RowRecord record, long index) {
+        for (String outputKey : getOutputFields()) {
+            boolean isField = false;
+            for (FieldData field : getFieldDataList()) {
+                if (outputKey.equals(field.getFieldName())) {
+                    FieldDataWrapper wrapper = new FieldDataWrapper(field);
+                    if (index < 0 || index >= wrapper.getRowCount()) {
+                        throw new ParamException("Index out of range");
+                    }
+                    Object value = wrapper.valueByIdx((int)index);
+                    if (wrapper.isJsonField()) {
+                        JSONObject jsonField = JSONObject.parseObject(new String((byte[])value));
+                        if (wrapper.isDynamicField()) {
+                            for (String key: jsonField.keySet()) {
+                                record.put(key, jsonField.get(key));
+                            }
+                        } else {
+                            record.put(field.getFieldName(), jsonField);
+                        }
+                    } else {
+                        record.put(field.getFieldName(), value);
+                    }
+                    isField = true;
+                    break;
+                }
+            }
+
+            // if the output field is not a field name, fetch it from dynamic field
+            if (!isField) {
+                FieldDataWrapper dynamicField = getDynamicWrapper();
+                Object obj = dynamicField.get((int)index, outputKey);
+                if (obj != null) {
+                    record.put(outputKey, obj);
+                }
+            }
+        }
+        return record;
+    }
+
+    protected abstract List<FieldData> getFieldDataList();
+    protected abstract List<String> getOutputFields();
+
+}

+ 1 - 1
src/main/milvus-proto

@@ -1 +1 @@
-Subproject commit aa8a661302177b80869d7c84b9d3a3679b452043
+Subproject commit c3fe148f5e1dbb1ad0bb46be654ed61381da4ddf

+ 223 - 13
src/test/java/io/milvus/client/MilvusClientDockerTest.java

@@ -19,6 +19,8 @@
 
 package io.milvus.client;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.google.common.util.concurrent.ListenableFuture;
 import io.milvus.grpc.*;
 import io.milvus.param.*;
@@ -56,7 +58,7 @@ class MilvusClientDockerTest {
     private static MilvusClient client;
     private static RandomStringGenerator generator;
     private static final int dimension = 128;
-    private static final Boolean useDockerCompose = Boolean.TRUE;
+    private static final Boolean useDockerCompose = Boolean.FALSE;
 
     private static void startDockerContainer() {
         if (!useDockerCompose) {
@@ -286,6 +288,16 @@ class MilvusClientDockerTest {
         DescCollResponseWrapper desc = new DescCollResponseWrapper(response.getData());
         System.out.println(desc.toString());
 
+        R<ShowPartitionsResponse> spResp = client.showPartitions(ShowPartitionsParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build());
+        System.out.println(spResp);
+
+        ShowPartResponseWrapper wra = new ShowPartResponseWrapper(spResp.getData());
+        List<ShowPartResponseWrapper.PartitionInfo> parts = wra.getPartitionsInfo();
+        System.out.println("Partition num: "+parts.size());
+
+
         // insert data
         int rowCount = 10000;
         List<Long> ids = new ArrayList<>();
@@ -341,8 +353,19 @@ class MilvusClientDockerTest {
         GetPartStatResponseWrapper statPart = new GetPartStatResponseWrapper(statPartR.getData());
         System.out.println("Partition row count: " + statPart.getRowCount());
 
-        // create index
+        // create index on scalar field
         CreateIndexParam indexParam = CreateIndexParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFieldName(field5Name)
+                .withIndexType(IndexType.STL_SORT)
+                .withSyncMode(Boolean.TRUE)
+                .build();
+
+        R<RpcStatus> createIndexR = client.createIndex(indexParam);
+        assertEquals(R.Status.Success.getCode(), createIndexR.getStatus().intValue());
+
+        // create index on vector field
+        indexParam = CreateIndexParam.newBuilder()
                 .withCollectionName(randomCollectionName)
                 .withFieldName(field2Name)
                 .withIndexName("abv")
@@ -354,7 +377,7 @@ class MilvusClientDockerTest {
                 .withSyncWaitingTimeout(30L)
                 .build();
 
-        R<RpcStatus> createIndexR = client.createIndex(indexParam);
+        createIndexR = client.createIndex(indexParam);
         assertEquals(R.Status.Success.getCode(), createIndexR.getStatus().intValue());
 
         // get index description
@@ -600,7 +623,7 @@ class MilvusClientDockerTest {
                 .withCollectionName(randomCollectionName)
                 .withFieldName(field2Name)
                 .withIndexType(IndexType.BIN_FLAT)
-                .withMetricType(MetricType.SUPERSTRUCTURE)
+                .withMetricType(MetricType.JACCARD)
                 .withSyncMode(Boolean.TRUE)
                 .withSyncWaitingInterval(500L)
                 .withSyncWaitingTimeout(30L)
@@ -621,7 +644,7 @@ class MilvusClientDockerTest {
 
         SearchParam searchOneParam = SearchParam.newBuilder()
                 .withCollectionName(randomCollectionName)
-                .withMetricType(MetricType.SUPERSTRUCTURE)
+                .withMetricType(MetricType.JACCARD)
                 .withTopK(5)
                 .withVectors(oneVector)
                 .withVectorFieldName(field2Name)
@@ -1121,7 +1144,7 @@ class MilvusClientDockerTest {
         }
         SearchParam searchParam = SearchParam.newBuilder()
                 .withCollectionName(randomCollectionName)
-                .withMetricType(MetricType.L2)
+                .withMetricType(MetricType.IP)
                 .withTopK(topK)
                 .withVectors(targetVectors)
                 .withVectorFieldName(field2Name)
@@ -1205,11 +1228,7 @@ class MilvusClientDockerTest {
         indexTypes.put(IndexType.IVF_FLAT, "{\"nlist\":128}");
         indexTypes.put(IndexType.IVF_SQ8, "{\"nlist\":128}");
         indexTypes.put(IndexType.IVF_PQ, "{\"nlist\":128, \"m\":16, \"nbits\":8}");
-        indexTypes.put(IndexType.ANNOY, "{\"n_trees\":16}");
         indexTypes.put(IndexType.HNSW, "{\"M\":16,\"efConstruction\":64}");
-        indexTypes.put(IndexType.RHNSW_FLAT, "{\"M\":16,\"efConstruction\":64}");
-        indexTypes.put(IndexType.RHNSW_PQ, "{\"M\":16,\"efConstruction\":64, \"PQM\":16}");
-        indexTypes.put(IndexType.RHNSW_SQ, "{\"M\":16,\"efConstruction\":64}");
 
         List<MetricType> metricTypes = new ArrayList<>();
         metricTypes.add(MetricType.L2);
@@ -1258,8 +1277,8 @@ class MilvusClientDockerTest {
 
         // test all supported indexes
         List<MetricType> flatMetricTypes = new ArrayList<>();
-        flatMetricTypes.add(MetricType.SUBSTRUCTURE);
-        flatMetricTypes.add(MetricType.SUPERSTRUCTURE);
+        flatMetricTypes.add(MetricType.HAMMING);
+        flatMetricTypes.add(MetricType.JACCARD);
 
         for (MetricType metric : flatMetricTypes) {
             testIndex(randomCollectionName, field2Name, IndexType.BIN_FLAT, metric, "{}", Boolean.TRUE);
@@ -1269,7 +1288,6 @@ class MilvusClientDockerTest {
         List<MetricType> ivfMetricTypes = new ArrayList<>();
         ivfMetricTypes.add(MetricType.HAMMING);
         ivfMetricTypes.add(MetricType.JACCARD);
-        ivfMetricTypes.add(MetricType.TANIMOTO);
 
         for (MetricType metric : ivfMetricTypes) {
             testIndex(randomCollectionName, field2Name, IndexType.BIN_IVF_FLAT, metric, "{\"nlist\":128}", Boolean.TRUE);
@@ -1278,4 +1296,196 @@ class MilvusClientDockerTest {
 
         client.dropCollection(DropCollectionParam.newBuilder().withCollectionName(randomCollectionName).build());
     }
+
+    @Test
+    void testDynamicField() {
+        String randomCollectionName = generator.generate(10);
+
+        // collection schema
+        String field1Name = "id_field";
+        String field2Name = "vec_field";
+        String field3Name = "json_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.JSON)
+                .withName(field3Name)
+                .withDescription("info")
+                .build());
+
+        // create collection
+        CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFieldTypes(fieldsSchema)
+                .withEnableDynamicField(true)
+                .build();
+
+        R<RpcStatus> createR = client.createCollection(createParam);
+        assertEquals(R.Status.Success.getCode(), createR.getStatus().intValue());
+
+        R<DescribeCollectionResponse> response = client.describeCollection(DescribeCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build());
+
+        DescCollResponseWrapper desc = new DescCollResponseWrapper(response.getData());
+        System.out.println(desc.toString());
+
+        // create index
+        CreateIndexParam indexParam = CreateIndexParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFieldName(field2Name)
+                .withIndexName("abv")
+                .withIndexType(IndexType.FLAT)
+                .withMetricType(MetricType.L2)
+                .withExtraParam("{}")
+                .build();
+
+        R<RpcStatus> createIndexR = client.createIndex(indexParam);
+        assertEquals(R.Status.Success.getCode(), createIndexR.getStatus().intValue());
+
+        // load collection
+        R<RpcStatus> loadR = client.loadCollection(LoadCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build());
+        assertEquals(R.Status.Success.getCode(), loadR.getStatus().intValue());
+
+        int rowCount = 10;
+        // insert data by row-based
+        List<JSONObject> rows = new ArrayList<>();
+        for (long i = 0L; i < rowCount; ++i) {
+            JSONObject row = new JSONObject();
+            row.put(field1Name, i);
+            row.put(field2Name, generateFloatVectors(1).get(0));
+
+            // JSON field
+            JSONObject info = new JSONObject();
+            info.put("row-based-info", i);
+            row.put(field3Name, info);
+
+            // extra meta is automatically stored in dynamic field
+            row.put("extra_meta", i % 3 == 0);
+            row.put(generator.generate(5), 100);
+
+            rows.add(row);
+        }
+
+        InsertParam insertRowParam = InsertParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withRows(rows)
+                .build();
+
+        R<MutationResult> insertRowResp = client.insert(insertRowParam);
+        assertEquals(R.Status.Success.getCode(), insertRowResp.getStatus().intValue());
+        System.out.println(rowCount + " rows inserted");
+
+        // insert data by column-based
+        List<Long> ids = new ArrayList<>();
+        List<JSONObject> infos = new ArrayList<>();
+        for (long i = 0L; i < rowCount; ++i) {
+            ids.add(rowCount + i);
+            JSONObject obj = new JSONObject();
+            obj.put("column-based-info", i);
+            obj.put(generator.generate(5), i);
+            infos.add(obj);
+        }
+        List<List<Float>> vectors = generateFloatVectors(rowCount);
+
+        List<InsertParam.Field> fieldsInsert = new ArrayList<>();
+        fieldsInsert.add(new InsertParam.Field(field1Name, ids));
+        fieldsInsert.add(new InsertParam.Field(field2Name, vectors));
+        fieldsInsert.add(new InsertParam.Field(field3Name, infos));
+
+        InsertParam insertColumnsParam = InsertParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFields(fieldsInsert)
+                .build();
+
+        R<MutationResult> insertColumnResp = client.insert(insertColumnsParam);
+        assertEquals(R.Status.Success.getCode(), insertColumnResp.getStatus().intValue());
+        System.out.println(rowCount + " rows inserted");
+
+        // get collection statistics
+        R<GetCollectionStatisticsResponse> statR = client.getCollectionStatistics(GetCollectionStatisticsParam
+                .newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withFlush(true)
+                .build());
+        assertEquals(R.Status.Success.getCode(), statR.getStatus().intValue());
+
+        GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(statR.getData());
+        System.out.println("Collection row count: " + stat.getRowCount());
+
+        // retrieve rows
+        String expr = "extra_meta == true";
+        List<String> outputFields = Arrays.asList(field3Name, "extra_meta");
+        QueryParam queryParam = QueryParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withExpr(expr)
+                .withOutFields(outputFields)
+                .build();
+
+        R<QueryResults> queryR = client.query(queryParam);
+        assertEquals(R.Status.Success.getCode(), queryR.getStatus().intValue());
+
+        QueryResultsWrapper queryResultsWrapper = new QueryResultsWrapper(queryR.getData());
+        List<QueryResultsWrapper.RowRecord> records = queryResultsWrapper.getRowRecords();
+        System.out.println("Query results:");
+        for (QueryResultsWrapper.RowRecord record:records) {
+            System.out.println(record);
+            Object extraMeta = record.get("extra_meta");
+            if (extraMeta != null) {
+                System.out.println("'extra_meta' is from dynamic field, value: " + extraMeta);
+            }
+        }
+
+        // search
+        List<List<Float>> targetVectors = generateFloatVectors(2);
+        int topK = 5;
+        SearchParam searchParam = SearchParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .withMetricType(MetricType.L2)
+                .withTopK(topK)
+                .withVectors(targetVectors)
+                .withVectorFieldName(field2Name)
+                .withParams("{}")
+                .withOutFields(outputFields)
+                .build();
+
+        R<SearchResults> searchR = client.search(searchParam);
+        assertEquals(R.Status.Success.getCode(), searchR.getStatus().intValue());
+
+        // 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:");
+            for (SearchResultsWrapper.IDScore score:scores) {
+                System.out.println(score);
+                Object extraMeta = score.get("extra_meta");
+                if (extraMeta != null) {
+                    System.out.println("'extra_meta' is from dynamic field, value: " + extraMeta);
+                }
+            }
+        }
+
+        // drop collection
+        R<RpcStatus> dropR = client.dropCollection(DropCollectionParam.newBuilder()
+                .withCollectionName(randomCollectionName)
+                .build());
+        assertEquals(R.Status.Success.getCode(), dropR.getStatus().intValue());
+    }
 }

+ 44 - 4
src/test/java/io/milvus/client/MilvusMultiClientDockerTest.java

@@ -72,13 +72,43 @@ class MilvusMultiClientDockerTest {
                 logger.error("Failed to start docker compose, status " + status);
             }
 
-            // here stop 10 seconds, reason: although milvus container is alive, it is still in initializing,
-            // connection will failed and get error "proxy not health".
-            TimeUnit.SECONDS.sleep(10);
             logger.debug("Milvus service started");
         } catch (Throwable t) {
             logger.error("Failed to execute docker compose up", t);
         }
+
+        ConnectParam connectParam = connectParamBuilder().withAuthorization("root", "Milvus").build();
+        MilvusServiceClient tempClient = new MilvusServiceClient(connectParam);
+        long waitTime = 0;
+        while (true) {
+            // although milvus container is alive, it is still in initializing,
+            // connection will failed and get error "proxy not health".
+            // check health state for every few seconds, until the server is ready.
+            long checkInterval = 3;
+            try {
+                TimeUnit.SECONDS.sleep(checkInterval);
+            } catch (InterruptedException t) {
+                logger.error("Interrupted", t);
+                break;
+            }
+
+            try{
+                R<CheckHealthResponse> resp = tempClient.checkHealth();
+                if (resp.getData().getIsHealthy()) {
+                    logger.info(String.format("Milvus service is ready after %d seconds", waitTime));
+                    break;
+                }
+                logger.info("Milvus service is not ready, waiting...");
+            } catch (Throwable t) {
+                logger.error("Milvus service is in initialize, not able to connect", t);
+            }
+
+            waitTime += checkInterval;
+            if (waitTime > 120) {
+                logger.error(String.format("Milvus service failed to start within %d seconds", waitTime));
+                break;
+            }
+        }
     }
 
     private static void stopDockerContainer() {
@@ -126,7 +156,9 @@ class MilvusMultiClientDockerTest {
     public static void setUp() {
         startDockerContainer();
 
-        MultiConnectParam connectParam = multiConnectParamBuilder().withAuthorization("root", "Milvus").build();
+        MultiConnectParam connectParam = multiConnectParamBuilder()
+                .withAuthorization("root", "Milvus")
+                .build();
         client = new MilvusMultiServiceClient(connectParam);
 //        TimeUnit.SECONDS.sleep(10);
         generator = new RandomStringGenerator.Builder().withinRange('a', 'z').build();
@@ -141,6 +173,14 @@ class MilvusMultiClientDockerTest {
         stopDockerContainer();
     }
 
+    protected static ConnectParam.Builder connectParamBuilder() {
+        return connectParamBuilder("localhost", 19530);
+    }
+
+    private static ConnectParam.Builder connectParamBuilder(String host, int port) {
+        return ConnectParam.newBuilder().withHost(host).withPort(port);
+    }
+
     private static MultiConnectParam.Builder multiConnectParamBuilder() {
         ServerAddress serverAddress = ServerAddress.newBuilder().withHost("localhost").withPort(19530).build();
         ServerAddress serverSlaveAddress = ServerAddress.newBuilder().withHost("localhost").withPort(19531).withHealthPort(9092).build();

+ 126 - 16
src/test/java/io/milvus/client/MilvusServiceClientTest.java

@@ -68,6 +68,7 @@ class MilvusServiceClientTest {
         return new MilvusServiceClient(connectParam);
     }
 
+    @SuppressWarnings("unchecked")
     private <T, P> void invokeFunc(Method testFunc, MilvusServiceClient client, T param, int ret, boolean equalRet) {
         try {
             R<P> resp = (R<P>) testFunc.invoke(client, param);
@@ -83,7 +84,6 @@ class MilvusServiceClientTest {
         }
     }
 
-    @SuppressWarnings("unchecked")
     private <T, P> void testFuncByName(String funcName, T param) {
         // start mock server
         MockMilvusServer server = startServer();
@@ -274,7 +274,7 @@ class MilvusServiceClientTest {
 
     @Test
     void createCollectionParam() {
-        // test throw exception with illegal input
+        // test throw exception with illegal input for FieldType
         assertThrows(ParamException.class, () ->
                 FieldType.newBuilder()
                         .withName("")
@@ -295,6 +295,54 @@ class MilvusServiceClientTest {
                         .build()
         );
 
+        assertThrows(ParamException.class, () ->
+                FieldType.newBuilder()
+                        .withName("userID")
+                        .withDataType(DataType.Int64)
+                        .withPrimaryKey(true)
+                        .withPartitionKey(true)
+                        .build()
+        );
+
+        assertThrows(ParamException.class, () ->
+                FieldType.newBuilder()
+                        .withName("userID")
+                        .withDataType(DataType.FloatVector)
+                        .withPartitionKey(true)
+                        .build()
+        );
+
+        assertDoesNotThrow(() ->
+                FieldType.newBuilder()
+                        .withName("partitionKey")
+                        .withDataType(DataType.Int64)
+                        .withPartitionKey(true)
+                        .build()
+        );
+
+        assertDoesNotThrow(() ->
+                FieldType.newBuilder()
+                        .withName("partitionKey")
+                        .withDataType(DataType.VarChar)
+                        .withMaxLength(120)
+                        .withPartitionKey(true)
+                        .build()
+        );
+
+        Map<String, String> params = new HashMap<>();
+        params.put("1", "1");
+        assertThrows(ParamException.class, () ->
+                FieldType.newBuilder()
+                        .withName("vec")
+                        .withDescription("desc")
+                        .withDataType(DataType.FloatVector)
+                        .withTypeParams(params)
+                        .addTypeParam("2", "2")
+                        .withDimension(-1)
+                        .build()
+        );
+
+        // test throw exception with illegal input for CreateCollectionParam
         assertThrows(ParamException.class, () ->
                 CreateCollectionParam
                         .newBuilder()
@@ -324,7 +372,7 @@ class MilvusServiceClientTest {
                 CreateCollectionParam
                         .newBuilder()
                         .withCollectionName("collection1")
-                        .withShardsNum(0)
+                        .withShardsNum(-1)
                         .addFieldType(fieldType1)
                         .build()
         );
@@ -339,16 +387,38 @@ class MilvusServiceClientTest {
                         .build()
         );
 
-        Map<String, String> params = new HashMap<>();
-        params.put("1", "1");
         assertThrows(ParamException.class, () ->
-                FieldType.newBuilder()
-                        .withName("vec")
-                        .withDescription("desc")
-                        .withDataType(DataType.FloatVector)
-                        .withTypeParams(params)
-                        .addTypeParam("2", "2")
-                        .withDimension(-1)
+                CreateCollectionParam
+                        .newBuilder()
+                        .withCollectionName("collection1")
+                        .withShardsNum(0)
+                        .withPartitionsNum(10)
+                        .addFieldType(fieldType1)
+                        .build()
+        );
+
+        FieldType fieldType2 = FieldType.newBuilder()
+                .withName("partitionKey")
+                .withDataType(DataType.Int64)
+                .withPartitionKey(true)
+                .build();
+
+        assertDoesNotThrow(() ->
+                CreateCollectionParam
+                        .newBuilder()
+                        .withCollectionName("collection1")
+                        .addFieldType(fieldType1)
+                        .addFieldType(fieldType2)
+                        .build()
+        );
+
+        assertDoesNotThrow(() ->
+                CreateCollectionParam
+                        .newBuilder()
+                        .withCollectionName("collection1")
+                        .withPartitionsNum(100)
+                        .addFieldType(fieldType1)
+                        .addFieldType(fieldType2)
                         .build()
         );
     }
@@ -1255,11 +1325,51 @@ class MilvusServiceClientTest {
         MockMilvusServer server = startServer();
         MilvusServiceClient client = startClient();
 
+        // createIndex() calls describeCollection() to check input
+        CollectionSchema schema = CollectionSchema.newBuilder()
+                .addFields(FieldSchema.newBuilder()
+                        .setName("field1")
+                        .setDataType(DataType.FloatVector)
+                        .addTypeParams(KeyValuePair.newBuilder().setKey(Constant.VECTOR_DIM).setValue("256").build())
+                        .build())
+                .build();
+        mockServerImpl.setDescribeCollectionResponse(DescribeCollectionResponse.newBuilder().setSchema(schema).build());
+
         // test return ok for sync mode loading
         mockServerImpl.setDescribeIndexResponse(DescribeIndexResponse.newBuilder()
                 .addIndexDescriptions(IndexDescription.newBuilder().setState(IndexState.InProgress).build())
                 .build());
 
+        // field doesn't exist
+        CreateIndexParam param = CreateIndexParam.newBuilder()
+                .withCollectionName("collection1")
+                .withFieldName("aaa")
+                .withIndexType(IndexType.IVF_FLAT)
+                .withMetricType(MetricType.L2)
+                .withExtraParam("dummy")
+                .withSyncMode(Boolean.TRUE)
+                .withSyncWaitingInterval(500L)
+                .withSyncWaitingTimeout(2L)
+                .build();
+
+        R<RpcStatus> resp = client.createIndex(param);
+        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
+
+        // index type doesn't match with data type
+        param = CreateIndexParam.newBuilder()
+                .withCollectionName("collection1")
+                .withFieldName("field1")
+                .withIndexType(IndexType.BIN_IVF_FLAT)
+                .withMetricType(MetricType.L2)
+                .withExtraParam("dummy")
+                .withSyncMode(Boolean.TRUE)
+                .withSyncWaitingInterval(500L)
+                .withSyncWaitingTimeout(2L)
+                .build();
+
+        resp = client.createIndex(param);
+        assertNotEquals(R.Status.Success.getCode(), resp.getStatus());
+
         new Thread(() -> {
             try {
                 TimeUnit.SECONDS.sleep(1);
@@ -1273,7 +1383,7 @@ class MilvusServiceClientTest {
             }
         }, "RefreshIndexState").start();
 
-        CreateIndexParam param = CreateIndexParam.newBuilder()
+        param = CreateIndexParam.newBuilder()
                 .withCollectionName("collection1")
                 .withFieldName("field1")
                 .withIndexType(IndexType.IVF_FLAT)
@@ -1285,7 +1395,7 @@ class MilvusServiceClientTest {
                 .build();
 
         // test return ok with correct input
-        R<RpcStatus> resp = client.createIndex(param);
+        resp = client.createIndex(param);
         assertEquals(R.Status.Success.getCode(), resp.getStatus());
 
         // stop mock server
@@ -2408,10 +2518,10 @@ class MilvusServiceClientTest {
                 .build()
         );
 
-        assertThrows(ParamException.class, () -> UpdateCredentialParam
+        assertDoesNotThrow(() -> UpdateCredentialParam
                 .newBuilder()
                 .withUsername("username")
-                .withOldPassword("  ")
+                .withOldPassword("")
                 .withNewPassword("newPassword")
                 .build()
         );

Some files were not shown because too many files changed in this diff