Browse Source

1. support milvus db; 2. support json and dynamic schema 3. support rename; (#517)

yelusion 2 years ago
parent
commit
ad9e3a4a51
33 changed files with 1344 additions and 199 deletions
  1. 6 0
      pom.xml
  2. 290 67
      src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java
  3. 39 0
      src/main/java/io/milvus/client/MilvusClient.java
  4. 37 0
      src/main/java/io/milvus/client/MilvusMultiServiceClient.java
  5. 5 0
      src/main/java/io/milvus/client/MilvusServiceClient.java
  6. 1 1
      src/main/java/io/milvus/common/clientenum/ConsistencyLevelEnum.java
  7. 1 1
      src/main/java/io/milvus/common/utils/JacksonUtils.java
  8. 55 0
      src/main/java/io/milvus/common/utils/URLParser.java
  9. 44 15
      src/main/java/io/milvus/param/ConnectParam.java
  10. 157 5
      src/main/java/io/milvus/param/ParamUtils.java
  11. 1 1
      src/main/java/io/milvus/param/collection/AlterCollectionParam.java
  12. 32 17
      src/main/java/io/milvus/param/collection/CreateCollectionParam.java
  13. 85 0
      src/main/java/io/milvus/param/collection/CreateDatabaseParam.java
  14. 16 10
      src/main/java/io/milvus/param/collection/DescribeCollectionParam.java
  15. 14 0
      src/main/java/io/milvus/param/collection/DropCollectionParam.java
  16. 84 0
      src/main/java/io/milvus/param/collection/DropDatabaseParam.java
  17. 14 0
      src/main/java/io/milvus/param/collection/FieldType.java
  18. 14 0
      src/main/java/io/milvus/param/collection/FlushParam.java
  19. 14 0
      src/main/java/io/milvus/param/collection/GetCollectionStatisticsParam.java
  20. 16 13
      src/main/java/io/milvus/param/collection/GetLoadStateParam.java
  21. 16 10
      src/main/java/io/milvus/param/collection/HasCollectionParam.java
  22. 16 15
      src/main/java/io/milvus/param/collection/LoadCollectionParam.java
  23. 105 0
      src/main/java/io/milvus/param/collection/RenameCollectionParam.java
  24. 8 0
      src/main/java/io/milvus/param/collection/ShowCollectionsParam.java
  25. 61 0
      src/main/java/io/milvus/param/control/GetFlushAllStateParam.java
  26. 69 25
      src/main/java/io/milvus/param/dml/InsertParam.java
  27. 16 18
      src/main/java/io/milvus/param/index/CreateIndexParam.java
  28. 14 0
      src/main/java/io/milvus/param/index/DescribeIndexParam.java
  29. 14 0
      src/main/java/io/milvus/param/partition/LoadPartitionsParam.java
  30. 15 1
      src/main/java/io/milvus/param/role/GrantRolePrivilegeParam.java
  31. 5 0
      src/main/java/io/milvus/response/DescCollResponseWrapper.java
  32. 69 0
      src/main/java/io/milvus/response/FieldDataWrapper.java
  33. 11 0
      src/main/java/io/milvus/response/QueryResultsWrapper.java

+ 6 - 0
pom.xml

@@ -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.76</version.fastjson>
     </properties>
 
     <dependencyManagement>
@@ -203,6 +204,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>

+ 290 - 67
src/main/java/io/milvus/client/AbstractMilvusGrpcClient.java

@@ -84,14 +84,17 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         return result;
     }
 
-    private void waitForLoadingCollection(String collectionName, List<String> partitionNames,
+    private void waitForLoadingCollection(String databaseName, String collectionName, List<String> partitionNames,
                                           long waitingInterval, long timeout) throws IllegalResponseException {
         long tsBegin = System.currentTimeMillis();
         if (partitionNames == null || partitionNames.isEmpty()) {
-            ShowCollectionsRequest showCollectionRequest = ShowCollectionsRequest.newBuilder()
+            ShowCollectionsRequest.Builder builder = ShowCollectionsRequest.newBuilder()
                     .addCollectionNames(collectionName)
-                    .setType(ShowType.InMemory)
-                    .build();
+                    .setType(ShowType.InMemory);
+            if (StringUtils.isNotEmpty(databaseName)) {
+                builder.setDbName(databaseName);
+            }
+            ShowCollectionsRequest showCollectionRequest = builder.build();
 
             // Use showCollection() to check loading percentages of the collection.
             // If the inMemory percentage is 100, that means the collection has finished loading.
@@ -134,10 +137,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
             }
 
         } else {
-            ShowPartitionsRequest showPartitionsRequest = ShowPartitionsRequest.newBuilder()
+            ShowPartitionsRequest.Builder builder = ShowPartitionsRequest.newBuilder()
                     .setCollectionName(collectionName)
-                    .addAllPartitionNames(partitionNames)
-                    .setType(ShowType.InMemory).build();
+                    .addAllPartitionNames(partitionNames);
+            if (StringUtils.isNotEmpty(databaseName)) {
+                builder.setDbName(databaseName);
+            }
+            ShowPartitionsRequest showPartitionsRequest = builder.setType(ShowType.InMemory).build();
 
             // Use showPartitions() to check loading percentages of all the partitions.
             // If each partition's  inMemory percentage is 100, that means all the partitions have finished loading.
@@ -275,7 +281,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         }
     }
 
-    private R<Boolean> waitForIndex(String collectionName, String indexName, String fieldName,
+    private R<Boolean> waitForIndex(String databaseName, String collectionName, String indexName, String fieldName,
                                     long waitingInterval, long timeout) {
         // This method use getIndexState() to check index state.
         // If all index state become Finished, then we say the sync index action is finished.
@@ -289,10 +295,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 return R.failed(R.Status.UnexpectedError, msg);
             }
 
-            DescribeIndexRequest request = DescribeIndexRequest.newBuilder()
+            DescribeIndexRequest.Builder builder = DescribeIndexRequest.newBuilder()
                     .setCollectionName(collectionName)
-                    .setIndexName(indexName)
-                    .build();
+                    .setIndexName(indexName);
+            if (StringUtils.isNotEmpty(databaseName)) {
+                builder.setDbName(databaseName);
+            }
+            DescribeIndexRequest request = builder.build();
 
             DescribeIndexResponse response = blockingStub().describeIndex(request);
 
@@ -347,8 +356,12 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            HasCollectionRequest hasCollectionRequest = HasCollectionRequest.newBuilder()
-                    .setCollectionName(requestParam.getCollectionName())
+            HasCollectionRequest.Builder builder = HasCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            HasCollectionRequest hasCollectionRequest = builder
                     .build();
 
             BoolResponse response = blockingStub().hasCollection(hasCollectionRequest);
@@ -371,6 +384,102 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         }
     }
 
+    @Override
+    public R<RpcStatus> createDatabase(CreateDatabaseParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            // Construct CreateDatabaseRequest
+            CreateDatabaseRequest createDatabaseRequest = CreateDatabaseRequest.newBuilder()
+                    .setDbName(requestParam.getDatabaseName())
+                    .build();
+
+            Status response = blockingStub().createDatabase(createDatabaseRequest);
+
+            if (response.getErrorCode() == ErrorCode.Success) {
+                logDebug("CreateDatabaseRequest successfully! Database name:{}",
+                        requestParam.getDatabaseName());
+                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
+            } else {
+                return failedStatus("CreateDatabaseRequest", response);
+            }
+        } catch (StatusRuntimeException e) {
+            logError("CreateDatabaseRequest RPC failed! Database name:{}\n{}",
+                    requestParam.getDatabaseName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("CreateDatabaseRequest failed! Database name:{}\n{}",
+                    requestParam.getDatabaseName(), e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<ListDatabasesResponse> listDatabases() {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        try {
+            // Construct ListDatabasesRequest
+            ListDatabasesRequest listDatabasesRequest = ListDatabasesRequest.newBuilder()
+                    .build();
+
+            ListDatabasesResponse response = blockingStub().listDatabases(listDatabasesRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logDebug("ListDatabasesRequest successfully!");
+                return R.success(response);
+            } else {
+                return failedStatus("ListDatabasesRequest", response.getStatus());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("ListDatabasesRequest RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("ListDatabasesRequest failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<RpcStatus> dropDatabase(DropDatabaseParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            // Construct DropDatabaseRequest
+            DropDatabaseRequest dropDatabaseRequest = DropDatabaseRequest.newBuilder()
+                    .setDbName(requestParam.getDatabaseName())
+                    .build();
+
+            Status response = blockingStub().dropDatabase(dropDatabaseRequest);
+
+            if (response.getErrorCode() == ErrorCode.Success) {
+                logDebug("DropDatabaseRequest successfully! Database name:{}",
+                        requestParam.getDatabaseName());
+                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
+            } else {
+                return failedStatus("DropDatabaseRequest", response);
+            }
+        } catch (StatusRuntimeException e) {
+            logError("DropDatabaseRequest RPC failed! Database name:{}\n{}",
+                    requestParam.getDatabaseName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("DropDatabaseRequest failed! Database name:{}\n{}",
+                    requestParam.getDatabaseName(), e.getMessage());
+            return R.failed(e);
+        }
+    }
+
     @Override
     public R<RpcStatus> createCollection(@NonNull CreateCollectionParam requestParam) {
         if (!clientIsReady()) {
@@ -383,7 +492,8 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
             // Construct CollectionSchema Params
             CollectionSchema.Builder collectionSchemaBuilder = CollectionSchema.newBuilder();
             collectionSchemaBuilder.setName(requestParam.getCollectionName())
-                    .setDescription(requestParam.getDescription());
+                    .setDescription(requestParam.getDescription())
+                    .setEnableDynamicField(requestParam.isEnableDynamicField());
 
             long fieldID = 0;
             for (FieldType fieldType : requestParam.getFieldTypes()) {
@@ -394,7 +504,8 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                         .setIsPartitionKey(fieldType.isPartitionKey())
                         .setDescription(fieldType.getDescription())
                         .setDataType(fieldType.getDataType())
-                        .setAutoID(fieldType.isAutoID());
+                        .setAutoID(fieldType.isAutoID())
+                        .setIsDynamic(fieldType.isDynamic());
 
                 // assemble typeParams for CollectionSchema
                 List<KeyValuePair> typeParamsList = assembleKvPair(fieldType.getTypeParams());
@@ -407,16 +518,16 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
             }
 
             // Construct CreateCollectionRequest
-            CreateCollectionRequest.Builder createCollectionBuilder = CreateCollectionRequest.newBuilder();
-            if (requestParam.getPartitionsNum() > 0) {
-                createCollectionBuilder.setNumPartitions(requestParam.getPartitionsNum());
-            }
-            CreateCollectionRequest createCollectionRequest = createCollectionBuilder
+            CreateCollectionRequest.Builder builder = CreateCollectionRequest.newBuilder()
                     .setCollectionName(requestParam.getCollectionName())
                     .setShardsNum(requestParam.getShardsNum())
                     .setConsistencyLevelValue(requestParam.getConsistencyLevel().getCode())
-                    .setSchema(collectionSchemaBuilder.build().toByteString())
-                    .build();
+                    .setSchema(collectionSchemaBuilder.build().toByteString());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+
+            CreateCollectionRequest createCollectionRequest = builder.build();
 
             Status response = blockingStub().createCollection(createCollectionRequest);
 
@@ -447,9 +558,12 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            DropCollectionRequest dropCollectionRequest = DropCollectionRequest.newBuilder()
-                    .setCollectionName(requestParam.getCollectionName())
-                    .build();
+            DropCollectionRequest.Builder builder = DropCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            DropCollectionRequest dropCollectionRequest = builder.build();
 
             Status response = blockingStub().dropCollection(dropCollectionRequest);
 
@@ -480,10 +594,15 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            LoadCollectionRequest loadCollectionRequest = LoadCollectionRequest.newBuilder()
+            LoadCollectionRequest.Builder builder = LoadCollectionRequest.newBuilder()
                     .setCollectionName(requestParam.getCollectionName())
                     .setReplicaNumber(requestParam.getReplicaNumber())
-                    .setRefresh(requestParam.isRefresh())
+                    .setRefresh(requestParam.isRefresh());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+
+            LoadCollectionRequest loadCollectionRequest = builder
                     .build();
 
             Status response = blockingStub().loadCollection(loadCollectionRequest);
@@ -494,7 +613,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
             // sync load, wait until collection finish loading
             if (requestParam.isSyncLoad()) {
-                waitForLoadingCollection(requestParam.getCollectionName(), null,
+                waitForLoadingCollection(requestParam.getDatabaseName(), requestParam.getCollectionName(), null,
                         requestParam.getSyncLoadWaitingInterval(), requestParam.getSyncLoadWaitingTimeout());
             }
 
@@ -550,7 +669,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
     }
 
     @Override
-    public R<DescribeCollectionResponse> describeCollection(@NonNull DescribeCollectionParam requestParam) {
+    public R<RpcStatus> renameCollection(RenameCollectionParam requestParam) {
         if (!clientIsReady()) {
             return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
         }
@@ -558,10 +677,47 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            DescribeCollectionRequest describeCollectionRequest = DescribeCollectionRequest.newBuilder()
-                    .setCollectionName(requestParam.getCollectionName())
+            RenameCollectionRequest renameCollectionRequest = RenameCollectionRequest.newBuilder()
+                    .setOldName(requestParam.getOldCollectionName())
+                    .setNewName(requestParam.getNewCollectionName())
                     .build();
 
+            Status response = blockingStub().renameCollection(renameCollectionRequest);
+
+            if (response.getErrorCode() == ErrorCode.Success) {
+                logDebug("RenameCollectionRequest successfully! Collection name:{}",
+                        requestParam.getOldCollectionName());
+                return R.success(new RpcStatus(RpcStatus.SUCCESS_MSG));
+            } else {
+                return failedStatus("RenameCollectionRequest", response);
+            }
+        } catch (StatusRuntimeException e) {
+            logError("RenameCollectionRequest RPC failed! Collection name:{}\n{}",
+                    requestParam.getOldCollectionName(), e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("RenameCollectionRequest failed! Collection name:{}\n{}",
+                    requestParam.getOldCollectionName(), e.getMessage());
+            return R.failed(e);
+        }
+    }
+
+    @Override
+    public R<DescribeCollectionResponse> describeCollection(@NonNull DescribeCollectionParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            DescribeCollectionRequest.Builder builder = DescribeCollectionRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            DescribeCollectionRequest describeCollectionRequest = builder.build();
+
             DescribeCollectionResponse response = blockingStub().describeCollection(describeCollectionRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
@@ -591,6 +747,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
             // flush collection if client command to do it(some times user may want to know the newest row count)
             if (requestParam.isFlushCollection()) {
                 R<FlushResponse> response = flush(FlushParam.newBuilder()
+                        .withDatabaseName(requestParam.getDatabaseName())
                         .addCollectionName(requestParam.getCollectionName())
                         .withSyncFlush(Boolean.TRUE)
                         .build());
@@ -599,9 +756,12 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 }
             }
 
-            GetCollectionStatisticsRequest getCollectionStatisticsRequest = GetCollectionStatisticsRequest.newBuilder()
-                    .setCollectionName(requestParam.getCollectionName())
-                    .build();
+            GetCollectionStatisticsRequest.Builder builder = GetCollectionStatisticsRequest.newBuilder()
+                    .setCollectionName(requestParam.getCollectionName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            GetCollectionStatisticsRequest getCollectionStatisticsRequest = builder.build();
 
             GetCollectionStatisticsResponse response = blockingStub().getCollectionStatistics(getCollectionStatisticsRequest);
 
@@ -629,9 +789,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            ShowCollectionsRequest showCollectionsRequest = ShowCollectionsRequest.newBuilder()
+            ShowCollectionsRequest.Builder builder = ShowCollectionsRequest.newBuilder()
                     .addAllCollectionNames(requestParam.getCollectionNames())
-                    .setType(requestParam.getShowType()).build();
+                    .setType(requestParam.getShowType());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            ShowCollectionsRequest showCollectionsRequest = builder.build();
 
             ShowCollectionsResponse response = blockingStub().showCollections(showCollectionsRequest);
 
@@ -700,10 +864,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
         try {
             MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Flush).build();
-            FlushRequest flushRequest = FlushRequest.newBuilder()
+            FlushRequest.Builder builder = FlushRequest.newBuilder()
                     .setBase(msgBase)
-                    .addAllCollectionNames(requestParam.getCollectionNames())
-                    .build();
+                    .addAllCollectionNames(requestParam.getCollectionNames());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            FlushRequest flushRequest = builder.build();
             FlushResponse response = blockingStub().flush(flushRequest);
 
             if (Objects.equals(requestParam.getSyncFlush(), Boolean.TRUE)) {
@@ -862,12 +1029,15 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            LoadPartitionsRequest loadPartitionsRequest = LoadPartitionsRequest.newBuilder()
+            LoadPartitionsRequest.Builder builder = LoadPartitionsRequest.newBuilder()
                     .setCollectionName(requestParam.getCollectionName())
                     .setReplicaNumber(requestParam.getReplicaNumber())
                     .addAllPartitionNames(requestParam.getPartitionNames())
-                    .setRefresh(requestParam.isRefresh())
-                    .build();
+                    .setRefresh(requestParam.isRefresh());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            LoadPartitionsRequest loadPartitionsRequest = builder.build();
 
             Status response = blockingStub().loadPartitions(loadPartitionsRequest);
 
@@ -877,7 +1047,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
             // sync load, wait until all partitions finish loading
             if (requestParam.isSyncLoad()) {
-                waitForLoadingCollection(requestParam.getCollectionName(), requestParam.getPartitionNames(),
+                waitForLoadingCollection(requestParam.getDatabaseName(), requestParam.getCollectionName(), requestParam.getPartitionNames(),
                         requestParam.getSyncLoadWaitingInterval(), requestParam.getSyncLoadWaitingTimeout());
             }
 
@@ -1122,11 +1292,14 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
                 extraParamList.forEach(createIndexRequestBuilder::addExtraParams);
             }
 
-            CreateIndexRequest createIndexRequest = createIndexRequestBuilder
+            CreateIndexRequest.Builder builder = createIndexRequestBuilder
                     .setCollectionName(requestParam.getCollectionName())
                     .setFieldName(requestParam.getFieldName())
-                    .setIndexName(requestParam.getIndexName())
-                    .build();
+                    .setIndexName(requestParam.getIndexName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            CreateIndexRequest createIndexRequest = builder.build();
 
             Status response = blockingStub().createIndex(createIndexRequest);
             if (response.getErrorCode() != ErrorCode.Success) {
@@ -1134,7 +1307,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
             }
 
             if (requestParam.isSyncMode()) {
-                R<Boolean> res = waitForIndex(requestParam.getCollectionName(), requestParam.getIndexName(),
+                R<Boolean> res = waitForIndex(requestParam.getDatabaseName(), requestParam.getCollectionName(), requestParam.getIndexName(),
                         requestParam.getFieldName(),
                         requestParam.getSyncWaitingInterval(), requestParam.getSyncWaitingTimeout());
                 if (res.getStatus() != R.Status.Success.getCode()) {
@@ -1199,10 +1372,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            DescribeIndexRequest describeIndexRequest = DescribeIndexRequest.newBuilder()
+            DescribeIndexRequest.Builder builder = DescribeIndexRequest.newBuilder()
                     .setCollectionName(requestParam.getCollectionName())
-                    .setIndexName(requestParam.getIndexName())
-                    .build();
+                    .setIndexName(requestParam.getIndexName());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+            DescribeIndexRequest describeIndexRequest = builder.build();
 
             DescribeIndexResponse response = blockingStub().describeIndex(describeIndexRequest);
 
@@ -1332,16 +1508,18 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            R<DescribeCollectionResponse> descResp = describeCollection(DescribeCollectionParam.newBuilder()
-                    .withCollectionName(requestParam.getCollectionName())
-                    .build());
+            DescribeCollectionParam.Builder builder = DescribeCollectionParam.newBuilder()
+                    .withDatabaseName(requestParam.getDatabaseName())
+                    .withCollectionName(requestParam.getCollectionName());
+            R<DescribeCollectionResponse> descResp = describeCollection(builder.build());
+
             if (descResp.getStatus() != R.Status.Success.getCode()) {
                 logError("Failed to describe collection: {}", requestParam.getCollectionName());
                 return R.failed(R.Status.valueOf(descResp.getStatus()), descResp.getMessage());
             }
 
             DescCollResponseWrapper wrapper = new DescCollResponseWrapper(descResp.getData());
-            InsertRequest insertRequest = ParamUtils.convertInsertParam(requestParam, wrapper.getFields());
+            InsertRequest insertRequest = ParamUtils.convertInsertParam(requestParam, wrapper);
             MutationResult response = blockingStub().insert(insertRequest);
 
             if (response.getStatus().getErrorCode() == ErrorCode.Success) {
@@ -1372,9 +1550,13 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
         logInfo(requestParam.toString());
 
-        R<DescribeCollectionResponse> descResp = describeCollection(DescribeCollectionParam.newBuilder()
-                .withCollectionName(requestParam.getCollectionName())
-                .build());
+        DescribeCollectionParam.Builder builder = DescribeCollectionParam.newBuilder()
+                .withCollectionName(requestParam.getCollectionName());
+        if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+            builder.withDatabaseName(requestParam.getDatabaseName());
+        }
+        R<DescribeCollectionResponse> descResp = describeCollection(builder.build());
+
         if (descResp.getStatus() != R.Status.Success.getCode()) {
             logDebug("Failed to describe collection: {}", requestParam.getCollectionName());
             return Futures.immediateFuture(
@@ -1382,7 +1564,7 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         }
 
         DescCollResponseWrapper wrapper = new DescCollResponseWrapper(descResp.getData());
-        InsertRequest insertRequest = ParamUtils.convertInsertParam(requestParam, wrapper.getFields());
+        InsertRequest insertRequest = ParamUtils.convertInsertParam(requestParam, wrapper);
         ListenableFuture<MutationResult> response = futureStub().insert(insertRequest);
 
         Futures.addCallback(
@@ -1636,6 +1818,38 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         }
     }
 
+    @Override
+    public R<GetFlushAllStateResponse> getFlushAllState(GetFlushAllStateParam requestParam) {
+        if (!clientIsReady()) {
+            return R.failed(new ClientNotConnectedException("Client rpc channel is not ready"));
+        }
+
+        logInfo(requestParam.toString());
+
+        try {
+            MsgBase msgBase = MsgBase.newBuilder().setMsgType(MsgType.Flush).build();
+            GetFlushAllStateRequest getFlushStateRequest = GetFlushAllStateRequest.newBuilder()
+                    .setBase(msgBase)
+                    .setFlushAllTs(requestParam.getFlushAllTs())
+                    .build();
+
+            GetFlushAllStateResponse response = blockingStub().getFlushAllState(getFlushStateRequest);
+
+            if (response.getStatus().getErrorCode() == ErrorCode.Success) {
+                logDebug("getFlushAllState successfully!");
+                return R.success(response);
+            } else {
+                return failedStatus("getFlushAllState", response.getStatus());
+            }
+        } catch (StatusRuntimeException e) {
+            logError("getFlushAllState RPC failed:\n{}", e.getStatus().toString());
+            return R.failed(e);
+        } catch (Exception e) {
+            logError("getFlushAllState failed:\n{}", e.getMessage());
+            return R.failed(e);
+        }
+    }
+
     @Override
     public R<GetPersistentSegmentInfoResponse> getPersistentSegmentInfo(@NonNull GetPersistentSegmentInfoParam requestParam) {
         if (!clientIsReady()) {
@@ -2191,16 +2405,20 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
         logInfo(requestParam.toString());
 
+        GrantEntity.Builder builder = GrantEntity.newBuilder()
+                .setRole(RoleEntity.newBuilder().setName(requestParam.getRoleName()).build())
+                .setObjectName(requestParam.getObjectName())
+                .setObject(ObjectEntity.newBuilder().setName(requestParam.getObject()).build())
+                .setGrantor(GrantorEntity.newBuilder()
+                        .setPrivilege(PrivilegeEntity.newBuilder().setName(requestParam.getPrivilege()).build()).build());
+        if (StringUtils.isNotBlank(requestParam.getDatabaseName())) {
+            builder.setDbName(requestParam.getDatabaseName());
+        }
+
         try {
             OperatePrivilegeRequest request = OperatePrivilegeRequest.newBuilder()
                     .setType(OperatePrivilegeType.Grant)
-                    .setEntity(GrantEntity.newBuilder()
-                            .setRole(RoleEntity.newBuilder().setName(requestParam.getRoleName()).build())
-                            .setObjectName(requestParam.getObjectName())
-                            .setObject(ObjectEntity.newBuilder().setName(requestParam.getObject()).build())
-                            .setGrantor(GrantorEntity.newBuilder()
-                                    .setPrivilege(PrivilegeEntity.newBuilder().setName(requestParam.getPrivilege()).build()).build())
-                            .build())
+                    .setEntity(builder.build())
                     .build();
 
             Status response = blockingStub().operatePrivilege(request);
@@ -2459,9 +2677,14 @@ public abstract class AbstractMilvusGrpcClient implements MilvusClient {
         logInfo(requestParam.toString());
 
         try {
-            GetLoadStateRequest loadStateRequest = GetLoadStateRequest.newBuilder()
+            GetLoadStateRequest.Builder builder = GetLoadStateRequest.newBuilder()
                     .setCollectionName(requestParam.getCollectionName())
-                    .addAllPartitionNames(requestParam.getPartitionNames())
+                    .addAllPartitionNames(requestParam.getPartitionNames());
+            if (StringUtils.isNotEmpty(requestParam.getDatabaseName())) {
+                builder.setDbName(requestParam.getDatabaseName());
+            }
+
+            GetLoadStateRequest loadStateRequest = builder
                     .build();
 
             GetLoadStateResponse response = blockingStub().getLoadState(loadStateRequest);

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

@@ -73,6 +73,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 +145,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 +389,14 @@ public interface MilvusClient {
      */
     R<GetFlushStateResponse> getFlushState(GetFlushStateParam requestParam);
 
+    /**
+     * Get flushAll state of specified 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.

+ 37 - 0
src/main/java/io/milvus/client/MilvusMultiServiceClient.java

@@ -114,6 +114,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 +170,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 +388,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);

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

@@ -25,6 +25,8 @@ import io.milvus.grpc.MilvusServiceGrpc;
 import io.milvus.param.ConnectParam;
 
 import lombok.NonNull;
+import org.apache.commons.lang3.StringUtils;
+
 import java.util.concurrent.TimeUnit;
 
 public class MilvusServiceClient extends AbstractMilvusGrpcClient {
@@ -39,6 +41,9 @@ public class MilvusServiceClient extends AbstractMilvusGrpcClient {
 
         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()

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

@@ -6,7 +6,7 @@ public enum ConsistencyLevelEnum {
 
     STRONG("Strong", 0),
     // Session level is not allowed here because no ORM is implemented
-//    SESSION("Session", 1),
+    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);
+        }
+    }
+
+}

+ 44 - 15
src/main/java/io/milvus/param/ConnectParam.java

@@ -27,17 +27,15 @@ 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.
  */
 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;
@@ -50,6 +48,8 @@ public class ConnectParam {
     private 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;
@@ -101,6 +101,10 @@ public class ConnectParam {
         return authorization;
     }
 
+    public String getDatabaseName() {
+        return databaseName;
+    }
+
     public static Builder newBuilder() {
         return new Builder();
     }
@@ -111,7 +115,9 @@ public class ConnectParam {
     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;
@@ -147,6 +153,17 @@ 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
          *
@@ -158,6 +175,17 @@ public class ConnectParam {
             return this;
         }
 
+        /**
+         * Sets the token
+         *
+         * @param token
+         * @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.
          *
@@ -248,7 +276,7 @@ public class ConnectParam {
          * @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;
         }
@@ -281,16 +309,17 @@ public class ConnectParam {
         public ConnectParam build() 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;
                 }
             }
 

+ 157 - 5
src/main/java/io/milvus/param/ParamUtils.java

@@ -1,5 +1,7 @@
 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;
@@ -11,12 +13,19 @@ 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.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 /**
@@ -42,6 +51,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 +146,13 @@ public class ParamUtils {
                     }
                 }
                 break;
+            case JSON:
+                for (Object value : values) {
+                    if (!(value instanceof ByteString)) {
+                        throw new ParamException(String.format(errMsgs.get(dataType), fieldSchema.getName()));
+                    }
+                }
+                break;
             default:
                 throw new IllegalResponseException("Unsupported data type returned by FieldData");
         }
@@ -179,10 +199,9 @@ public class ParamUtils {
     }
 
     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();
@@ -190,9 +209,29 @@ public class ParamUtils {
                 .setCollectionName(collectionName)
                 .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) {
+        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
+        String partitionName = requestParam.getPartitionName();
         boolean isPartitionKeyEnabled = false;
         for (FieldType fieldType : fieldTypes) {
             if (fieldType.isPartitionKey()) {
@@ -223,15 +262,101 @@ public class ParamUtils {
         // set partition name only when there is no partition key field
         if (isPartitionKeyEnabled) {
             if (partitionName != null && !partitionName.isEmpty()) {
-                String msg = "Collection " + collectionName + " has partition key, not allow to specify partition name";
+                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);
         }
+    }
 
-        // 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 jsonField
+                if (fieldType.getDataType() == DataType.JSON) {
+                    JSONObject jsonField = parseJsonData(row, fieldType);
+                    insertDataInfo.getData().add(jsonField);
+                    nameInsertInfo.put(fieldName, insertDataInfo);
+                } else {
+                    // 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()) {
+                    Pair<String, String> outerJsonField = parseData(rowFieldName);
+                    if (!nameInsertInfo.containsKey(rowFieldName) && outerJsonField == null) {
+                        dynamicField.put(rowFieldName, row.get(rowFieldName));
+                    }
+                }
+                insertDynamicDataInfo.getData().add(dynamicField);
+            }
+
+            for (String rowFieldName : row.keySet()) {
+                Pair<String, String> outerJsonField = parseData(rowFieldName);
+                if (outerJsonField != null  && !nameInsertInfo.containsKey(outerJsonField.getKey())) {
+                    String msg = "The field: " + outerJsonField.getKey() + " not exists, no need to provided.";
+                    throw new ParamException(msg);
+                }
+            }
+        }
+
+        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));
+        }
+    }
+
+    private static JSONObject parseJsonData(JSONObject row, FieldType fieldType) {
+        JSONObject jsonField = new JSONObject();
+        for (String rowFieldName : row.keySet()) {
+            Pair<String, String> outerJsonField = parseData(rowFieldName);
+            if (outerJsonField != null && fieldType.getName().equals(outerJsonField.getKey())) {
+                jsonField.put(outerJsonField.getValue(), row.get(rowFieldName));
+            }
+        }
+        return jsonField;
+    }
+
+    public static Pair<String, String> parseData(String data) {
+        String pattern = "(\\w+)\\[\"(\\w+)\"\\]";
+        Pattern regex = Pattern.compile(pattern);
+        Matcher matcher = regex.matcher(data);
+        if (matcher.matches()) {
+            return Pair.of(matcher.group(1), matcher.group(2));
+        }
+        return null;
     }
 
     @SuppressWarnings("unchecked")
@@ -414,6 +539,11 @@ public class ParamUtils {
 
     @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");
         }
@@ -499,6 +629,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();
+                }
             }
         }
 
@@ -520,6 +660,10 @@ public class ParamUtils {
                 .withDataType(field.getDataType())
                 .withPartitionKey(field.getIsPartitionKey());
 
+        if (field.getIsDynamic()) {
+            builder.withIsDynamic(true);
+        }
+
         List<KeyValuePair> keyValuePairs = field.getTypeParamsList();
         keyValuePairs.forEach((kv) -> builder.addTypeParam(kv.getKey(), kv.getValue()));
 
@@ -545,4 +689,12 @@ public class ParamUtils {
 
         return builder.build();
     }
+
+    @Builder
+    @Getter
+    public static class InsertDataInfo {
+        private String fieldName;
+        private DataType dataType;
+        private LinkedList<Object> data;
+    }
 }

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

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

+ 32 - 17
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,6 +33,7 @@ import java.util.List;
  * Parameters for <code>createCollection</code> interface.
  */
 @Getter
+@ToString
 public class CreateCollectionParam {
     private final String collectionName;
     private final int shardsNum;
@@ -39,6 +41,9 @@ public class CreateCollectionParam {
     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;
@@ -47,6 +52,8 @@ public class CreateCollectionParam {
         this.fieldTypes = builder.fieldTypes;
         this.partitionsNum = builder.partitionsNum;
         this.consistencyLevel = builder.consistencyLevel;
+        this.databaseName = builder.databaseName;
+        this.enableDynamicField = builder.enableDynamicField;
     }
 
     public static Builder newBuilder() {
@@ -62,8 +69,10 @@ public class CreateCollectionParam {
         private String description = "";
         private final List<FieldType> fieldTypes = new ArrayList<>();
         private int partitionsNum = 0;
-        private ConsistencyLevelEnum consistencyLevel = ConsistencyLevelEnum.BOUNDED;
+        private ConsistencyLevelEnum consistencyLevel = ConsistencyLevelEnum.SESSION;
+        private String databaseName;
 
+        private boolean enableDynamicField;
         private Builder() {
         }
 
@@ -78,6 +87,17 @@ public class CreateCollectionParam {
             return this;
         }
 
+        /**
+         * 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 than zero. The default value is 2.
          *
@@ -89,6 +109,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 "".
          *
@@ -189,20 +220,4 @@ public class CreateCollectionParam {
             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 + '\'' + '}';
+    }
+}

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

@@ -42,6 +42,7 @@ public class FieldType {
     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;
@@ -51,6 +52,7 @@ public class FieldType {
         this.typeParams = builder.typeParams;
         this.autoID = builder.autoID;
         this.partitionKey = builder.partitionKey;
+        this.isDynamic = builder.isDynamic;
     }
 
     public int getDimension() {
@@ -84,6 +86,7 @@ public class FieldType {
         private final Map<String,String> typeParams = new HashMap<>();
         private boolean autoID = false;
         private boolean partitionKey = false;
+        private boolean isDynamic = false;
 
         private Builder() {
         }
@@ -93,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.

+ 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 +
+                '}';
+    }
+}

+ 69 - 25
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 = "";
         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.
          *
@@ -85,16 +107,21 @@ public class InsertParam {
 
         /**
          * Sets the data to insert. The field list cannot be empty.
-         * @see InsertParam.Field
          *
          * @param fields insert data
          * @return <code>Builder</code>
+         * @see InsertParam.Field
          */
         public Builder withFields(@NonNull List<InsertParam.Field> fields) {
             this.fields = fields;
             return this;
         }
 
+        public Builder withRows(@NonNull List<JSONObject> rows) {
+            this.rows = rows;
+            return this;
+        }
+
         /**
          * Verifies parameters and creates a new {@link InsertParam} instance.
          *
@@ -103,10 +130,33 @@ 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)) {
+                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." +
@@ -122,38 +172,31 @@ 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");
 
-    /**
-     * Constructs a <code>String</code> by {@link InsertParam} instance.
-     *
-     * @return <code>String</code>
-     */
-    @Override
-    public String toString() {
-        return "InsertParam{" +
-                "collectionName='" + collectionName + '\'' +
-                ", partitionName='" + partitionName + '\'' +
-                ", row_count=" + rowCount +
-                ", fields=" + fields +
-                '}';
+                    if (row.get(rowFieldName) == null) {
+                        throw new ParamException("Field value cannot be empty." +
+                                " If the field is auto-id, just ignore it from withRows()");
+                    }
+                }
+            }
+        }
     }
-
     /**
      * Internal class for insert data.
      * If dataType is Bool, values is List of Boolean;
@@ -169,6 +212,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;

+ 16 - 18
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,7 +37,9 @@ 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;
@@ -46,6 +49,7 @@ public class CreateIndexParam {
     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;
@@ -71,6 +75,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 +100,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 +263,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.
          *

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

@@ -20,6 +20,11 @@ public class DescCollResponseWrapper {
         this.response = response;
     }
 
+    public boolean getEnableDynamicField() {
+        CollectionSchema schema = response.getSchema();
+        return schema.getEnableDynamicField();
+    }
+
     /**
      * Get name of the collection.
      *

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

@@ -1,5 +1,6 @@
 package io.milvus.response;
 
+import com.alibaba.fastjson.JSONObject;
 import com.google.protobuf.ProtocolStringList;
 import io.milvus.grpc.DataType;
 import io.milvus.grpc.FieldData;
@@ -10,9 +11,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 +31,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 +94,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 +166,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) {
+        if (isDynamicField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Integer.parseInt(result);
+        }
+        throw new IllegalResponseException("The field is not dynamic and does not support this operation");
+    }
+
+    public String getAsString(int index, String paramName) {
+        if (isDynamicField()) {
+            JSONObject jsonObject = parseObjectData(index);
+            return jsonObject.getString(paramName);
+        }
+        throw new IllegalResponseException("The field is not dynamic and does not support this operation");
+    }
+
+    public Boolean getAsBool(int index, String paramName) {
+        if (isDynamicField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Boolean.parseBoolean(result);
+        }
+        throw new IllegalResponseException("The field is not dynamic and does not support this operation");
+    }
+
+    public Double getAsDouble(int index, String paramName) {
+        if (isDynamicField()) {
+            String result = getAsString(index, paramName);
+            return result == null ? null : Double.parseDouble(result);
+        }
+        throw new IllegalResponseException("The field is not dynamic and does not support this operation");
+    }
+
+    public Object get(int index, String paramName) throws Exception {
+        if (isDynamicField()) {
+            JSONObject jsonObject = parseObjectData(index);
+            return jsonObject.get(paramName);
+        }
+        throw new IllegalResponseException("The field is not dynamic and does not support this operation");
+    }
+
+    private Object valueByIdx(int index) {
+        if (index < 0 || index >= getFieldData().size()) {
+            throw new IllegalResponseException("index out of range");
+        }
+        return getFieldData().get(index);
+    }
+
+    private JSONObject parseObjectData(int index) {
+        Object object = valueByIdx(index);
+        return JSONObject.parseObject(new String((byte[])object));
+    }
 }

+ 11 - 0
src/main/java/io/milvus/response/QueryResultsWrapper.java

@@ -34,4 +34,15 @@ public class QueryResultsWrapper {
 
         throw new ParamException("The field name doesn't exist");
     }
+
+    public FieldDataWrapper getDynamicWrapper() throws ParamException {
+        List<FieldData> fields = results.getFieldsDataList();
+        for (FieldData field : fields) {
+            if (field.getIsDynamic()) {
+                return new FieldDataWrapper(field);
+            }
+        }
+
+        throw new ParamException("The dynamic field doesn't exist");
+    }
 }