Browse Source

Merge pull request #154 from milvus-io/0.9.2

0.9.2 to master
Xiaohai Xu 4 years ago
parent
commit
25677fdfd7
34 changed files with 2152 additions and 753 deletions
  1. 26 22
      CHANGELOG.md
  2. 7 6
      README.md
  3. 2 2
      examples/pom.xml
  4. 7 12
      examples/src/main/java/MilvusBasicExample.java
  5. 65 60
      examples/src/main/java/MilvusIndexExample.java
  6. 1 1
      pom.xml
  7. 43 42
      src/main/java/io/milvus/client/CollectionMapping.java
  8. 6 6
      src/main/java/io/milvus/client/CompactParam.java
  9. 22 5
      src/main/java/io/milvus/client/ConnectParam.java
  10. 6 8
      src/main/java/io/milvus/client/DataType.java
  11. 18 17
      src/main/java/io/milvus/client/Index.java
  12. 34 25
      src/main/java/io/milvus/client/InsertParam.java
  13. 50 29
      src/main/java/io/milvus/client/LoggingAdapter.java
  14. 38 41
      src/main/java/io/milvus/client/MilvusClient.java
  15. 299 197
      src/main/java/io/milvus/client/MilvusGrpcClient.java
  16. 64 49
      src/main/java/io/milvus/client/SearchParam.java
  17. 19 13
      src/main/java/io/milvus/client/SearchResult.java
  18. 45 0
      src/main/java/io/milvus/client/dsl/BoolQuery.java
  19. 30 0
      src/main/java/io/milvus/client/dsl/InsertParam.java
  20. 339 0
      src/main/java/io/milvus/client/dsl/MilvusService.java
  21. 33 0
      src/main/java/io/milvus/client/dsl/Query.java
  22. 71 0
      src/main/java/io/milvus/client/dsl/RangeQuery.java
  23. 139 0
      src/main/java/io/milvus/client/dsl/Schema.java
  24. 39 0
      src/main/java/io/milvus/client/dsl/TermQuery.java
  25. 117 0
      src/main/java/io/milvus/client/dsl/VectorQuery.java
  26. 1 1
      src/main/java/io/milvus/client/exception/InvalidDsl.java
  27. 2 2
      src/main/java/io/milvus/client/exception/MilvusException.java
  28. 2 2
      src/main/java/io/milvus/client/exception/ServerSideMilvusException.java
  29. 5 4
      src/main/java/io/milvus/client/exception/UnsupportedServerVersion.java
  30. 265 202
      src/test/java/io/milvus/client/MilvusGrpcClientTest.java
  31. 6 7
      src/test/java/io/milvus/client/StaticNameResolverProvider.java
  32. 333 0
      src/test/java/io/milvus/client/dsl/SearchDslTest.java
  33. 9 0
      src/test/java/io/milvus/client/dsl/TestBinarySchema.java
  34. 9 0
      src/test/java/io/milvus/client/dsl/TestFloatSchema.java

+ 26 - 22
CHANGELOG.md

@@ -1,43 +1,49 @@
 # Changelog   
 # Changelog   
 
 
-## milvus-sdk-java 0.9.0 (2020-10-16)
+## v0.9.1 (2020-10-29)
+
+### Bug
+
+- [\#4086](https://github.com/milvus-io/milvus/issues/4086) - Fix vector search error when topK is small
+
+## v0.9.0 (2020-10-16)
 
 
 ### Feature
 ### Feature
 
 
-- \#2976 Scalar-field filtering support
+- [\#2976](https://github.com/milvus-io/milvus/pull/2976) Scalar-field filtering support
 
 
 ### Improvement
 ### Improvement
 
 
-- \#134 - Simplify the client code
+- [\#134](https://github.com/milvus-io/milvus-sdk-java/pull/134) - Simplify the client code
 
 
-## milvus-sdk-java 0.8.5 (2020-08-26)
+## v0.8.5 (2020-08-26)
 
 
 ### Feature
 ### Feature
 
 
-- \#128 - GRPC timeout support
-- \#129 - Support GRPC name resolver and load balancing
+- [\#128](https://github.com/milvus-io/milvus-sdk-java/pull/128) - GRPC timeout support
+- [\#129](https://github.com/milvus-io/milvus-sdk-java/pull/129) - Support GRPC name resolver and load balancing
 
 
-## milvus-sdk-java 0.8.3 (2020-07-15)
+## v0.8.3 (2020-07-15)
 
 
 ### Improvement
 ### Improvement
 
 
-- \#118 - Remove isConnect() API
+- [\#118](https://github.com/milvus-io/milvus-sdk-java/pull/118) - Remove isConnect() API
 
 
-## milvus-sdk-java 0.8.0 (2020-05-15)
+## v0.8.0 (2020-05-15)
 
 
 ### Feature
 ### Feature
 
 
-- \#93 - Add/Improve getVectorByID, collectionInfo and hasPartition API
-- \#2295 - Rename SDK interfaces
+- [\#93](https://github.com/milvus-io/milvus-sdk-java/pull/93) - Add/Improve getVectorByID, collectionInfo and hasPartition API
+- [\#2295](https://github.com/milvus-io/milvus/issues/2295) - Rename SDK interfaces
 
 
-## milvus-sdk-java 0.7.0 (2020-04-15)
+## v0.7.0 (2020-04-15)
 
 
 ### Feature
 ### Feature
 
 
-- \#261 - Integrate ANNOY into Milvus
-- \#1828 - Add searchAsync / createIndexAsync / insertAsync / flushAsync / compactAsync API
+- [\#261](https://github.com/milvus-io/milvus/issues/261) - Integrate ANNOY into Milvus
+- [\#1828](https://github.com/milvus-io/milvus/issues/1828) - Add searchAsync / createIndexAsync / insertAsync / flushAsync / compactAsync API
 
 
-## milvus-sdk-java 0.6.0 (2020-03-31)
+## v0.6.0 (2020-03-31)
 
 
 ### Bug
 ### Bug
 
 
@@ -48,15 +54,13 @@
 
 
 - \#1603 - Add binary metrics: Substructure & Superstructure
 - \#1603 - Add binary metrics: Substructure & Superstructure
 
 
-## milvus-sdk-java 0.5.0 (2020-03-11)
-
-## milvus-sdk-java 0.4.1 (2019-12-16)
+## v0.4.1 (2019-12-16)
 
 
 ### Bug
 ### Bug
 
 
 - \#78 - Partition tag not working when searching
 - \#78 - Partition tag not working when searching
 
 
-## milvus-sdk-java 0.4.0 (2019-12-7)
+## v0.4.0 (2019-12-7)
 
 
 ### Bug
 ### Bug
 
 
@@ -69,7 +73,7 @@
 - \#72 - Add more getters in ShowPartitionResponse
 - \#72 - Add more getters in ShowPartitionResponse
 - \#73 - Add @Deprecated for DateRanges in SearchParam
 - \#73 - Add @Deprecated for DateRanges in SearchParam
 
 
-## milvus-sdk-java 0.3.0 (2019-11-13)
+## v0.3.0 (2019-11-13)
 
 
 ### Bug
 ### Bug
 
 
@@ -82,7 +86,7 @@
 - \#62 - Change GRPC proto (and related code) to increase search result's transmission speed
 - \#62 - Change GRPC proto (and related code) to increase search result's transmission speed
 - \#63 - Make some functions and constructors package-private if necessary
 - \#63 - Make some functions and constructors package-private if necessary
 
 
-## milvus-sdk-java 0.2.2 (2019-11-4)
+## v0.2.2 (2019-11-4)
 
 
 ### Improvement
 ### Improvement
 
 
@@ -90,7 +94,7 @@
 - \#51 - Change connect waitTime to timeout
 - \#51 - Change connect waitTime to timeout
 - \#52 - Change IVF_SQ8H to IVF_SQ8_H
 - \#52 - Change IVF_SQ8H to IVF_SQ8_H
 
 
-## milvus-sdk-java 0.2.0 (2019-10-21)
+## v0.2.0 (2019-10-21)
 
 
 ### Bug
 ### Bug
 
 

+ 7 - 6
README.md

@@ -8,15 +8,16 @@ Java SDK for [Milvus](https://github.com/milvus-io/milvus). To contribute to thi
 
 
 ### Prerequisites
 ### Prerequisites
 
 
-    -   Java 8 or higher
+    -   Java 9 or higher
     -   Apache Maven or Gradle/Grails
     -   Apache Maven or Gradle/Grails
 
 
 The following table shows compatibilities between Milvus and Java SDK.
 The following table shows compatibilities between Milvus and Java SDK.
 
 
 | Milvus version | Java SDK version |
 | Milvus version | Java SDK version |
 | :------------: | :--------------: |
 | :------------: | :--------------: |
+|     0.11.1     |    0.9.2         |
 |     0.11.0     |    0.9.1         |
 |     0.11.0     |    0.9.1         |
-|     0.10.3     |    0.8.5         |
+|     0.10.4<br>0.10.3     |    0.8.5         |
 |     0.10.2     |    0.8.4         |
 |     0.10.2     |    0.8.4         |
 |     0.10.1     |    0.8.3         |
 |     0.10.1     |    0.8.3         |
 |     0.10.0     |    0.8.2         |
 |     0.10.0     |    0.8.2         |
@@ -35,19 +36,19 @@ You can use **Apache Maven** or **Gradle**/**Grails** to download the SDK.
         <dependency>
         <dependency>
             <groupId>io.milvus</groupId>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>0.9.1</version>
+            <version>0.9.2</version>
         </dependency>
         </dependency>
        ```
        ```
 
 
    - Gradle/Grails
    - Gradle/Grails
 
 
         ```gradle
         ```gradle
-        compile 'io.milvus:milvus-sdk-java:0.9.1'
+        compile group: 'io.milvus', name: 'milvus-sdk-java', version: '0.9.2'
         ```
         ```
 
 
 ### Examples
 ### Examples
 
 
-Please refer to [examples](https://github.com/milvus-io/milvus-sdk-java/tree/0.9.1/examples) folder for Java SDK examples.
+Please refer to [examples](https://github.com/milvus-io/milvus-sdk-java/tree/0.9.2/examples) folder for Java SDK examples.
 
 
 ### Documentation
 ### Documentation
 
 
@@ -63,7 +64,7 @@ Please refer to [examples](https://github.com/milvus-io/milvus-sdk-java/tree/0.9
     ```
     ```
   This is because SLF4J jar files need to be added into your application's classpath. SLF4J is used by Java SDK for logging purpose.
   This is because SLF4J jar files need to be added into your application's classpath. SLF4J is used by Java SDK for logging purpose.
   
   
-  To fix this issue, you can use **Apache Maven** or **Gradle**/**Grails** to download the required jar files.
+  To fix this issue, you can download the required jar files.
                                                                                                          
                                                                                                          
     - Apache Maven
     - Apache Maven
     
     

+ 2 - 2
examples/pom.xml

@@ -25,7 +25,7 @@
 
 
     <groupId>io.milvus</groupId>
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java-examples</artifactId>
     <artifactId>milvus-sdk-java-examples</artifactId>
-    <version>0.9.1</version>
+    <version>0.9.2</version>
     <build>
     <build>
         <plugins>
         <plugins>
             <plugin>
             <plugin>
@@ -63,7 +63,7 @@
         <dependency>
         <dependency>
             <groupId>io.milvus</groupId>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>0.9.1</version>
+            <version>0.9.2-SNAPSHOT</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <groupId>org.slf4j</groupId>

+ 7 - 12
examples/src/main/java/MilvusBasicExample.java

@@ -19,15 +19,7 @@
 
 
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import io.milvus.client.CollectionMapping;
-import io.milvus.client.CompactParam;
-import io.milvus.client.ConnectParam;
-import io.milvus.client.DataType;
-import io.milvus.client.InsertParam;
-import io.milvus.client.MilvusClient;
-import io.milvus.client.MilvusGrpcClient;
-import io.milvus.client.SearchParam;
-import io.milvus.client.SearchResult;
+import io.milvus.client.*;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.List;
 import java.util.List;
@@ -39,7 +31,7 @@ import java.util.stream.DoubleStream;
 import org.json.JSONObject;
 import org.json.JSONObject;
 
 
 /**
 /**
- * This is a simple example demonstrating how to use Milvus Java SDK v0.9.1. For detailed API
+ * This is a simple example demonstrating how to use Milvus Java SDK v0.9.2. For detailed API
  * documentation, please refer to
  * documentation, please refer to
  * https://milvus-io.github.io/milvus-sdk-java/javadoc/io/milvus/client/package-summary.html You can
  * https://milvus-io.github.io/milvus-sdk-java/javadoc/io/milvus/client/package-summary.html You can
  * also find more information on https://milvus.io/docs/overview.md
  * also find more information on https://milvus.io/docs/overview.md
@@ -77,8 +69,11 @@ public class MilvusBasicExample {
      *
      *
      *   You can use `withLogging()` for `client` to enable logging framework.
      *   You can use `withLogging()` for `client` to enable logging framework.
      */
      */
-    ConnectParam connectParam =
-        new ConnectParam.Builder().withHost("127.0.0.1").withPort(19530).build();
+    ConnectParam connectParam = new ConnectParam.Builder()
+        .withHost("127.0.0.1")
+        .withPort(19530)
+        .withClientTag("films_client")
+        .build();
     MilvusClient client = new MilvusGrpcClient(connectParam);
     MilvusClient client = new MilvusGrpcClient(connectParam);
 
 
     /*
     /*

+ 65 - 60
examples/src/main/java/MilvusIndexExample.java

@@ -17,18 +17,10 @@
  * under the License.
  * under the License.
  */
  */
 
 
-import io.milvus.client.CollectionMapping;
-import io.milvus.client.ConnectParam;
-import io.milvus.client.DataType;
-import io.milvus.client.Index;
-import io.milvus.client.IndexType;
-import io.milvus.client.InsertParam;
-import io.milvus.client.JsonBuilder;
-import io.milvus.client.MetricType;
-import io.milvus.client.MilvusClient;
-import io.milvus.client.MilvusGrpcClient;
-import io.milvus.client.SearchParam;
-import io.milvus.client.SearchResult;
+import io.milvus.client.*;
+import io.milvus.client.dsl.MilvusService;
+import io.milvus.client.dsl.Query;
+import io.milvus.client.dsl.Schema;
 import java.io.BufferedReader;
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.IOException;
@@ -41,7 +33,7 @@ import java.util.stream.DoubleStream;
 import org.json.JSONObject;
 import org.json.JSONObject;
 
 
 /**
 /**
- * This is an example of Milvus Java SDK v0.9.1. In particular, we demonstrate how we can build and
+ * This is an example of Milvus Java SDK v0.9.2. In particular, we demonstrate how we can build and
  * search by index in Milvus.
  * search by index in Milvus.
  *
  *
  * <p>We will be using `films.csv` as our dataset. There are 4 columns in the file, namely `id`,
  * <p>We will be using `films.csv` as our dataset. There are 4 columns in the file, namely `id`,
@@ -54,6 +46,8 @@ import org.json.JSONObject;
  */
  */
 public class MilvusIndexExample {
 public class MilvusIndexExample {
 
 
+  public static final int dimension = 8;
+
   // Helper function that generates random float vectors
   // Helper function that generates random float vectors
   private static List<List<Float>> randomFloatVectors(int vectorCount, int dimension) {
   private static List<List<Float>> randomFloatVectors(int vectorCount, int dimension) {
     SplittableRandom splitCollectionRandom = new SplittableRandom();
     SplittableRandom splitCollectionRandom = new SplittableRandom();
@@ -82,20 +76,22 @@ public class MilvusIndexExample {
     ConnectParam connectParam = new ConnectParam.Builder().build();
     ConnectParam connectParam = new ConnectParam.Builder().build();
     MilvusClient client = new MilvusGrpcClient(connectParam);
     MilvusClient client = new MilvusGrpcClient(connectParam);
 
 
+    /*
+     * Basic create collection:
+     *   Another way to create a collection is to predefine a schema. Then we can use a
+     *   MilvusService to wrap the schema, collection name and Milvus client. The service
+     *   is intended to simplify API calls.
+     */
     final String collectionName = "demo_index";
     final String collectionName = "demo_index";
-    if (client.listCollections().contains(collectionName)) {
-      client.dropCollection(collectionName);
-    }
+    FilmSchema filmSchema = new FilmSchema();
+    MilvusService service = new MilvusService(client, collectionName, filmSchema);
 
 
-    // Create collection
-    final int dimension = 8;
-    CollectionMapping collectionMapping =
-        CollectionMapping.create(collectionName)
-            .addField("release_year", DataType.INT64)
-            .addVectorField("embedding", DataType.VECTOR_FLOAT, dimension)
-            .setParamsInJson("{\"segment_row_limit\": 4096, \"auto_id\": false}");
+    if (service.listCollections().contains(collectionName)) {
+      service.dropCollection();
+    }
 
 
-    client.createCollection(collectionMapping);
+    service.createCollection(
+        new JsonBuilder().param("auto_id", false).param("segment_row_limit", 4096).build());
 
 
     /*
     /*
      * Basic insert and create index:
      * Basic insert and create index:
@@ -110,7 +106,7 @@ public class MilvusIndexExample {
     BufferedReader csvReader = new BufferedReader(new FileReader(path));
     BufferedReader csvReader = new BufferedReader(new FileReader(path));
     List<Long> ids = new ArrayList<>();
     List<Long> ids = new ArrayList<>();
     List<String> titles = new ArrayList<>();
     List<String> titles = new ArrayList<>();
-    List<Long> releaseYears = new ArrayList<>();
+    List<Integer> releaseYears = new ArrayList<>();
     List<List<Float>> embeddings = new ArrayList<>();
     List<List<Float>> embeddings = new ArrayList<>();
     String row;
     String row;
     while ((row = csvReader.readLine()) != null) {
     while ((row = csvReader.readLine()) != null) {
@@ -118,7 +114,7 @@ public class MilvusIndexExample {
       // process four columns in order
       // process four columns in order
       ids.add(Long.parseLong(data[0]));
       ids.add(Long.parseLong(data[0]));
       titles.add(data[1]);
       titles.add(data[1]);
-      releaseYears.add(Long.parseLong(data[2]));
+      releaseYears.add(Integer.parseInt(data[2]));
       List<Float> embedding = new ArrayList<>(dimension);
       List<Float> embedding = new ArrayList<>(dimension);
       for (int i = 3; i < 11; i++) {
       for (int i = 3; i < 11; i++) {
         // 8 float values in a vector
         // 8 float values in a vector
@@ -135,16 +131,14 @@ public class MilvusIndexExample {
     csvReader.close();
     csvReader.close();
 
 
     // Now we can insert entities, the total row count should be 8657.
     // Now we can insert entities, the total row count should be 8657.
-    InsertParam insertParam =
-        InsertParam.create(collectionName)
-            .addField("release_year", DataType.INT64, releaseYears)
-            .addVectorField("embedding", DataType.VECTOR_FLOAT, embeddings)
-            .setEntityIds(ids);
-
-    client.insert(insertParam);
-    client.flush(collectionName);
-    System.out.printf(
-        "There are %d films in the collection.\n", client.countEntities(collectionName));
+    service.insert(
+        insertParam ->
+            insertParam
+                .withIds(ids)
+                .with(filmSchema.releaseYear, releaseYears)
+                .with(filmSchema.embedding, embeddings));
+    service.flush();
+    System.out.printf("There are %d films in the collection.\n", service.countEntities());
 
 
     /*
     /*
      * Basic create index:
      * Basic create index:
@@ -155,18 +149,27 @@ public class MilvusIndexExample {
      *
      *
      *   Note that if there is already an index and create index is called again, the previous index
      *   Note that if there is already an index and create index is called again, the previous index
      *   will be replaced.
      *   will be replaced.
+     *
+     *   We present two ways to create an index: using Index or MilvusService.
      */
      */
+    // Approach one
     Index index =
     Index index =
         Index.create(collectionName, "embedding")
         Index.create(collectionName, "embedding")
             .setIndexType(IndexType.IVF_FLAT)
             .setIndexType(IndexType.IVF_FLAT)
             .setMetricType(MetricType.L2)
             .setMetricType(MetricType.L2)
             .setParamsInJson(new JsonBuilder().param("nlist", 100).build());
             .setParamsInJson(new JsonBuilder().param("nlist", 100).build());
-
     client.createIndex(index);
     client.createIndex(index);
 
 
+    // Approach two
+    service.createIndex(
+        filmSchema.embedding,
+        IndexType.IVF_FLAT,
+        MetricType.L2,
+        new JsonBuilder().param("nlist", 100).build());
+
     // Get collection stats with index
     // Get collection stats with index
     System.out.println("\n--------Collection Stats--------");
     System.out.println("\n--------Collection Stats--------");
-    JSONObject json = new JSONObject(client.getCollectionStats(collectionName));
+    JSONObject json = new JSONObject(service.getCollectionStats());
     System.out.println(json.toString(4));
     System.out.println(json.toString(4));
 
 
     /*
     /*
@@ -176,29 +179,25 @@ public class MilvusIndexExample {
      *
      *
      *   Based on the index you created, the available search parameters will be different. Refer to
      *   Based on the index you created, the available search parameters will be different. Refer to
      *   Milvus documentation for how to set the optimal parameters based on your needs.
      *   Milvus documentation for how to set the optimal parameters based on your needs.
+     *
+     *   Here we present a way to use predefined schema to search vectors.
      */
      */
     List<List<Float>> queryEmbedding = randomFloatVectors(1, dimension);
     List<List<Float>> queryEmbedding = randomFloatVectors(1, dimension);
-    final long topK = 3;
-    String dsl =
-        String.format(
-            "{\"bool\": {"
-                + "\"must\": [{"
-                + "    \"term\": {"
-                + "        \"release_year\": [2002, 1995]"
-                + "    }},{"
-                + "    \"vector\": {"
-                + "        \"embedding\": {"
-                + "            \"topk\": %d, \"metric_type\": \"L2\", \"type\": \"float\", \"query\": "
-                + "%s, \"params\": {\"nprobe\": 8}"
-                + "    }}}]}}",
-            topK, queryEmbedding.toString());
-
+    final int topK = 3;
+    Query query =
+        Query.bool(
+            Query.must(
+                filmSchema.releaseYear.in(1995, 2002),
+                filmSchema.embedding.query(queryEmbedding)
+                    .metricType(MetricType.L2)
+                    .top(topK)
+                    .param("nprobe", 8)));
     SearchParam searchParam =
     SearchParam searchParam =
-        SearchParam.create(collectionName)
-            .setDsl(dsl)
+        service
+            .buildSearchParam(query)
             .setParamsInJson("{\"fields\": [\"release_year\", \"embedding\"]}");
             .setParamsInJson("{\"fields\": [\"release_year\", \"embedding\"]}");
     System.out.println("\n--------Search Result--------");
     System.out.println("\n--------Search Result--------");
-    SearchResult searchResult = client.search(searchParam);
+    SearchResult searchResult = service.search(searchParam);
     System.out.println("- ids: " + searchResult.getResultIdsList().toString());
     System.out.println("- ids: " + searchResult.getResultIdsList().toString());
     System.out.println("- distances: " + searchResult.getResultDistancesList().toString());
     System.out.println("- distances: " + searchResult.getResultDistancesList().toString());
     for (List<Map<String, Object>> singleQueryResult : searchResult.getFieldsMap()) {
     for (List<Map<String, Object>> singleQueryResult : searchResult.getFieldsMap()) {
@@ -217,13 +216,19 @@ public class MilvusIndexExample {
      * Basic delete index:
      * Basic delete index:
      *   Index can be dropped for a vector field.
      *   Index can be dropped for a vector field.
      */
      */
-    client.dropIndex(collectionName, "embedding");
+    service.dropIndex(filmSchema.embedding);
 
 
-    if (client.listCollections().contains(collectionName)) {
-      client.dropCollection(collectionName);
+    if (service.listCollections().contains(collectionName)) {
+      service.dropCollection();
     }
     }
 
 
     // Close connection
     // Close connection
-    client.close();
+    service.close();
+  }
+
+  // Schema that defines a collection
+  private static class FilmSchema extends Schema {
+    public final Int32Field releaseYear = new Int32Field("release_year");
+    public final FloatVectorField embedding = new FloatVectorField("embedding", dimension);
   }
   }
 }
 }

+ 1 - 1
pom.xml

@@ -25,7 +25,7 @@
 
 
     <groupId>io.milvus</groupId>
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java</artifactId>
     <artifactId>milvus-sdk-java</artifactId>
-    <version>0.9.1</version>
+    <version>0.9.2-SNAPSHOT</version>
     <packaging>jar</packaging>
     <packaging>jar</packaging>
 
 
     <name>io.milvus:milvus-sdk-java</name>
     <name>io.milvus:milvus-sdk-java</name>

+ 43 - 42
src/main/java/io/milvus/client/CollectionMapping.java

@@ -23,23 +23,15 @@ import com.google.common.collect.ImmutableMap;
 import io.milvus.grpc.FieldParam;
 import io.milvus.grpc.FieldParam;
 import io.milvus.grpc.KeyValuePair;
 import io.milvus.grpc.KeyValuePair;
 import io.milvus.grpc.Mapping;
 import io.milvus.grpc.Mapping;
-import org.json.JSONObject;
-
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
+import org.json.JSONObject;
 
 
 /** Represents a collection mapping */
 /** Represents a collection mapping */
 public class CollectionMapping {
 public class CollectionMapping {
   private final Mapping.Builder builder;
   private final Mapping.Builder builder;
 
 
-  /**
-   * @param collectionName collection name
-   */
-  public static CollectionMapping create(String collectionName) {
-    return new CollectionMapping(collectionName);
-  }
-
   CollectionMapping(Mapping mapping) {
   CollectionMapping(Mapping mapping) {
     this.builder = mapping.toBuilder();
     this.builder = mapping.toBuilder();
   }
   }
@@ -49,8 +41,13 @@ public class CollectionMapping {
     builder.setCollectionName(collectionName);
     builder.setCollectionName(collectionName);
   }
   }
 
 
+  /** @param collectionName collection name */
+  public static CollectionMapping create(String collectionName) {
+    return new CollectionMapping(collectionName);
+  }
+
   /**
   /**
-   * add a scalar field
+   * Add a scalar field to the collection schema.
    *
    *
    * @param name the field name
    * @param name the field name
    * @param type the field data type
    * @param type the field data type
@@ -62,7 +59,7 @@ public class CollectionMapping {
   }
   }
 
 
   /**
   /**
-   * add a vector field
+   * Add a vector field to the collection schema.
    *
    *
    * @param name the field name
    * @param name the field name
    * @param type the field data type
    * @param type the field data type
@@ -70,55 +67,59 @@ public class CollectionMapping {
    * @return this CollectionMapping
    * @return this CollectionMapping
    */
    */
   public CollectionMapping addVectorField(String name, DataType type, int dimension) {
   public CollectionMapping addVectorField(String name, DataType type, int dimension) {
-    FieldParam field = FieldParam.newBuilder()
-        .setName(name)
-        .setTypeValue(type.getVal())
-        .addExtraParams(KeyValuePair.newBuilder()
-            .setKey(MilvusClient.extraParamKey)
-            .setValue(new JSONObject().put("dim", dimension).toString())
-            .build())
-        .build();
+    FieldParam field =
+        FieldParam.newBuilder()
+            .setName(name)
+            .setTypeValue(type.getVal())
+            .addExtraParams(
+                KeyValuePair.newBuilder()
+                    .setKey(MilvusClient.extraParamKey)
+                    .setValue(new JSONObject().put("dim", dimension).toString())
+                    .build())
+            .build();
     builder.addFields(field);
     builder.addFields(field);
     return this;
     return this;
   }
   }
 
 
   public List<Map<String, Object>> getFields() {
   public List<Map<String, Object>> getFields() {
     return builder.getFieldsList().stream()
     return builder.getFieldsList().stream()
-        .map(f -> {
-          ImmutableMap.Builder<String, Object> builder = ImmutableMap
-              .<String, Object>builder()
-              .put("name", f.getName())
-              .put("type", DataType.valueOf(f.getType().getNumber()));
-          String paramsInJson = getParamsInJson(f.getExtraParamsList());
-          if (paramsInJson != null) {
-            builder.put(MilvusClient.extraParamKey, paramsInJson);
-          }
-          return builder.build();
-        })
+        .map(
+            f -> {
+              ImmutableMap.Builder<String, Object> builder =
+                  ImmutableMap.<String, Object>builder()
+                      .put("name", f.getName())
+                      .put("type", DataType.valueOf(f.getType().getNumber()));
+              String paramsInJson = getParamsInJson(f.getExtraParamsList());
+              if (paramsInJson != null) {
+                builder.put(MilvusClient.extraParamKey, paramsInJson);
+              }
+              return builder.build();
+            })
         .collect(Collectors.toList());
         .collect(Collectors.toList());
   }
   }
 
 
+  public String getParamsInJson() {
+    return getParamsInJson(builder.getExtraParamsList());
+  }
+
   /**
   /**
    * Set extra params in json string
    * Set extra params in json string
    *
    *
-   * @param paramsInJson Two optional parameters can be included. "segment_row_limit" is default
-   *                     to 100,000. Merge will be triggered if more than this number of entities
-   *                     are inserted into collection. "auto_id" is default to <code>true</code>.
-   *                     Entity ids will be auto-generated by Milvus if set to true.
+   * @param paramsInJson Two optional parameters can be included. "segment_row_limit" is default to
+   *     524,288. Merge will be triggered if more than this number of entities are inserted into the
+   *     collection. "auto_id" is default to <code>true</code>. Entity ids will be auto-generated by
+   *     Milvus if set to true.
    * @return this CollectionMapping
    * @return this CollectionMapping
    */
    */
   public CollectionMapping setParamsInJson(String paramsInJson) {
   public CollectionMapping setParamsInJson(String paramsInJson) {
-    builder.addExtraParams(KeyValuePair.newBuilder()
-        .setKey(MilvusClient.extraParamKey)
-        .setValue(paramsInJson)
-        .build());
+    builder.addExtraParams(
+        KeyValuePair.newBuilder()
+            .setKey(MilvusClient.extraParamKey)
+            .setValue(paramsInJson)
+            .build());
     return this;
     return this;
   }
   }
 
 
-  public String getParamsInJson() {
-    return getParamsInJson(builder.getExtraParamsList());
-  }
-
   public String getCollectionName() {
   public String getCollectionName() {
     return builder.getCollectionName();
     return builder.getCollectionName();
   }
   }

+ 6 - 6
src/main/java/io/milvus/client/CompactParam.java

@@ -21,18 +21,18 @@ package io.milvus.client;
 
 
 /** Contains parameters for <code>compact</code> */
 /** Contains parameters for <code>compact</code> */
 public class CompactParam {
 public class CompactParam {
-  private io.milvus.grpc.CompactParam.Builder builder;
-
-  /** @param collectionName collection to compact */
-  public static CompactParam create(String collectionName) {
-    return new CompactParam(collectionName);
-  }
+  private final io.milvus.grpc.CompactParam.Builder builder;
 
 
   private CompactParam(String collectionName) {
   private CompactParam(String collectionName) {
     builder = io.milvus.grpc.CompactParam.newBuilder();
     builder = io.milvus.grpc.CompactParam.newBuilder();
     builder.setCollectionName(collectionName).setThreshold(0.2);
     builder.setCollectionName(collectionName).setThreshold(0.2);
   }
   }
 
 
+  /** @param collectionName collection to compact */
+  public static CompactParam create(String collectionName) {
+    return new CompactParam(collectionName);
+  }
+
   /**
   /**
    * Optional. Default to 0.2. Segment will compact if and only if the percentage of entities
    * Optional. Default to 0.2. Segment will compact if and only if the percentage of entities
    * deleted exceeds the threshold.
    * deleted exceeds the threshold.

+ 22 - 5
src/main/java/io/milvus/client/ConnectParam.java

@@ -20,14 +20,14 @@
 package io.milvus.client;
 package io.milvus.client;
 
 
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
-
-import javax.annotation.Nonnull;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
 
 
 /** Contains parameters for connecting to Milvus server */
 /** Contains parameters for connecting to Milvus server */
 public class ConnectParam {
 public class ConnectParam {
   private final String target;
   private final String target;
   private final String defaultLoadBalancingPolicy;
   private final String defaultLoadBalancingPolicy;
+  private final String clientTag;
   private final long connectTimeoutNanos;
   private final long connectTimeoutNanos;
   private final long keepAliveTimeNanos;
   private final long keepAliveTimeNanos;
   private final long keepAliveTimeoutNanos;
   private final long keepAliveTimeoutNanos;
@@ -35,8 +35,12 @@ public class ConnectParam {
   private final long idleTimeoutNanos;
   private final long idleTimeoutNanos;
 
 
   private ConnectParam(@Nonnull Builder builder) {
   private ConnectParam(@Nonnull Builder builder) {
-    this.target = builder.target != null ? builder.target : String.format("dns:///%s:%d", builder.host, builder.port);
+    this.target =
+        builder.target != null
+            ? builder.target
+            : String.format("dns:///%s:%d", builder.host, builder.port);
     this.defaultLoadBalancingPolicy = builder.defaultLoadBalancingPolicy;
     this.defaultLoadBalancingPolicy = builder.defaultLoadBalancingPolicy;
+    this.clientTag = builder.clientTag;
     this.connectTimeoutNanos = builder.connectTimeoutNanos;
     this.connectTimeoutNanos = builder.connectTimeoutNanos;
     this.keepAliveTimeNanos = builder.keepAliveTimeNanos;
     this.keepAliveTimeNanos = builder.keepAliveTimeNanos;
     this.keepAliveTimeoutNanos = builder.keepAliveTimeoutNanos;
     this.keepAliveTimeoutNanos = builder.keepAliveTimeoutNanos;
@@ -52,6 +56,8 @@ public class ConnectParam {
     return defaultLoadBalancingPolicy;
     return defaultLoadBalancingPolicy;
   }
   }
 
 
+  public String getClientTag() { return clientTag; }
+
   public long getConnectTimeout(@Nonnull TimeUnit timeUnit) {
   public long getConnectTimeout(@Nonnull TimeUnit timeUnit) {
     return timeUnit.convert(connectTimeoutNanos, TimeUnit.NANOSECONDS);
     return timeUnit.convert(connectTimeoutNanos, TimeUnit.NANOSECONDS);
   }
   }
@@ -79,6 +85,7 @@ public class ConnectParam {
     private String host = "localhost";
     private String host = "localhost";
     private int port = 19530;
     private int port = 19530;
     private String defaultLoadBalancingPolicy = "round_robin";
     private String defaultLoadBalancingPolicy = "round_robin";
+    private String clientTag = "";
     private long connectTimeoutNanos = TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS);
     private long connectTimeoutNanos = TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS);
     private long keepAliveTimeNanos = Long.MAX_VALUE; // Disabling keepalive
     private long keepAliveTimeNanos = Long.MAX_VALUE; // Disabling keepalive
     private long keepAliveTimeoutNanos = TimeUnit.NANOSECONDS.convert(20, TimeUnit.SECONDS);
     private long keepAliveTimeoutNanos = TimeUnit.NANOSECONDS.convert(20, TimeUnit.SECONDS);
@@ -90,7 +97,6 @@ public class ConnectParam {
      *
      *
      * @param target a GRPC target string
      * @param target a GRPC target string
      * @return <code>Builder</code>
      * @return <code>Builder</code>
-     *
      * @see ManagedChannelBuilder#forTarget(String)
      * @see ManagedChannelBuilder#forTarget(String)
      */
      */
     public Builder withTarget(@Nonnull String target) {
     public Builder withTarget(@Nonnull String target) {
@@ -134,6 +140,17 @@ public class ConnectParam {
       return this;
       return this;
     }
     }
 
 
+    /**
+     * Optional. Defaults to empty string.
+     *
+     * @param clientTag the client tag to be passed to server
+     * @return <code>Builder</code>
+     */
+    public Builder withClientTag(String clientTag) {
+      this.clientTag = clientTag;
+      return this;
+    }
+
     /**
     /**
      * Optional. Defaults to 10 seconds.
      * Optional. Defaults to 10 seconds.
      *
      *
@@ -175,7 +192,7 @@ public class ConnectParam {
      * expires without any read activity on the connection, the connection is considered dead. An
      * expires without any read activity on the connection, the connection is considered dead. An
      * unreasonably small value might be increased. Defaults to 20 seconds.
      * unreasonably small value might be increased. Defaults to 20 seconds.
      *
      *
-     * <p>This value should be at least multiple times the RTT to allow for lost packets.</p>
+     * <p>This value should be at least multiple times the RTT to allow for lost packets.
      *
      *
      * @see <a
      * @see <a
      *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveTimeout-long-java.util.concurrent.TimeUnit-">
      *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveTimeout-long-java.util.concurrent.TimeUnit-">

+ 6 - 8
src/main/java/io/milvus/client/DataType.java

@@ -22,21 +22,19 @@ package io.milvus.client;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Optional;
 import java.util.Optional;
 
 
-/**
- * Represents available data types.
- */
+/** Represents available data types. */
 public enum DataType {
 public enum DataType {
-  NONE(0),
-  BOOL(1),
-  INT8(2),
-  INT16(3),
+  // NONE(0),
+  // BOOL(1),
+  // INT8(2),
+  // INT16(3),
   INT32(4),
   INT32(4),
   INT64(5),
   INT64(5),
 
 
   FLOAT(10),
   FLOAT(10),
   DOUBLE(11),
   DOUBLE(11),
 
 
-  STRING(20),
+  // STRING(20),
 
 
   VECTOR_BINARY(100),
   VECTOR_BINARY(100),
   VECTOR_FLOAT(101),
   VECTOR_FLOAT(101),

+ 18 - 17
src/main/java/io/milvus/client/Index.java

@@ -21,18 +21,23 @@ package io.milvus.client;
 
 
 import io.milvus.grpc.IndexParam;
 import io.milvus.grpc.IndexParam;
 import io.milvus.grpc.KeyValuePair;
 import io.milvus.grpc.KeyValuePair;
-
-import javax.annotation.Nonnull;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
 
 
-/** Represents an index containing <code>fieldName</code>, <code>indexName</code> and
- * <code>paramsInJson</code>, which contains index_type, params etc.
+/**
+ * Represents an index containing <code>fieldName</code>, <code>indexName</code> and <code>
+ * paramsInJson</code>, which contains <code>index_type</code>, params etc.
  */
  */
 public class Index {
 public class Index {
   private final IndexParam.Builder builder;
   private final IndexParam.Builder builder;
 
 
+  private Index(String collectionName, String fieldName) {
+    this.builder =
+        IndexParam.newBuilder().setCollectionName(collectionName).setFieldName(fieldName);
+  }
+
   /**
   /**
    * @param collectionName collection to create index for
    * @param collectionName collection to create index for
    * @param fieldName name of the field on which index is built.
    * @param fieldName name of the field on which index is built.
@@ -41,16 +46,11 @@ public class Index {
     return new Index(collectionName, fieldName);
     return new Index(collectionName, fieldName);
   }
   }
 
 
-  private Index(String collectionName, String fieldName) {
-    this.builder = IndexParam.newBuilder()
-        .setCollectionName(collectionName)
-        .setFieldName(fieldName);
-  }
-
   public String getCollectionName() {
   public String getCollectionName() {
     return builder.getCollectionName();
     return builder.getCollectionName();
   }
   }
 
 
+  /** @param collectionName The collection name */
   public Index setCollectionName(@Nonnull String collectionName) {
   public Index setCollectionName(@Nonnull String collectionName) {
     builder.setCollectionName(collectionName);
     builder.setCollectionName(collectionName);
     return this;
     return this;
@@ -60,8 +60,9 @@ public class Index {
     return builder.getFieldName();
     return builder.getFieldName();
   }
   }
 
 
-  public Index setFieldName(@Nonnull String collectionName) {
-    builder.setFieldName(collectionName);
+  /** @param fieldName The field name */
+  public Index setFieldName(@Nonnull String fieldName) {
+    builder.setFieldName(fieldName);
     return this;
     return this;
   }
   }
 
 
@@ -73,10 +74,12 @@ public class Index {
     return toMap(builder.getExtraParamsList());
     return toMap(builder.getExtraParamsList());
   }
   }
 
 
+  /** @param indexType The index type */
   public Index setIndexType(IndexType indexType) {
   public Index setIndexType(IndexType indexType) {
     return addParam("index_type", indexType.name());
     return addParam("index_type", indexType.name());
   }
   }
 
 
+  /** @param metricType The metric type */
   public Index setMetricType(MetricType metricType) {
   public Index setMetricType(MetricType metricType) {
     return addParam("metric_type", metricType.name());
     return addParam("metric_type", metricType.name());
   }
   }
@@ -88,10 +91,7 @@ public class Index {
 
 
   private Index addParam(String key, Object value) {
   private Index addParam(String key, Object value) {
     builder.addExtraParams(
     builder.addExtraParams(
-        KeyValuePair.newBuilder()
-            .setKey(key)
-            .setValue(String.valueOf(value))
-            .build());
+        KeyValuePair.newBuilder().setKey(key).setValue(String.valueOf(value)).build());
     return this;
     return this;
   }
   }
 
 
@@ -112,6 +112,7 @@ public class Index {
   }
   }
 
 
   private Map<String, String> toMap(List<KeyValuePair> extraParams) {
   private Map<String, String> toMap(List<KeyValuePair> extraParams) {
-    return extraParams.stream().collect(Collectors.toMap(KeyValuePair::getKey, KeyValuePair::getValue));
+    return extraParams.stream()
+        .collect(Collectors.toMap(KeyValuePair::getKey, KeyValuePair::getValue));
   }
   }
 }
 }

+ 34 - 25
src/main/java/io/milvus/client/InsertParam.java

@@ -25,28 +25,27 @@ import io.milvus.grpc.AttrRecord;
 import io.milvus.grpc.FieldValue;
 import io.milvus.grpc.FieldValue;
 import io.milvus.grpc.VectorRecord;
 import io.milvus.grpc.VectorRecord;
 import io.milvus.grpc.VectorRowRecord;
 import io.milvus.grpc.VectorRowRecord;
-
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 /** Contains parameters for <code>insert</code> */
 /** Contains parameters for <code>insert</code> */
 public class InsertParam {
 public class InsertParam {
-  private io.milvus.grpc.InsertParam.Builder builder;
-
-  /** @param collectionName collection to insert entities to */
-  public static InsertParam create(String collectionName) {
-    return new InsertParam(collectionName);
-  }
+  private final io.milvus.grpc.InsertParam.Builder builder;
 
 
   private InsertParam(String collectionName) {
   private InsertParam(String collectionName) {
     this.builder = io.milvus.grpc.InsertParam.newBuilder();
     this.builder = io.milvus.grpc.InsertParam.newBuilder();
     builder.setCollectionName(collectionName);
     builder.setCollectionName(collectionName);
   }
   }
 
 
+  /** @param collectionName collection to insert entities to */
+  public static InsertParam create(String collectionName) {
+    return new InsertParam(collectionName);
+  }
+
   /**
   /**
-   * Optional. Only needed when entity ids are not auto-generated by milvus.
-   * This is specified when creating collection.
+   * Optional. Only needed when entity ids are not auto-generated by milvus. This is specified when
+   * creating collection.
    *
    *
    * @param entityIds a <code>List</code> of ids associated with the entities to insert.
    * @param entityIds a <code>List</code> of ids associated with the entities to insert.
    */
    */
@@ -57,6 +56,7 @@ public class InsertParam {
 
 
   /**
   /**
    * Insert into a scalar field.
    * Insert into a scalar field.
+   *
    * @param name field name
    * @param name field name
    * @param type field DataType
    * @param type field DataType
    * @param values the field values
    * @param values the field values
@@ -79,16 +79,18 @@ public class InsertParam {
       default:
       default:
         throw new UnsupportedDataType("Unsupported data type: " + type.name());
         throw new UnsupportedDataType("Unsupported data type: " + type.name());
     }
     }
-    builder.addFields(FieldValue.newBuilder()
-        .setFieldName(name)
-        .setTypeValue(type.getVal())
-        .setAttrRecord(record.build())
-        .build());
+    builder.addFields(
+        FieldValue.newBuilder()
+            .setFieldName(name)
+            .setTypeValue(type.getVal())
+            .setAttrRecord(record.build())
+            .build());
     return this;
     return this;
   }
   }
 
 
   /**
   /**
    * Insert into a vector field.
    * Insert into a vector field.
+   *
    * @param name field name
    * @param name field name
    * @param type field DataType
    * @param type field DataType
    * @param values vectors
    * @param values vectors
@@ -98,24 +100,31 @@ public class InsertParam {
     switch (type) {
     switch (type) {
       case VECTOR_FLOAT:
       case VECTOR_FLOAT:
         record.addAllRecords(
         record.addAllRecords(
-            ((List<List<Float>>) values).stream()
-                .map(row -> VectorRowRecord.newBuilder().addAllFloatData(row).build())
-                .collect(Collectors.toList()));
+            ((List<List<Float>>) values)
+                .stream()
+                    .map(row -> VectorRowRecord.newBuilder().addAllFloatData(row).build())
+                    .collect(Collectors.toList()));
         break;
         break;
       case VECTOR_BINARY:
       case VECTOR_BINARY:
         record.addAllRecords(
         record.addAllRecords(
-            ((List<ByteBuffer>) values).stream()
-                .map(row -> VectorRowRecord.newBuilder().setBinaryData(ByteString.copyFrom(row.slice())).build())
-                .collect(Collectors.toList()));
+            ((List<ByteBuffer>) values)
+                .stream()
+                    .map(
+                        row ->
+                            VectorRowRecord.newBuilder()
+                                .setBinaryData(ByteString.copyFrom(row.slice()))
+                                .build())
+                    .collect(Collectors.toList()));
         break;
         break;
       default:
       default:
         throw new UnsupportedDataType("Unsupported data type: " + type.name());
         throw new UnsupportedDataType("Unsupported data type: " + type.name());
     }
     }
-    builder.addFields(FieldValue.newBuilder()
-        .setFieldName(name)
-        .setTypeValue(type.getVal())
-        .setVectorRecord(record.build())
-        .build());
+    builder.addFields(
+        FieldValue.newBuilder()
+            .setFieldName(name)
+            .setTypeValue(type.getVal())
+            .setVectorRecord(record.build())
+            .build());
     return this;
     return this;
   }
   }
 
 

+ 50 - 29
src/main/java/io/milvus/client/LoggingAdapter.java

@@ -4,35 +4,51 @@ import com.google.protobuf.Descriptors;
 import com.google.protobuf.MessageOrBuilder;
 import com.google.protobuf.MessageOrBuilder;
 import com.google.protobuf.TextFormat;
 import com.google.protobuf.TextFormat;
 import io.grpc.MethodDescriptor;
 import io.grpc.MethodDescriptor;
-import org.slf4j.Logger;
-
 import java.util.List;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicLong;
+import org.slf4j.Logger;
 
 
 public class LoggingAdapter {
 public class LoggingAdapter {
   public static final LoggingAdapter DEFAULT_LOGGING_ADAPTER = new LoggingAdapter();
   public static final LoggingAdapter DEFAULT_LOGGING_ADAPTER = new LoggingAdapter();
   private static final AtomicLong traceId = new AtomicLong(0);
   private static final AtomicLong traceId = new AtomicLong(0);
 
 
-  protected LoggingAdapter() {
-  }
+  protected LoggingAdapter() {}
 
 
   protected String getTraceId() {
   protected String getTraceId() {
     return Long.toHexString(traceId.getAndIncrement());
     return Long.toHexString(traceId.getAndIncrement());
   }
   }
 
 
-  protected void logRequest(Logger logger, String traceId, MethodDescriptor method, Object message) {
+  protected void logRequest(
+      Logger logger, String traceId, MethodDescriptor method, Object message) {
     if (logger.isTraceEnabled()) {
     if (logger.isTraceEnabled()) {
-      logger.trace("TraceId: {}, Method: {}, Request: {}", traceId, method.getFullMethodName(), trace(message));
+      logger.trace(
+          "TraceId: {}, Method: {}, Request: {}",
+          traceId,
+          method.getFullMethodName(),
+          trace(message));
     } else if (logger.isInfoEnabled()) {
     } else if (logger.isInfoEnabled()) {
-      logger.info("TraceId: {}, Method: {}, Request: {}", traceId, method.getFullMethodName(), info(message));
+      logger.info(
+          "TraceId: {}, Method: {}, Request: {}",
+          traceId,
+          method.getFullMethodName(),
+          info(message));
     }
     }
   }
   }
 
 
-  protected void logResponse(Logger logger, String traceId, MethodDescriptor method, Object message) {
+  protected void logResponse(
+      Logger logger, String traceId, MethodDescriptor method, Object message) {
     if (logger.isTraceEnabled()) {
     if (logger.isTraceEnabled()) {
-      logger.trace("TraceId: {}, Method: {}, Response: {}", traceId, method.getFullMethodName(), trace(message));
+      logger.trace(
+          "TraceId: {}, Method: {}, Response: {}",
+          traceId,
+          method.getFullMethodName(),
+          trace(message));
     } else if (logger.isInfoEnabled()) {
     } else if (logger.isInfoEnabled()) {
-      logger.info("TraceId: {}, Method: {}, Response: {}", traceId, method.getFullMethodName(), info(message));
+      logger.info(
+          "TraceId: {}, Method: {}, Response: {}",
+          traceId,
+          method.getFullMethodName(),
+          info(message));
     }
     }
   }
   }
 
 
@@ -55,25 +71,30 @@ public class LoggingAdapter {
 
 
   protected void write(MessageOrBuilder message, StringBuilder output) {
   protected void write(MessageOrBuilder message, StringBuilder output) {
     output.append(" { ");
     output.append(" { ");
-    message.getAllFields().entrySet().stream().forEach(e -> {
-      if (e.getKey().isRepeated()) {
-        output.append(e.getKey().getName())
-            .append(" [ ")
-            .append(((List<?>) e.getValue()).size())
-            .append(" items ], ");
-      } else if (e.getKey().isMapField()) {
-        output.append(e.getKey().getName())
-            .append(" { ")
-            .append(((List<?>) e.getValue()).size())
-            .append(" entries }, ");
-      } else if (e.getKey().getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) {
-        output.append(e.getKey().getName());
-        write((MessageOrBuilder) e.getValue(), output);
-      } else {
-        output.append(TextFormat.printer().shortDebugString(e.getKey(), e.getValue()))
-            .append(", ");
-      }
-    });
+    message.getAllFields().entrySet().stream()
+        .forEach(
+            e -> {
+              if (e.getKey().isRepeated()) {
+                output
+                    .append(e.getKey().getName())
+                    .append(" [ ")
+                    .append(((List<?>) e.getValue()).size())
+                    .append(" items ], ");
+              } else if (e.getKey().isMapField()) {
+                output
+                    .append(e.getKey().getName())
+                    .append(" { ")
+                    .append(((List<?>) e.getValue()).size())
+                    .append(" entries }, ");
+              } else if (e.getKey().getJavaType() == Descriptors.FieldDescriptor.JavaType.MESSAGE) {
+                output.append(e.getKey().getName());
+                write((MessageOrBuilder) e.getValue(), output);
+              } else {
+                output
+                    .append(TextFormat.printer().shortDebugString(e.getKey(), e.getValue()))
+                    .append(", ");
+              }
+            });
     output.setLength(output.length() - 2);
     output.setLength(output.length() - 2);
     output.append(" } ");
     output.append(" } ");
   }
   }

+ 38 - 41
src/main/java/io/milvus/client/MilvusClient.java

@@ -20,8 +20,6 @@
 package io.milvus.client;
 package io.milvus.client;
 
 
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.util.List;
 import java.util.List;
@@ -29,27 +27,28 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
+import org.apache.commons.lang3.exception.ExceptionUtils;
 
 
 /** The Milvus Client Interface */
 /** The Milvus Client Interface */
-public interface MilvusClient {
+public interface MilvusClient extends AutoCloseable {
 
 
   String extraParamKey = "params";
   String extraParamKey = "params";
 
 
-  String clientVersion = new Supplier<String>() {
-
-    @Override
-    public String get() {
-      Properties properties = new Properties();
-      try (InputStream inputStream =
-               MilvusClient.class.getClassLoader()
-                   .getResourceAsStream("milvus-client.properties")) {
-        properties.load(inputStream);
-      } catch (IOException ex) {
-        ExceptionUtils.wrapAndThrow(ex);
-      }
-      return properties.getProperty("version");
-    }
-  }.get();
+  String clientVersion =
+      new Supplier<String>() {
+
+        @Override
+        public String get() {
+          Properties properties = new Properties();
+          try (InputStream inputStream =
+              MilvusClient.class.getClassLoader().getResourceAsStream("milvus-client.properties")) {
+            properties.load(inputStream);
+          } catch (IOException ex) {
+            ExceptionUtils.wrapAndThrow(ex);
+          }
+          return properties.getProperty("version");
+        }
+      }.get();
 
 
   String target();
   String target();
 
 
@@ -58,20 +57,17 @@ public interface MilvusClient {
     return clientVersion;
     return clientVersion;
   }
   }
 
 
-  /**
-   * Close this MilvusClient. Wait at most 1 minute for graceful shutdown.
-   */
+  /** Close this MilvusClient. Wait at most 1 minute for graceful shutdown. */
   default void close() {
   default void close() {
     close(TimeUnit.MINUTES.toSeconds(1));
     close(TimeUnit.MINUTES.toSeconds(1));
   }
   }
 
 
-  /**
-   * Close this MilvusClient. Wait at most `maxWaitSeconds` for graceful shutdown.
-   */
+  /** Close this MilvusClient. Wait at most `maxWaitSeconds` for graceful shutdown. */
   void close(long maxWaitSeconds);
   void close(long maxWaitSeconds);
 
 
   /**
   /**
    * Milvus service with timeout support.
    * Milvus service with timeout support.
+   *
    * @param timeout the desired timeout
    * @param timeout the desired timeout
    * @param timeoutUnit unit for timeout
    * @param timeoutUnit unit for timeout
    */
    */
@@ -81,7 +77,7 @@ public interface MilvusClient {
    * Creates collection specified by <code>collectionMapping</code>
    * Creates collection specified by <code>collectionMapping</code>
    *
    *
    * @param collectionMapping the <code>CollectionMapping</code> object
    * @param collectionMapping the <code>CollectionMapping</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * CollectionMapping collectionMapping = CollectionMapping
    * CollectionMapping collectionMapping = CollectionMapping
@@ -101,7 +97,7 @@ public interface MilvusClient {
   void createCollection(CollectionMapping collectionMapping);
   void createCollection(CollectionMapping collectionMapping);
 
 
   /**
   /**
-   * Checks whether the collection exists
+   * Check whether a collection exists
    *
    *
    * @param collectionName collection to check
    * @param collectionName collection to check
    * @return true if the collection exists, false otherwise.
    * @return true if the collection exists, false otherwise.
@@ -119,7 +115,7 @@ public interface MilvusClient {
    * Creates index specified by <code>index</code>
    * Creates index specified by <code>index</code>
    *
    *
    * @param index the <code>Index</code> object
    * @param index the <code>Index</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * Index index = Index.create(collectionName, "float_vec")
    * Index index = Index.create(collectionName, "float_vec")
@@ -137,7 +133,7 @@ public interface MilvusClient {
    * Creates index specified by <code>index</code> asynchronously
    * Creates index specified by <code>index</code> asynchronously
    *
    *
    * @param index the <code>Index</code> object
    * @param index the <code>Index</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * Index index = Index.create(collectionName, "float_vec")
    * Index index = Index.create(collectionName, "float_vec")
@@ -190,7 +186,7 @@ public interface MilvusClient {
    * Inserts data specified by <code>insertParam</code>
    * Inserts data specified by <code>insertParam</code>
    *
    *
    * @param insertParam the <code>InsertParam</code> object
    * @param insertParam the <code>InsertParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * InsertParam insertParam = InsertParam
    * InsertParam insertParam = InsertParam
@@ -211,7 +207,7 @@ public interface MilvusClient {
    * Inserts data specified by <code>insertParam</code> asynchronously
    * Inserts data specified by <code>insertParam</code> asynchronously
    *
    *
    * @param insertParam the <code>InsertParam</code> object
    * @param insertParam the <code>InsertParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * InsertParam insertParam = InsertParam
    * InsertParam insertParam = InsertParam
@@ -223,7 +219,8 @@ public interface MilvusClient {
    * </code>
    * </code>
    * </pre>
    * </pre>
    *
    *
-   * @return a <code>ListenableFuture</code> object which holds the list of ids of the inserted entities.
+   * @return a <code>ListenableFuture</code> object which holds the list of ids of the inserted
+   *     entities.
    * @see InsertParam
    * @see InsertParam
    * @see ListenableFuture
    * @see ListenableFuture
    */
    */
@@ -233,7 +230,7 @@ public interface MilvusClient {
    * Searches entities specified by <code>searchParam</code>
    * Searches entities specified by <code>searchParam</code>
    *
    *
    * @param searchParam the <code>SearchParam</code> object
    * @param searchParam the <code>SearchParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * SearchParam searchParam = SearchParam.create(collectionName)
    * SearchParam searchParam = SearchParam.create(collectionName)
@@ -255,7 +252,7 @@ public interface MilvusClient {
    * Searches entities specified by <code>searchParam</code> asynchronously
    * Searches entities specified by <code>searchParam</code> asynchronously
    *
    *
    * @param searchParam the <code>SearchParam</code> object
    * @param searchParam the <code>SearchParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * SearchParam searchParam = SearchParam.create(collectionName)
    * SearchParam searchParam = SearchParam.create(collectionName)
@@ -330,8 +327,8 @@ public interface MilvusClient {
    * Drops collection index
    * Drops collection index
    *
    *
    * @param collectionName The collection to drop index.
    * @param collectionName The collection to drop index.
-   * @param fieldName Name of the field to drop index for. If this is set to empty string,
-   *                  index of all fields in the collection will be dropped.
+   * @param fieldName Name of the field to drop index for. If this is set to empty string, index of
+   *     all fields in the collection will be dropped.
    */
    */
   void dropIndex(String collectionName, String fieldName);
   void dropIndex(String collectionName, String fieldName);
 
 
@@ -342,7 +339,6 @@ public interface MilvusClient {
    * result will be returned as JSON string.
    * result will be returned as JSON string.
    *
    *
    * @param collectionName collection to show info from
    * @param collectionName collection to show info from
-   *
    * @return collection stats
    * @return collection stats
    */
    */
   String getCollectionStats(String collectionName);
   String getCollectionStats(String collectionName);
@@ -352,11 +348,12 @@ public interface MilvusClient {
    *
    *
    * @param collectionName collection to get entities from
    * @param collectionName collection to get entities from
    * @param ids a <code>List</code> of entity ids
    * @param ids a <code>List</code> of entity ids
-   * @param fieldNames  a <code>List</code> of field names. Server will only return entity
-   *                    information for these fields.
+   * @param fieldNames a <code>List</code> of field names. Server will only return entity
+   *     information for these fields.
    * @return a map of entity id to entity properties
    * @return a map of entity id to entity properties
    */
    */
-  Map<Long, Map<String, Object>> getEntityByID(String collectionName, List<Long> ids, List<String> fieldNames);
+  Map<Long, Map<String, Object>> getEntityByID(
+      String collectionName, List<Long> ids, List<String> fieldNames);
 
 
   /**
   /**
    * Gets entities data by id array
    * Gets entities data by id array
@@ -426,7 +423,7 @@ public interface MilvusClient {
    * until you call compact.
    * until you call compact.
    *
    *
    * @param compactParam the <code>CompactParam</code> object
    * @param compactParam the <code>CompactParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * CompactParam compactParam = CompactParam.create(collectionName).setThreshold(0.3);
    * CompactParam compactParam = CompactParam.create(collectionName).setThreshold(0.3);
@@ -443,7 +440,7 @@ public interface MilvusClient {
    * until you call compact.
    * until you call compact.
    *
    *
    * @param compactParam the <code>CompactParam</code> object
    * @param compactParam the <code>CompactParam</code> object
-   * <pre>
+   *     <pre>
    * example usage:
    * example usage:
    * <code>
    * <code>
    * CompactParam compactParam = CompactParam.create(collectionName).setThreshold(0.3);
    * CompactParam compactParam = CompactParam.create(collectionName).setThreshold(0.3);

+ 299 - 197
src/main/java/io/milvus/client/MilvusGrpcClient.java

@@ -19,6 +19,8 @@
 
 
 package io.milvus.client;
 package io.milvus.client;
 
 
+import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
+
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -32,15 +34,36 @@ import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.Metadata;
 import io.grpc.Metadata;
 import io.grpc.MethodDescriptor;
 import io.grpc.MethodDescriptor;
+import io.grpc.stub.MetadataUtils;
 import io.milvus.client.exception.ClientSideMilvusException;
 import io.milvus.client.exception.ClientSideMilvusException;
 import io.milvus.client.exception.MilvusException;
 import io.milvus.client.exception.MilvusException;
 import io.milvus.client.exception.ServerSideMilvusException;
 import io.milvus.client.exception.ServerSideMilvusException;
 import io.milvus.client.exception.UnsupportedServerVersion;
 import io.milvus.client.exception.UnsupportedServerVersion;
-import io.milvus.grpc.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.annotation.Nonnull;
+import io.milvus.grpc.AttrRecord;
+import io.milvus.grpc.BoolReply;
+import io.milvus.grpc.CollectionInfo;
+import io.milvus.grpc.CollectionName;
+import io.milvus.grpc.CollectionNameList;
+import io.milvus.grpc.CollectionRowCount;
+import io.milvus.grpc.Command;
+import io.milvus.grpc.DeleteByIDParam;
+import io.milvus.grpc.Entities;
+import io.milvus.grpc.EntityIdentity;
+import io.milvus.grpc.EntityIds;
+import io.milvus.grpc.ErrorCode;
+import io.milvus.grpc.FieldValue;
+import io.milvus.grpc.FlushParam;
+import io.milvus.grpc.GetEntityIDsParam;
+import io.milvus.grpc.IndexParam;
+import io.milvus.grpc.Mapping;
+import io.milvus.grpc.MilvusServiceGrpc;
+import io.milvus.grpc.PartitionList;
+import io.milvus.grpc.PartitionParam;
+import io.milvus.grpc.QueryResult;
+import io.milvus.grpc.Status;
+import io.milvus.grpc.StringReply;
+import io.milvus.grpc.VectorRecord;
+import io.milvus.grpc.VectorRowRecord;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -50,6 +73,9 @@ import java.util.Map;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /** Actual implementation of interface <code>MilvusClient</code> */
 /** Actual implementation of interface <code>MilvusClient</code> */
 public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
 public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
@@ -64,22 +90,29 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
 
 
   public MilvusGrpcClient(ConnectParam connectParam) {
   public MilvusGrpcClient(ConnectParam connectParam) {
     target = connectParam.getTarget();
     target = connectParam.getTarget();
-    channel = ManagedChannelBuilder
-        .forTarget(connectParam.getTarget())
-        .usePlaintext()
-        .maxInboundMessageSize(Integer.MAX_VALUE)
-        .defaultLoadBalancingPolicy(connectParam.getDefaultLoadBalancingPolicy())
-        .keepAliveTime(connectParam.getKeepAliveTime(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
-        .keepAliveTimeout(connectParam.getKeepAliveTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
-        .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
-        .idleTimeout(connectParam.getIdleTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
-        .build();
+    Metadata metadata = new Metadata();
+    metadata.put(
+        Metadata.Key.of("client_tag", ASCII_STRING_MARSHALLER), connectParam.getClientTag());
+    channel =
+        ManagedChannelBuilder.forTarget(connectParam.getTarget())
+            .usePlaintext()
+            .intercept(MetadataUtils.newAttachHeadersInterceptor(metadata))
+            .maxInboundMessageSize(Integer.MAX_VALUE)
+            .defaultLoadBalancingPolicy(connectParam.getDefaultLoadBalancingPolicy())
+            .keepAliveTime(
+                connectParam.getKeepAliveTime(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
+            .keepAliveTimeout(
+                connectParam.getKeepAliveTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
+            .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
+            .idleTimeout(connectParam.getIdleTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
+            .build();
     blockingStub = MilvusServiceGrpc.newBlockingStub(channel);
     blockingStub = MilvusServiceGrpc.newBlockingStub(channel);
     futureStub = MilvusServiceGrpc.newFutureStub(channel);
     futureStub = MilvusServiceGrpc.newFutureStub(channel);
     try {
     try {
       String serverVersion = getServerVersion();
       String serverVersion = getServerVersion();
       if (!serverVersion.matches("^" + SUPPORTED_SERVER_VERSION + "(\\..*)?$")) {
       if (!serverVersion.matches("^" + SUPPORTED_SERVER_VERSION + "(\\..*)?$")) {
-        throw new UnsupportedServerVersion(connectParam.getTarget(), SUPPORTED_SERVER_VERSION, serverVersion);
+        throw new UnsupportedServerVersion(
+            connectParam.getTarget(), SUPPORTED_SERVER_VERSION, serverVersion);
       }
       }
     } catch (Throwable t) {
     } catch (Throwable t) {
       channel.shutdownNow();
       channel.shutdownNow();
@@ -163,7 +196,7 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
   }
   }
 
 
   private static class TimeoutInterceptor implements ClientInterceptor {
   private static class TimeoutInterceptor implements ClientInterceptor {
-    private long timeoutMillis;
+    private final long timeoutMillis;
 
 
     TimeoutInterceptor(long timeoutMillis) {
     TimeoutInterceptor(long timeoutMillis) {
       this.timeoutMillis = timeoutMillis;
       this.timeoutMillis = timeoutMillis;
@@ -172,12 +205,13 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
     @Override
     @Override
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
         MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
         MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
-      return next.newCall(method, callOptions.withDeadlineAfter(timeoutMillis, TimeUnit.MILLISECONDS));
+      return next.newCall(
+          method, callOptions.withDeadlineAfter(timeoutMillis, TimeUnit.MILLISECONDS));
     }
     }
   }
   }
 
 
   private static class LoggingInterceptor implements ClientInterceptor {
   private static class LoggingInterceptor implements ClientInterceptor {
-    private LoggingAdapter loggingAdapter;
+    private final LoggingAdapter loggingAdapter;
 
 
     LoggingInterceptor(LoggingAdapter loggingAdapter) {
     LoggingInterceptor(LoggingAdapter loggingAdapter) {
       this.loggingAdapter = loggingAdapter;
       this.loggingAdapter = loggingAdapter;
@@ -186,8 +220,9 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
     @Override
     @Override
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
     public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
         MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
         MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
-      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
-        private String traceId = loggingAdapter.getTraceId();
+      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
+          next.newCall(method, callOptions)) {
+        private final String traceId = loggingAdapter.getTraceId();
 
 
         @Override
         @Override
         public void sendMessage(ReqT message) {
         public void sendMessage(ReqT message) {
@@ -197,13 +232,16 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
 
 
         @Override
         @Override
         public void start(Listener<RespT> responseListener, Metadata headers) {
         public void start(Listener<RespT> responseListener, Metadata headers) {
-          super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(responseListener) {
-            @Override
-            public void onMessage(RespT message) {
-              loggingAdapter.logResponse(logger, traceId, method, message);
-              super.onMessage(message);
-            }
-          }, headers);
+          super.start(
+              new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(
+                  responseListener) {
+                @Override
+                public void onMessage(RespT message) {
+                  loggingAdapter.logResponse(logger, traceId, method, message);
+                  super.onMessage(message);
+                }
+              },
+              headers);
         }
         }
       };
       };
     }
     }
@@ -212,13 +250,15 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
 
 
 abstract class AbstractMilvusGrpcClient implements MilvusClient {
 abstract class AbstractMilvusGrpcClient implements MilvusClient {
   protected abstract MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub();
   protected abstract MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub();
+
   protected abstract MilvusServiceGrpc.MilvusServiceFutureStub futureStub();
   protected abstract MilvusServiceGrpc.MilvusServiceFutureStub futureStub();
 
 
   private void translateExceptions(Runnable body) {
   private void translateExceptions(Runnable body) {
-    translateExceptions(() -> {
-      body.run();
-      return null;
-    });
+    translateExceptions(
+        () -> {
+          body.run();
+          return null;
+        });
   }
   }
 
 
   @SuppressWarnings("unchecked")
   @SuppressWarnings("unchecked")
@@ -227,8 +267,10 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
       T result = body.get();
       T result = body.get();
       if (result instanceof ListenableFuture) {
       if (result instanceof ListenableFuture) {
         ListenableFuture futureResult = (ListenableFuture) result;
         ListenableFuture futureResult = (ListenableFuture) result;
-        result = (T) Futures.catching(
-            futureResult, Throwable.class, this::translate, MoreExecutors.directExecutor());
+        result =
+            (T)
+                Futures.catching(
+                    futureResult, Throwable.class, this::translate, MoreExecutors.directExecutor());
       }
       }
       return result;
       return result;
     } catch (Throwable e) {
     } catch (Throwable e) {
@@ -255,84 +297,99 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public void createCollection(@Nonnull CollectionMapping collectionMapping) {
   public void createCollection(@Nonnull CollectionMapping collectionMapping) {
-    translateExceptions(() -> {
-      Status response = blockingStub().createCollection(collectionMapping.grpc());
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          Status response = blockingStub().createCollection(collectionMapping.grpc());
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public boolean hasCollection(@Nonnull String collectionName) {
   public boolean hasCollection(@Nonnull String collectionName) {
-    return translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      BoolReply response = blockingStub().hasCollection(request);
-      checkResponseStatus(response.getStatus());
-      return response.getBoolReply();
-    });
+    return translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          BoolReply response = blockingStub().hasCollection(request);
+          checkResponseStatus(response.getStatus());
+          return response.getBoolReply();
+        });
   }
   }
 
 
   @Override
   @Override
   public void dropCollection(@Nonnull String collectionName) {
   public void dropCollection(@Nonnull String collectionName) {
-    translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      Status response = blockingStub().dropCollection(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          Status response = blockingStub().dropCollection(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public void createIndex(@Nonnull Index index) {
   public void createIndex(@Nonnull Index index) {
-    translateExceptions(() -> {
-      Futures.getUnchecked(createIndexAsync(index));
-    });
+    translateExceptions(
+        () -> {
+          Futures.getUnchecked(createIndexAsync(index));
+        });
   }
   }
 
 
   @Override
   @Override
   public ListenableFuture<Void> createIndexAsync(@Nonnull Index index) {
   public ListenableFuture<Void> createIndexAsync(@Nonnull Index index) {
-    return translateExceptions(() -> {
-      IndexParam request = index.grpc();
-      ListenableFuture<Status> responseFuture = futureStub().createIndex(request);
-      return Futures.transform(responseFuture, this::checkResponseStatus, MoreExecutors.directExecutor());
-    });
+    return translateExceptions(
+        () -> {
+          IndexParam request = index.grpc();
+          ListenableFuture<Status> responseFuture = futureStub().createIndex(request);
+          return Futures.transform(
+              responseFuture, this::checkResponseStatus, MoreExecutors.directExecutor());
+        });
   }
   }
 
 
   @Override
   @Override
   public void createPartition(String collectionName, String tag) {
   public void createPartition(String collectionName, String tag) {
-    translateExceptions(() -> {
-      PartitionParam request = PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-      Status response = blockingStub().createPartition(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          PartitionParam request =
+              PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
+          Status response = blockingStub().createPartition(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public boolean hasPartition(String collectionName, String tag) {
   public boolean hasPartition(String collectionName, String tag) {
-    return translateExceptions(() -> {
-      PartitionParam request = PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-      BoolReply response = blockingStub().hasPartition(request);
-      checkResponseStatus(response.getStatus());
-      return response.getBoolReply();
-    });
+    return translateExceptions(
+        () -> {
+          PartitionParam request =
+              PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
+          BoolReply response = blockingStub().hasPartition(request);
+          checkResponseStatus(response.getStatus());
+          return response.getBoolReply();
+        });
   }
   }
 
 
   @Override
   @Override
   public List<String> listPartitions(String collectionName) {
   public List<String> listPartitions(String collectionName) {
-    return translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      PartitionList response = blockingStub().showPartitions(request);
-      checkResponseStatus(response.getStatus());
-      return response.getPartitionTagArrayList();
-    });
+    return translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          PartitionList response = blockingStub().showPartitions(request);
+          checkResponseStatus(response.getStatus());
+          return response.getPartitionTagArrayList();
+        });
   }
   }
 
 
   @Override
   @Override
   public void dropPartition(String collectionName, String tag) {
   public void dropPartition(String collectionName, String tag) {
-    translateExceptions(() -> {
-      PartitionParam request =
-          PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-      Status response = blockingStub().dropPartition(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          PartitionParam request =
+              PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
+          Status response = blockingStub().dropPartition(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
@@ -342,14 +399,18 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public ListenableFuture<List<Long>> insertAsync(@Nonnull InsertParam insertParam) {
   public ListenableFuture<List<Long>> insertAsync(@Nonnull InsertParam insertParam) {
-    return translateExceptions(() -> {
-      io.milvus.grpc.InsertParam request = insertParam.grpc();
-      ListenableFuture<EntityIds> responseFuture = futureStub().insert(request);
-      return Futures.transform(responseFuture, entityIds -> {
-        checkResponseStatus(entityIds.getStatus());
-        return entityIds.getEntityIdArrayList();
-      }, MoreExecutors.directExecutor());
-    });
+    return translateExceptions(
+        () -> {
+          io.milvus.grpc.InsertParam request = insertParam.grpc();
+          ListenableFuture<EntityIds> responseFuture = futureStub().insert(request);
+          return Futures.transform(
+              responseFuture,
+              entityIds -> {
+                checkResponseStatus(entityIds.getStatus());
+                return entityIds.getEntityIdArrayList();
+              },
+              MoreExecutors.directExecutor());
+        });
   }
   }
 
 
   @Override
   @Override
@@ -359,44 +420,53 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public ListenableFuture<SearchResult> searchAsync(@Nonnull SearchParam searchParam) {
   public ListenableFuture<SearchResult> searchAsync(@Nonnull SearchParam searchParam) {
-    return translateExceptions(() -> {
-      io.milvus.grpc.SearchParam request = searchParam.grpc();
-      ListenableFuture<QueryResult> responseFuture = futureStub().search(request);
-      return Futures.transform(responseFuture, queryResult -> {
-        checkResponseStatus(queryResult.getStatus());
-        return buildSearchResponse(queryResult);
-      }, MoreExecutors.directExecutor());
-    });
+    return translateExceptions(
+        () -> {
+          io.milvus.grpc.SearchParam request = searchParam.grpc();
+          ListenableFuture<QueryResult> responseFuture = futureStub().search(request);
+          return Futures.transform(
+              responseFuture,
+              queryResult -> {
+                checkResponseStatus(queryResult.getStatus());
+                return buildSearchResponse(queryResult);
+              },
+              MoreExecutors.directExecutor());
+        });
   }
   }
 
 
   @Override
   @Override
   public CollectionMapping getCollectionInfo(@Nonnull String collectionName) {
   public CollectionMapping getCollectionInfo(@Nonnull String collectionName) {
-    return translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      Mapping response = blockingStub().describeCollection(request);
-      checkResponseStatus(response.getStatus());
-      return new CollectionMapping(response);
-    });
+    return translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          Mapping response = blockingStub().describeCollection(request);
+          checkResponseStatus(response.getStatus());
+          return new CollectionMapping(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public List<String> listCollections() {
   public List<String> listCollections() {
-    return translateExceptions(() -> {
-      Command request = Command.newBuilder().setCmd("").build();
-      CollectionNameList response = blockingStub().showCollections(request);
-      checkResponseStatus(response.getStatus());
-      return response.getCollectionNamesList();
-    });
+    return translateExceptions(
+        () -> {
+          Command request = Command.newBuilder().setCmd("").build();
+          CollectionNameList response = blockingStub().showCollections(request);
+          checkResponseStatus(response.getStatus());
+          return response.getCollectionNamesList();
+        });
   }
   }
 
 
   @Override
   @Override
   public long countEntities(@Nonnull String collectionName) {
   public long countEntities(@Nonnull String collectionName) {
-    return translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      CollectionRowCount response = blockingStub().countCollection(request);
-      checkResponseStatus(response.getStatus());
-      return response.getCollectionRowCount();
-    });
+    return translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          CollectionRowCount response = blockingStub().countCollection(request);
+          checkResponseStatus(response.getStatus());
+          return response.getCollectionRowCount();
+        });
   }
   }
 
 
   @Override
   @Override
@@ -410,75 +480,84 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
   }
   }
 
 
   public String command(@Nonnull String command) {
   public String command(@Nonnull String command) {
-    return translateExceptions(() -> {
-      Command request = Command.newBuilder().setCmd(command).build();
-      StringReply response = blockingStub().cmd(request);
-      checkResponseStatus(response.getStatus());
-      return response.getStringReply();
-    });
+    return translateExceptions(
+        () -> {
+          Command request = Command.newBuilder().setCmd(command).build();
+          StringReply response = blockingStub().cmd(request);
+          checkResponseStatus(response.getStatus());
+          return response.getStringReply();
+        });
   }
   }
 
 
   @Override
   @Override
   public void loadCollection(@Nonnull String collectionName) {
   public void loadCollection(@Nonnull String collectionName) {
-    translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      Status response = blockingStub().preloadCollection(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          Status response = blockingStub().preloadCollection(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public void dropIndex(String collectionName, String fieldName) {
   public void dropIndex(String collectionName, String fieldName) {
-    translateExceptions(() -> {
-      IndexParam request = IndexParam.newBuilder()
-          .setCollectionName(collectionName)
-          .setFieldName(fieldName)
-          .build();
-      Status response = blockingStub().dropIndex(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          IndexParam request =
+              IndexParam.newBuilder()
+                  .setCollectionName(collectionName)
+                  .setFieldName(fieldName)
+                  .build();
+          Status response = blockingStub().dropIndex(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
   public String getCollectionStats(String collectionName) {
   public String getCollectionStats(String collectionName) {
-    return translateExceptions(() -> {
-      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-      CollectionInfo response = blockingStub().showCollectionInfo(request);
-      checkResponseStatus(response.getStatus());
-      return response.getJsonInfo();
-    });
-  }
-  
-  @Override
-  public Map<Long, Map<String, Object>> getEntityByID(String collectionName, List<Long> ids, List<String> fieldNames) {
-    return translateExceptions(() -> {
-      EntityIdentity request = EntityIdentity.newBuilder()
-          .setCollectionName(collectionName)
-          .addAllIdArray(ids)
-          .addAllFieldNames(fieldNames)
-          .build();
-      Entities response = blockingStub().getEntityByID(request);
-      checkResponseStatus(response.getStatus());
-      Map<String, Iterator<?>> fieldIterators = response.getFieldsList()
-          .stream()
-          .collect(Collectors.toMap(FieldValue::getFieldName, this::fieldValueIterator));
-      Iterator<Long> idIterator = ids.iterator();
-      Map<Long, Map<String, Object>> entities = new HashMap<>(response.getValidRowList().size());
-      for (boolean valid : response.getValidRowList()) {
-        long id = idIterator.next();
-        if (valid) {
-          entities.put(id, toMap(fieldIterators));
-        }
-      }
-      return entities;
-    });
+    return translateExceptions(
+        () -> {
+          CollectionName request =
+              CollectionName.newBuilder().setCollectionName(collectionName).build();
+          CollectionInfo response = blockingStub().showCollectionInfo(request);
+          checkResponseStatus(response.getStatus());
+          return response.getJsonInfo();
+        });
+  }
+
+  @Override
+  public Map<Long, Map<String, Object>> getEntityByID(
+      String collectionName, List<Long> ids, List<String> fieldNames) {
+    return translateExceptions(
+        () -> {
+          EntityIdentity request =
+              EntityIdentity.newBuilder()
+                  .setCollectionName(collectionName)
+                  .addAllIdArray(ids)
+                  .addAllFieldNames(fieldNames)
+                  .build();
+          Entities response = blockingStub().getEntityByID(request);
+          checkResponseStatus(response.getStatus());
+          Map<String, Iterator<?>> fieldIterators =
+              response.getFieldsList().stream()
+                  .collect(Collectors.toMap(FieldValue::getFieldName, this::fieldValueIterator));
+          Iterator<Long> idIterator = ids.iterator();
+          Map<Long, Map<String, Object>> entities =
+              new HashMap<>(response.getValidRowList().size());
+          for (boolean valid : response.getValidRowList()) {
+            long id = idIterator.next();
+            if (valid) {
+              entities.put(id, toMap(fieldIterators));
+            }
+          }
+          return entities;
+        });
   }
   }
-  
+
   private Map<String, Object> toMap(Map<String, Iterator<?>> fieldIterators) {
   private Map<String, Object> toMap(Map<String, Iterator<?>> fieldIterators) {
     return fieldIterators.entrySet().stream()
     return fieldIterators.entrySet().stream()
-        .collect(Collectors.toMap(
-            entry -> entry.getKey(),
-            entry -> entry.getValue().next()));
+        .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().next()));
   }
   }
 
 
   private Iterator<?> fieldValueIterator(FieldValue fieldValue) {
   private Iterator<?> fieldValueIterator(FieldValue fieldValue) {
@@ -496,7 +575,11 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
     }
     }
     VectorRecord record = fieldValue.getVectorRecord();
     VectorRecord record = fieldValue.getVectorRecord();
     return record.getRecordsList().stream()
     return record.getRecordsList().stream()
-        .map(row -> row.getFloatDataCount() > 0 ? row.getFloatDataList() : row.getBinaryData().asReadOnlyByteBuffer())
+        .map(
+            row ->
+                row.getFloatDataCount() > 0
+                    ? row.getFloatDataList()
+                    : row.getBinaryData().asReadOnlyByteBuffer())
         .iterator();
         .iterator();
   }
   }
 
 
@@ -507,27 +590,31 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public List<Long> listIDInSegment(String collectionName, Long segmentId) {
   public List<Long> listIDInSegment(String collectionName, Long segmentId) {
-    return translateExceptions(() -> {
-      GetEntityIDsParam request = GetEntityIDsParam.newBuilder()
-          .setCollectionName(collectionName)
-          .setSegmentId(segmentId)
-          .build();
-      EntityIds response = blockingStub().getEntityIDs(request);
-      checkResponseStatus(response.getStatus());
-      return response.getEntityIdArrayList();
-    });
+    return translateExceptions(
+        () -> {
+          GetEntityIDsParam request =
+              GetEntityIDsParam.newBuilder()
+                  .setCollectionName(collectionName)
+                  .setSegmentId(segmentId)
+                  .build();
+          EntityIds response = blockingStub().getEntityIDs(request);
+          checkResponseStatus(response.getStatus());
+          return response.getEntityIdArrayList();
+        });
   }
   }
 
 
   @Override
   @Override
   public void deleteEntityByID(String collectionName, List<Long> ids) {
   public void deleteEntityByID(String collectionName, List<Long> ids) {
-    translateExceptions(() -> {
-      DeleteByIDParam request = DeleteByIDParam.newBuilder()
-          .setCollectionName(collectionName)
-          .addAllIdArray(ids)
-          .build();
-      Status response = blockingStub().deleteByID(request);
-      checkResponseStatus(response);
-    });
+    translateExceptions(
+        () -> {
+          DeleteByIDParam request =
+              DeleteByIDParam.newBuilder()
+                  .setCollectionName(collectionName)
+                  .addAllIdArray(ids)
+                  .build();
+          Status response = blockingStub().deleteByID(request);
+          checkResponseStatus(response);
+        });
   }
   }
 
 
   @Override
   @Override
@@ -537,11 +624,14 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public ListenableFuture<Void> flushAsync(@Nonnull List<String> collectionNames) {
   public ListenableFuture<Void> flushAsync(@Nonnull List<String> collectionNames) {
-    return translateExceptions(() -> {
-      FlushParam request = FlushParam.newBuilder().addAllCollectionNameArray(collectionNames).build();
-      ListenableFuture<Status> response = futureStub().flush(request);
-      return Futures.transform(response, this::checkResponseStatus, MoreExecutors.directExecutor());
-    });
+    return translateExceptions(
+        () -> {
+          FlushParam request =
+              FlushParam.newBuilder().addAllCollectionNameArray(collectionNames).build();
+          ListenableFuture<Status> response = futureStub().flush(request);
+          return Futures.transform(
+              response, this::checkResponseStatus, MoreExecutors.directExecutor());
+        });
   }
   }
 
 
   @Override
   @Override
@@ -561,11 +651,13 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public ListenableFuture<Void> compactAsync(@Nonnull CompactParam compactParam) {
   public ListenableFuture<Void> compactAsync(@Nonnull CompactParam compactParam) {
-    return translateExceptions(() -> {
-      io.milvus.grpc.CompactParam request = compactParam.grpc();
-      ListenableFuture<Status> response = futureStub().compact(request);
-      return Futures.transform(response, this::checkResponseStatus, MoreExecutors.directExecutor());
-    });
+    return translateExceptions(
+        () -> {
+          io.milvus.grpc.CompactParam request = compactParam.grpc();
+          ListenableFuture<Status> response = futureStub().compact(request);
+          return Futures.transform(
+              response, this::checkResponseStatus, MoreExecutors.directExecutor());
+        });
   }
   }
 
 
   ///////////////////// Util Functions/////////////////////
   ///////////////////// Util Functions/////////////////////
@@ -607,7 +699,11 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
               if (vectorRowRecordList.get(j).getFloatDataCount() > 0) {
               if (vectorRowRecordList.get(j).getFloatDataCount() > 0) {
                 fieldsMap.get(j).put(fieldName, vectorRowRecordList.get(j).getFloatDataList());
                 fieldsMap.get(j).put(fieldName, vectorRowRecordList.get(j).getFloatDataList());
               } else {
               } else {
-                fieldsMap.get(j).put(fieldName, vectorRowRecordList.get(j).getBinaryData().asReadOnlyByteBuffer());
+                fieldsMap
+                    .get(j)
+                    .put(
+                        fieldName,
+                        vectorRowRecordList.get(j).getBinaryData().asReadOnlyByteBuffer());
               }
               }
             }
             }
           }
           }
@@ -626,6 +722,12 @@ abstract class AbstractMilvusGrpcClient implements MilvusClient {
         resultDistancesList.add(queryDistancesList.subList(i * topK, pos));
         resultDistancesList.add(queryDistancesList.subList(i * topK, pos));
         resultFieldsMap.add(fieldsMap.subList(i * topK, pos));
         resultFieldsMap.add(fieldsMap.subList(i * topK, pos));
       }
       }
+    } else {
+      for (int i = 0; i < numQueries; i++) {
+        resultIdsList.add(new ArrayList<>());
+        resultDistancesList.add(new ArrayList<>());
+        resultFieldsMap.add(new ArrayList<>());
+      }
     }
     }
 
 
     return new SearchResult(numQueries, topK, resultIdsList, resultDistancesList, resultFieldsMap);
     return new SearchResult(numQueries, topK, resultIdsList, resultDistancesList, resultFieldsMap);

+ 64 - 49
src/main/java/io/milvus/client/SearchParam.java

@@ -27,38 +27,46 @@ import io.milvus.grpc.KeyValuePair;
 import io.milvus.grpc.VectorParam;
 import io.milvus.grpc.VectorParam;
 import io.milvus.grpc.VectorRecord;
 import io.milvus.grpc.VectorRecord;
 import io.milvus.grpc.VectorRowRecord;
 import io.milvus.grpc.VectorRowRecord;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 import java.util.stream.StreamSupport;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 
 
 /** Contains parameters for <code>search</code> */
 /** Contains parameters for <code>search</code> */
 public class SearchParam {
 public class SearchParam {
   private static final String VECTOR_QUERY_KEY = "vector";
   private static final String VECTOR_QUERY_KEY = "vector";
   private static final String VECTOR_QUERY_PLACEHOLDER = "placeholder";
   private static final String VECTOR_QUERY_PLACEHOLDER = "placeholder";
 
 
-  private io.milvus.grpc.SearchParam.Builder builder;
+  private final io.milvus.grpc.SearchParam.Builder builder;
 
 
+  private SearchParam(String collectionName) {
+    builder = io.milvus.grpc.SearchParam.newBuilder();
+    builder.setCollectionName(collectionName);
+  }
+
+  /** @param collectionName collection name */
   public static SearchParam create(String collectionName) {
   public static SearchParam create(String collectionName) {
     return new SearchParam(collectionName);
     return new SearchParam(collectionName);
   }
   }
 
 
-  private SearchParam(String collectionName) {
-    builder = io.milvus.grpc.SearchParam.newBuilder();
-    builder.setCollectionName(collectionName);
+  /** @param json DSL in JSON format. */
+  public SearchParam setDsl(JSONObject json) {
+    builder.setDsl(json.toString());
+    return this;
   }
   }
 
 
+  /** @param dsl DSL in string format. */
   public SearchParam setDsl(String dsl) {
   public SearchParam setDsl(String dsl) {
     try {
     try {
       JSONObject dslJson = new JSONObject(dsl);
       JSONObject dslJson = new JSONObject(dsl);
-      JSONObject vectorQueryParent = locateVectorQuery(dslJson)
-          .orElseThrow(() -> new InvalidDsl("A vector query must be specified", dsl));
+      JSONObject vectorQueryParent =
+          locateVectorQuery(dslJson)
+              .orElseThrow(() -> new InvalidDsl("A vector query must be specified", dsl));
       JSONObject vectorQueries = vectorQueryParent.getJSONObject(VECTOR_QUERY_KEY);
       JSONObject vectorQueries = vectorQueryParent.getJSONObject(VECTOR_QUERY_KEY);
       vectorQueryParent.put(VECTOR_QUERY_KEY, VECTOR_QUERY_PLACEHOLDER);
       vectorQueryParent.put(VECTOR_QUERY_KEY, VECTOR_QUERY_PLACEHOLDER);
       String vectorQueryField = vectorQueries.keys().next();
       String vectorQueryField = vectorQueries.keys().next();
@@ -82,29 +90,32 @@ public class SearchParam {
       vectorQuery.remove("type");
       vectorQuery.remove("type");
       vectorQuery.remove("query");
       vectorQuery.remove("query");
       json.put("placeholder", vectorQueries);
       json.put("placeholder", vectorQueries);
-      VectorParam vectorParam = VectorParam.newBuilder()
-          .setJson(json.toString())
-          .setRowRecord(vectorRecord)
-          .build();
+      VectorParam vectorParam =
+          VectorParam.newBuilder().setJson(json.toString()).setRowRecord(vectorRecord).build();
 
 
-      builder.setDsl(dslJson.toString())
-          .addAllVectorParam(ImmutableList.of(vectorParam));
+      builder.setDsl(dslJson.toString()).addAllVectorParam(ImmutableList.of(vectorParam));
       return this;
       return this;
     } catch (JSONException e) {
     } catch (JSONException e) {
       throw new InvalidDsl(e.getMessage(), dsl);
       throw new InvalidDsl(e.getMessage(), dsl);
     }
     }
   }
   }
 
 
+  public SearchParam addQueries(VectorParam vectorParam) {
+    builder.addVectorParam(vectorParam);
+    return this;
+  }
+
   public SearchParam setPartitionTags(List<String> partitionTags) {
   public SearchParam setPartitionTags(List<String> partitionTags) {
     builder.addAllPartitionTagArray(partitionTags);
     builder.addAllPartitionTagArray(partitionTags);
     return this;
     return this;
   }
   }
 
 
   public SearchParam setParamsInJson(String paramsInJson) {
   public SearchParam setParamsInJson(String paramsInJson) {
-    builder.addExtraParams(KeyValuePair.newBuilder()
-        .setKey(MilvusClient.extraParamKey)
-        .setValue(paramsInJson)
-        .build());
+    builder.addExtraParams(
+        KeyValuePair.newBuilder()
+            .setKey(MilvusClient.extraParamKey)
+            .setValue(paramsInJson)
+            .build());
     return this;
     return this;
   }
   }
 
 
@@ -113,9 +124,9 @@ public class SearchParam {
   }
   }
 
 
   private Optional<JSONObject> locateVectorQuery(Object obj) {
   private Optional<JSONObject> locateVectorQuery(Object obj) {
-    return obj instanceof JSONObject ? locateVectorQuery((JSONObject) obj)
-        : obj instanceof JSONArray ? locateVectorQuery((JSONArray) obj)
-        : Optional.empty();
+    return obj instanceof JSONObject
+        ? locateVectorQuery((JSONObject) obj)
+        : obj instanceof JSONArray ? locateVectorQuery((JSONArray) obj) : Optional.empty();
   }
   }
 
 
   private Optional<JSONObject> locateVectorQuery(JSONArray array) {
   private Optional<JSONObject> locateVectorQuery(JSONArray array) {
@@ -138,36 +149,40 @@ public class SearchParam {
   }
   }
 
 
   private VectorRecord toFloatVectorRecord(JSONArray data) {
   private VectorRecord toFloatVectorRecord(JSONArray data) {
-    return VectorRecord.newBuilder().addAllRecords(
-        StreamSupport.stream(data.spliterator(), false)
-            .map(element -> (JSONArray) element)
-            .map(array -> {
-              int dimension = array.length();
-              List<Float> vector = new ArrayList<>(dimension);
-              for (int i = 0; i < dimension; i++) {
-                vector.add(array.getFloat(i));
-              }
-              return VectorRowRecord.newBuilder().addAllFloatData(vector).build();
-            })
-            .collect(Collectors.toList()))
+    return VectorRecord.newBuilder()
+        .addAllRecords(
+            StreamSupport.stream(data.spliterator(), false)
+                .map(element -> (JSONArray) element)
+                .map(
+                    array -> {
+                      int dimension = array.length();
+                      List<Float> vector = new ArrayList<>(dimension);
+                      for (int i = 0; i < dimension; i++) {
+                        vector.add(array.getFloat(i));
+                      }
+                      return VectorRowRecord.newBuilder().addAllFloatData(vector).build();
+                    })
+                .collect(Collectors.toList()))
         .build();
         .build();
   }
   }
 
 
   private VectorRecord toBinaryVectorRecord(JSONArray data) {
   private VectorRecord toBinaryVectorRecord(JSONArray data) {
-    return VectorRecord.newBuilder().addAllRecords(
-        StreamSupport.stream(data.spliterator(), false)
-            .map(element -> (JSONArray) element)
-            .map(array -> {
-              int dimension = array.length();
-              ByteBuffer bytes = ByteBuffer.allocate(dimension);
-              for (int i = 0; i < dimension; i++) {
-                bytes.put(array.getNumber(i).byteValue());
-              }
-              bytes.flip();
-              ByteString vector = UnsafeByteOperations.unsafeWrap(bytes);
-              return VectorRowRecord.newBuilder().setBinaryData(vector).build();
-            })
-            .collect(Collectors.toList()))
+    return VectorRecord.newBuilder()
+        .addAllRecords(
+            StreamSupport.stream(data.spliterator(), false)
+                .map(element -> (JSONArray) element)
+                .map(
+                    array -> {
+                      int dimension = array.length();
+                      ByteBuffer bytes = ByteBuffer.allocate(dimension);
+                      for (int i = 0; i < dimension; i++) {
+                        bytes.put(array.getNumber(i).byteValue());
+                      }
+                      bytes.flip();
+                      ByteString vector = UnsafeByteOperations.unsafeWrap(bytes);
+                      return VectorRowRecord.newBuilder().setBinaryData(vector).build();
+                    })
+                .collect(Collectors.toList()))
         .build();
         .build();
   }
   }
 }
 }

+ 19 - 13
src/main/java/io/milvus/client/SearchResult.java

@@ -7,17 +7,18 @@ import java.util.stream.IntStream;
 
 
 /** A class that contains information from Search */
 /** A class that contains information from Search */
 public class SearchResult {
 public class SearchResult {
-  private int numQueries;
-  private long topK;
-  private List<List<Long>> resultIdsList;
-  private List<List<Float>> resultDistancesList;
-  private List<List<Map<String, Object>>> fieldsMap;
+  private final int numQueries;
+  private final long topK;
+  private final List<List<Long>> resultIdsList;
+  private final List<List<Float>> resultDistancesList;
+  private final List<List<Map<String, Object>>> fieldsMap;
 
 
-  public SearchResult(int numQueries,
-                      long topK,
-                      List<List<Long>> resultIdsList,
-                      List<List<Float>> resultDistancesList,
-                      List<List<Map<String, Object>>> fieldsMap) {
+  public SearchResult(
+      int numQueries,
+      long topK,
+      List<List<Long>> resultIdsList,
+      List<List<Float>> resultDistancesList,
+      List<List<Map<String, Object>>> fieldsMap) {
     this.numQueries = numQueries;
     this.numQueries = numQueries;
     this.topK = topK;
     this.topK = topK;
     this.resultIdsList = resultIdsList;
     this.resultIdsList = resultIdsList;
@@ -47,9 +48,14 @@ public class SearchResult {
 
 
   public List<List<QueryResult>> getQueryResultsList() {
   public List<List<QueryResult>> getQueryResultsList() {
     return IntStream.range(0, numQueries)
     return IntStream.range(0, numQueries)
-        .mapToObj(i -> IntStream.range(0, resultIdsList.get(i).size())
-            .mapToObj(j -> new QueryResult(resultIdsList.get(i).get(j), resultDistancesList.get(i).get(j)))
-            .collect(Collectors.toList()))
+        .mapToObj(
+            i ->
+                IntStream.range(0, resultIdsList.get(i).size())
+                    .mapToObj(
+                        j ->
+                            new QueryResult(
+                                resultIdsList.get(i).get(j), resultDistancesList.get(i).get(j)))
+                    .collect(Collectors.toList()))
         .collect(Collectors.toList());
         .collect(Collectors.toList());
   }
   }
 
 

+ 45 - 0
src/main/java/io/milvus/client/dsl/BoolQuery.java

@@ -0,0 +1,45 @@
+package io.milvus.client.dsl;
+
+import io.milvus.client.SearchParam;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/** Boolean Query */
+public class BoolQuery extends Query {
+  private final Type type;
+  private final List<Query> subqueries;
+
+  BoolQuery(Type type, List<Query> subqueries) {
+    this.type = type;
+    this.subqueries = subqueries;
+  }
+
+  @Override
+  protected JSONObject buildSearchParam(SearchParam searchParam, JSONObject outer) {
+    return outer.put(type.name().toLowerCase(), type.buildSearchParam(searchParam, subqueries));
+  }
+
+  enum Type {
+    MUST,
+    MUST_NOT,
+    SHOULD,
+
+    BOOL {
+      @Override
+      public Object buildSearchParam(SearchParam searchParam, List<Query> subqueries) {
+        JSONObject outer = new JSONObject();
+        subqueries.forEach(query -> query.buildSearchParam(searchParam, outer));
+        return outer;
+      }
+    };
+
+    public Object buildSearchParam(SearchParam searchParam, List<Query> subqueries) {
+      return new JSONArray(
+          subqueries.stream()
+              .map(query -> query.buildSearchParam(searchParam, new JSONObject()))
+              .collect(Collectors.toList()));
+    }
+  }
+}

+ 30 - 0
src/main/java/io/milvus/client/dsl/InsertParam.java

@@ -0,0 +1,30 @@
+package io.milvus.client.dsl;
+
+import java.util.List;
+
+public class InsertParam {
+  private final io.milvus.client.InsertParam insertParam;
+
+  InsertParam(String collectionName) {
+    this.insertParam = io.milvus.client.InsertParam.create(collectionName);
+  }
+
+  public InsertParam withIds(List<Long> ids) {
+    insertParam.setEntityIds(ids);
+    return this;
+  }
+
+  public <T> InsertParam with(Schema.Field<T> field, List<T> data) {
+    insertParam.addField(field.name, field.dataType, data);
+    return this;
+  }
+
+  public <T> InsertParam with(Schema.VectorField<T> vectorField, List<T> data) {
+    insertParam.addVectorField(vectorField.name, vectorField.dataType, data);
+    return this;
+  }
+
+  io.milvus.client.InsertParam getInsertParam() {
+    return insertParam;
+  }
+}

+ 339 - 0
src/main/java/io/milvus/client/dsl/MilvusService.java

@@ -0,0 +1,339 @@
+package io.milvus.client.dsl;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.milvus.client.CollectionMapping;
+import io.milvus.client.CompactParam;
+import io.milvus.client.Index;
+import io.milvus.client.IndexType;
+import io.milvus.client.MetricType;
+import io.milvus.client.MilvusClient;
+import io.milvus.client.SearchParam;
+import io.milvus.client.SearchResult;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+/**
+ * A service that wraps client, collection name and schema together to simplify API calls. It is
+ * recommended to use the service if you need a schema for Milvus operations. All operations can
+ * then be called with <code>MilvusService</code> instead of <code>MilvusClient</code>.
+ */
+public class MilvusService {
+  private final MilvusClient client;
+  private final String collectionName;
+  private final Schema schema;
+
+  public MilvusService(MilvusClient client, String collectionName, Schema schema) {
+    this.client = client;
+    this.collectionName = collectionName;
+    this.schema = schema;
+  }
+
+  /**
+   * Milvus service with timeout support.
+   *
+   * @param timeout the desired timeout
+   * @param timeoutUnit unit for timeout
+   */
+  public MilvusService withTimeout(int timeout, TimeUnit timeoutUnit) {
+    return new MilvusService(client.withTimeout(timeout, timeoutUnit), collectionName, schema);
+  }
+
+  /** Close the client. Wait at most 1 minute for graceful shutdown. */
+  public void close() {
+    client.close();
+  }
+
+  /** Count entities in the current collection */
+  public long countEntities() {
+    return client.countEntities(collectionName);
+  }
+
+  /** Create collection with predefined schema. */
+  public void createCollection() {
+    createCollection("{}");
+  }
+
+  /**
+   * Create collection with predefined schema.
+   *
+   * @param paramsInJson Set optional "segment_row_limit" or "auto_id".
+   */
+  public void createCollection(String paramsInJson) {
+    client.createCollection(schema.mapToCollection(collectionName).setParamsInJson(paramsInJson));
+  }
+
+  /**
+   * Create index with schema field.
+   *
+   * <pre>
+   * example usage:
+   * <code>
+   * service.createIndex(
+   *     schema.embedding,
+   *     IndexType.IVF_FLAT,
+   *     MetricType.L2,
+   *     new JsonBuilder().param("nlist", 100).build());
+   * </code>
+   * </pre>
+   */
+  public void createIndex(
+      Schema.VectorField vectorField,
+      IndexType indexType,
+      MetricType metricType,
+      String paramsInJson) {
+    Futures.getUnchecked(createIndexAsync(vectorField, indexType, metricType, paramsInJson));
+  }
+
+  /**
+   * Create index with schema field.
+   *
+   * <pre>
+   * example usage:
+   * <code>
+   * service.createIndex(
+   *     schema.embedding,
+   *     IndexType.IVF_FLAT,
+   *     MetricType.L2,
+   *     new JsonBuilder().param("nlist", 100).build());
+   * </code>
+   * </pre>
+   */
+  public ListenableFuture<Void> createIndexAsync(
+      Schema.VectorField vectorField,
+      IndexType indexType,
+      MetricType metricType,
+      String paramsInJson) {
+    return client.createIndexAsync(
+        Index.create(collectionName, vectorField.name)
+            .setIndexType(indexType)
+            .setMetricType(metricType)
+            .setParamsInJson(paramsInJson));
+  }
+
+  /** Delete entities by IDs. */
+  public void deleteEntityByID(List<Long> ids) {
+    client.deleteEntityByID(collectionName, ids);
+  }
+
+  /** Drop the current collection. */
+  public void dropCollection() {
+    client.dropCollection(collectionName);
+  }
+
+  /** Flush the current collection. */
+  public void flush() {
+    client.flush(collectionName);
+  }
+
+  /** Flush the current collection. */
+  public ListenableFuture<Void> flushAsync() {
+    return client.flushAsync(collectionName);
+  }
+
+  /**
+   * Get entity by IDs.
+   *
+   * @param ids The ids to get
+   * @return Map of id to <code>Schema.Entity</code> value.
+   */
+  public Map<Long, Schema.Entity> getEntityByID(List<Long> ids) {
+    return getEntityByID(ids, Collections.emptyList());
+  }
+
+  /**
+   * Get entity by IDs.
+   *
+   * @param ids The ids to get
+   * @param fields The fields to return values for
+   * @return Map of id to <code>Schema.Entity</code> value.
+   */
+  public Map<Long, Schema.Entity> getEntityByID(List<Long> ids, List<Schema.Field<?>> fields) {
+    List<String> fieldNames = fields.stream().map(f -> f.name).collect(Collectors.toList());
+    return client.getEntityByID(collectionName, ids, fieldNames).entrySet().stream()
+        .collect(Collectors.toMap(e -> e.getKey(), e -> schema.new Entity(e.getValue())));
+  }
+
+  /**
+   * Check whether a collection exists
+   *
+   * @param collectionName collection to check
+   * @return true if the collection exists, false otherwise.
+   */
+  public boolean hasCollection(String collectionName) {
+    return client.hasCollection(collectionName);
+  }
+
+  /**
+   * Insert data with schema.
+   *
+   * <pre>
+   * example usage:
+   * <code>
+   * service.insert(
+   *         insertParam ->
+   *             insertParam
+   *                 .withIds(ids)
+   *                 .with(schema.int_field, int_values)
+   *                 .with(schema.embedding, embeddings));
+   * </code>
+   * </pre>
+   *
+   * @return a list of ids of the inserted entities
+   */
+  public List<Long> insert(Consumer<InsertParam> insertParamBuilder) {
+    return Futures.getUnchecked(insertAsync(insertParamBuilder));
+  }
+
+  /**
+   * Insert data with schema.
+   *
+   * <pre>
+   * example usage:
+   * <code>
+   * service.insert(
+   *         insertParam ->
+   *             insertParam
+   *                 .withIds(ids)
+   *                 .with(schema.int_field, int_values)
+   *                 .with(schema.embedding, embeddings));
+   * </code>
+   * </pre>
+   *
+   * @return a list of ids of the inserted entities
+   */
+  public ListenableFuture<List<Long>> insertAsync(Consumer<InsertParam> insertParamBuilder) {
+    InsertParam insertParam = schema.insertInto(collectionName);
+    insertParamBuilder.accept(insertParam);
+    return client.insertAsync(insertParam.getInsertParam());
+  }
+
+  /** Search with searchParam. */
+  public SearchResult search(SearchParam searchParam) {
+    return client.search(searchParam);
+  }
+
+  /** Search with searchParam. */
+  public ListenableFuture<SearchResult> searchAsync(SearchParam searchParam) {
+    return client.searchAsync(searchParam);
+  }
+
+  /**
+   * Build a SearchParam from Query.
+   *
+   * <pre>
+   * example usage:
+   * <code>
+   * SearchParam searchParam =
+   *     service.buildSearchParam(query)
+   *            .setParamsInJson("{\"fields\": [\"A\", \"B\"]}");
+   * </code>
+   * </pre>
+   */
+  public SearchParam buildSearchParam(Query query) {
+    return query.buildSearchParam(collectionName);
+  }
+
+  /**
+   * Create a partition specified by <code>tag</code>
+   *
+   * @param tag partition tag
+   */
+  public void createPartition(String tag) {
+    client.createPartition(collectionName, tag);
+  }
+
+  /**
+   * Check whether the partition exists in this collection
+   *
+   * @param tag partition tag
+   * @return true if the partition exists, false otherwise.
+   */
+  public boolean hasPartition(String tag) {
+    return client.hasPartition(collectionName, tag);
+  }
+
+  /**
+   * List current partitions in the collection
+   *
+   * @return a list of partition names
+   */
+  public List<String> listPartitions() {
+    return client.listPartitions(collectionName);
+  }
+
+  /**
+   * Drop partition in the collection specified by <code>tag</code>
+   *
+   * @param tag partition tag
+   */
+  public void dropPartition(String tag) {
+    client.dropPartition(collectionName, tag);
+  }
+
+  /** Get current collection info */
+  public CollectionMapping getCollectionInfo() {
+    return client.getCollectionInfo(collectionName);
+  }
+
+  /** List collections. */
+  public List<String> listCollections() {
+    return client.listCollections();
+  }
+
+  /** Drop index by schema. */
+  public void dropIndex(Schema.VectorField vectorField) {
+    client.dropIndex(collectionName, vectorField.name);
+  }
+
+  /** Get collection stats. */
+  public String getCollectionStats() {
+    return client.getCollectionStats(collectionName);
+  }
+
+  /**
+   * Compacts the collection, erasing deleted data from disk and rebuild index in background (if the
+   * data size after compaction is still larger than indexFileSize). Data was only soft-deleted
+   * until you call compact.
+   */
+  public void compact() {
+    client.compact(CompactParam.create(collectionName));
+  }
+
+  /**
+   * Compacts the collection, erasing deleted data from disk and rebuild index in background (if the
+   * data size after compaction is still larger than indexFileSize). Data was only soft-deleted
+   * until you call compact.
+   */
+  public ListenableFuture<Void> compactAsync() {
+    return client.compactAsync(CompactParam.create(collectionName));
+  }
+
+  /**
+   * Compacts the collection, erasing deleted data from disk and rebuild index in background (if the
+   * data size after compaction is still larger than indexFileSize). Data was only soft-deleted
+   * until you call compact.
+   *
+   * @param threshold Defaults to 0.2. Segment will compact if and only if the percentage of
+   *     entities deleted exceeds the threshold.
+   */
+  public void compact(double threshold) {
+    client.compact(CompactParam.create(collectionName).setThreshold(threshold));
+  }
+
+  /**
+   * Compacts the collection, erasing deleted data from disk and rebuild index in background (if the
+   * data size after compaction is still larger than indexFileSize). Data was only soft-deleted
+   * until you call compact.
+   *
+   * @param threshold Defaults to 0.2. Segment will compact if and only if the percentage of
+   *     entities deleted exceeds the threshold.
+   */
+  public ListenableFuture<Void> compactAsync(double threshold) {
+    return client.compactAsync(CompactParam.create(collectionName).setThreshold(threshold));
+  }
+}

+ 33 - 0
src/main/java/io/milvus/client/dsl/Query.java

@@ -0,0 +1,33 @@
+package io.milvus.client.dsl;
+
+import io.milvus.client.SearchParam;
+import java.util.Arrays;
+import org.json.JSONObject;
+
+public abstract class Query {
+
+  public static BoolQuery bool(Query... subqueries) {
+    return new BoolQuery(BoolQuery.Type.BOOL, Arrays.asList(subqueries));
+  }
+
+  public static BoolQuery must(Query... subqueries) {
+    return new BoolQuery(BoolQuery.Type.MUST, Arrays.asList(subqueries));
+  }
+
+  public static BoolQuery must_not(Query... subqueries) {
+    return new BoolQuery(BoolQuery.Type.MUST_NOT, Arrays.asList(subqueries));
+  }
+
+  public static BoolQuery should(Query... subqueries) {
+    return new BoolQuery(BoolQuery.Type.SHOULD, Arrays.asList(subqueries));
+  }
+
+  public SearchParam buildSearchParam(String collectionName) {
+    SearchParam searchParam = SearchParam.create(collectionName);
+    JSONObject json = buildSearchParam(searchParam, new JSONObject());
+    searchParam.setDsl(json);
+    return searchParam;
+  }
+
+  protected abstract JSONObject buildSearchParam(SearchParam searchParam, JSONObject json);
+}

+ 71 - 0
src/main/java/io/milvus/client/dsl/RangeQuery.java

@@ -0,0 +1,71 @@
+package io.milvus.client.dsl;
+
+import io.milvus.client.SearchParam;
+import java.util.ArrayList;
+import java.util.List;
+import org.json.JSONObject;
+
+/** Range Query */
+public class RangeQuery<T> extends Query {
+  private final Schema.Field<T> field;
+  private final List<Expr> exprs = new ArrayList<>();
+
+  RangeQuery(Schema.Field field) {
+    this.field = field;
+  }
+
+  public RangeQuery<T> gt(T value) {
+    exprs.add(new Expr(Type.GT, value));
+    return this;
+  }
+
+  public RangeQuery<T> gte(T value) {
+    exprs.add(new Expr(Type.GTE, value));
+    return this;
+  }
+
+  public RangeQuery<T> lt(T value) {
+    exprs.add(new Expr(Type.LT, value));
+    return this;
+  }
+
+  public RangeQuery<T> lte(T value) {
+    exprs.add(new Expr(Type.LTE, value));
+    return this;
+  }
+
+  @Override
+  protected JSONObject buildSearchParam(SearchParam searchParam, JSONObject outer) {
+    return outer.put("range", new JSONObject().put(field.name, buildSearchParam(exprs)));
+  }
+
+  private JSONObject buildSearchParam(List<Expr> exprs) {
+    JSONObject json = new JSONObject();
+    exprs.forEach(e -> json.put(e.type.name().toLowerCase(), e.value));
+    return json;
+  }
+
+  /**
+   * Range query types.
+   * GT: greater than
+   * GTE: greater than or equal to
+   * LT: less than
+   * LTE: less than or equal to
+   */
+  public enum Type {
+    GT,
+    GTE,
+    LT,
+    LTE
+  }
+
+  private class Expr {
+    Type type;
+    T value;
+
+    Expr(Type type, T value) {
+      this.type = type;
+      this.value = value;
+    }
+  }
+}

+ 139 - 0
src/main/java/io/milvus/client/dsl/Schema.java

@@ -0,0 +1,139 @@
+package io.milvus.client.dsl;
+
+import io.milvus.client.CollectionMapping;
+import io.milvus.client.DataType;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/** An abstract class that allows you to predefine a schema to be used. */
+public abstract class Schema {
+  private final Map<String, Field> fields = new LinkedHashMap<>();
+
+  Field<?> getField(String name) {
+    return fields.get(name);
+  }
+
+  CollectionMapping mapToCollection(String collectionName) {
+    CollectionMapping mapping = CollectionMapping.create(collectionName);
+    fields
+        .values()
+        .forEach(
+            f -> {
+              if (f instanceof ScalarField) {
+                mapping.addField(f.name, f.dataType);
+              } else if (f instanceof VectorField) {
+                mapping.addVectorField(f.name, f.dataType, ((VectorField<?>) f).dimension);
+              }
+            });
+    return mapping;
+  }
+
+  InsertParam insertInto(String collectionName) {
+    return new InsertParam(collectionName);
+  }
+
+  public class Field<T> {
+    public final String name;
+    public final DataType dataType;
+
+    private Field(String name, DataType dataType) {
+      this.name = name;
+      this.dataType = dataType;
+      if (fields.putIfAbsent(name, this) != null) {
+        throw new IllegalArgumentException("Field name conflict: " + name);
+      }
+    }
+  }
+
+  public class ScalarField<T> extends Field<T> {
+    private ScalarField(String name, DataType dataType) {
+      super(name, dataType);
+    }
+
+    public RangeQuery<T> gt(T value) {
+      return new RangeQuery<T>(this).gt(value);
+    }
+
+    public RangeQuery<T> gte(T value) {
+      return new RangeQuery<T>(this).gte(value);
+    }
+
+    public RangeQuery<T> lt(T value) {
+      return new RangeQuery<T>(this).lt(value);
+    }
+
+    public RangeQuery<T> lte(T value) {
+      return new RangeQuery<T>(this).lte(value);
+    }
+
+    @SuppressWarnings("unchecked")
+    public TermQuery<T> in(T... values) {
+      return new TermQuery<>(this, TermQuery.Type.IN, Arrays.asList(values));
+    }
+  }
+
+  public class Int32Field extends ScalarField<Integer> {
+    public Int32Field(String name) {
+      super(name, DataType.INT32);
+    }
+  }
+
+  public class Int64Field extends ScalarField<Long> {
+    public Int64Field(String name) {
+      super(name, DataType.INT64);
+    }
+  }
+
+  public class FloatField extends ScalarField<Float> {
+    public FloatField(String name) {
+      super(name, DataType.FLOAT);
+    }
+  }
+
+  public class DoubleField extends ScalarField<Double> {
+    public DoubleField(String name) {
+      super(name, DataType.DOUBLE);
+    }
+  }
+
+  public class VectorField<T> extends Field<T> {
+    public final int dimension;
+
+    private VectorField(String name, DataType dataType, int dimension) {
+      super(name, dataType);
+      this.dimension = dimension;
+    }
+
+    public VectorQuery<T> query(List<T> queries) {
+      return new VectorQuery<>(this, queries);
+    }
+  }
+
+  public class FloatVectorField extends VectorField<List<Float>> {
+    public FloatVectorField(String name, int dimension) {
+      super(name, DataType.VECTOR_FLOAT, dimension);
+    }
+  }
+
+  public class BinaryVectorField extends VectorField<ByteBuffer> {
+    public BinaryVectorField(String name, int dimension) {
+      super(name, DataType.VECTOR_BINARY, dimension);
+    }
+  }
+
+  public class Entity {
+    private final Map<String, Object> properties;
+
+    Entity(Map<String, Object> properties) {
+      this.properties = properties;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T get(Field<T> field) {
+      return (T) properties.get(field.name);
+    }
+  }
+}

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

@@ -0,0 +1,39 @@
+package io.milvus.client.dsl;
+
+import io.milvus.client.SearchParam;
+import java.util.Collection;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/** Term Query */
+public class TermQuery<T> extends Query {
+  private final Schema.Field<T> field;
+  private final Type type;
+  private final Object param;
+
+  public TermQuery(Schema.Field<T> field, Type type, Object param) {
+    this.field = field;
+    this.type = type;
+    this.param = param;
+  }
+
+  @Override
+  protected JSONObject buildSearchParam(SearchParam searchParam, JSONObject outer) {
+    return outer.put("term", new JSONObject().put(field.name, type.toJson(param)));
+  }
+
+  /**
+   * Term query types.
+   * IN: field value should belong to one of the following values
+   */
+  enum Type {
+    IN {
+      @Override
+      Object toJson(Object param) {
+        return new JSONArray((Collection<?>) param);
+      }
+    };
+
+    abstract Object toJson(Object param);
+  }
+}

+ 117 - 0
src/main/java/io/milvus/client/dsl/VectorQuery.java

@@ -0,0 +1,117 @@
+package io.milvus.client.dsl;
+
+import com.google.protobuf.UnsafeByteOperations;
+import io.milvus.client.MetricType;
+import io.milvus.client.SearchParam;
+import io.milvus.grpc.VectorParam;
+import io.milvus.grpc.VectorRecord;
+import io.milvus.grpc.VectorRowRecord;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.json.JSONObject;
+
+/** Vector query */
+public class VectorQuery<T> extends Query {
+  private final Schema.VectorField<T> field;
+  private final List<T> queries;
+  private String placeholder;
+  private int topK = 10;
+  private float boost = 1.0f;
+  private MetricType metricType;
+  private JSONObject params = new JSONObject();
+
+  VectorQuery(Schema.VectorField<T> field, List<T> queries) {
+    this.field = field;
+    this.queries = queries;
+    this.placeholder = field.name;
+    this.metricType = field instanceof Schema.FloatVectorField ? MetricType.L2 : MetricType.JACCARD;
+  }
+
+  public VectorQuery<T> placeholder(String placeholder) {
+    this.placeholder = placeholder;
+    return this;
+  }
+
+  /** topK */
+  public VectorQuery<T> top(int topK) {
+    this.topK = topK;
+    return this;
+  }
+
+  public VectorQuery<T> boost(float value) {
+    this.boost = value;
+    return this;
+  }
+
+  public VectorQuery<T> metricType(MetricType metricType) {
+    this.metricType = metricType;
+    return this;
+  }
+
+  public VectorQuery<T> param(String key, Object value) {
+    params.put(key, value);
+    return this;
+  }
+
+  public VectorQuery<T> paramsInJson(String paramsInJson) {
+    params = new JSONObject(paramsInJson);
+    return this;
+  }
+
+  @SuppressWarnings("unchecked")
+  void buildSearchParam(SearchParam searchParam) {
+    VectorRecord vectorRecord = null;
+    if (field instanceof Schema.FloatVectorField) {
+      vectorRecord =
+          VectorRecord.newBuilder()
+              .addAllRecords(
+                  ((List<List<Float>>) this.queries)
+                      .stream()
+                          .map(
+                              vector ->
+                                  VectorRowRecord.newBuilder().addAllFloatData(vector).build())
+                          .collect(Collectors.toList()))
+              .build();
+    } else if (field instanceof Schema.BinaryVectorField) {
+      vectorRecord =
+          VectorRecord.newBuilder()
+              .addAllRecords(
+                  ((List<ByteBuffer>) this.queries)
+                      .stream()
+                          .map(
+                              vector ->
+                                  VectorRowRecord.newBuilder()
+                                      .setBinaryData(UnsafeByteOperations.unsafeWrap(vector))
+                                      .build())
+                          .collect(Collectors.toList()))
+              .build();
+    }
+
+    VectorParam vectorParam =
+        VectorParam.newBuilder()
+            .setJson(
+                new JSONObject()
+                    .put(
+                        placeholder,
+                        new JSONObject()
+                            .put(
+                                field.name,
+                                new JSONObject()
+                                    .put("topk", topK)
+                                    .put("metric_type", metricType.name())
+                                    .put("boost", boost)
+                                    .put("params", params)))
+                    .toString())
+            .setRowRecord(vectorRecord)
+            .build();
+
+    searchParam.addQueries(vectorParam);
+  }
+
+  @Override
+  protected JSONObject buildSearchParam(SearchParam searchParam, JSONObject outer) {
+    buildSearchParam(searchParam);
+    return outer.put("vector", placeholder);
+  }
+}

+ 1 - 1
src/main/java/io/milvus/client/exception/InvalidDsl.java

@@ -2,7 +2,7 @@ package io.milvus.client.exception;
 
 
 /** Milvus exception where invalid DSL is passed by client as a query */
 /** Milvus exception where invalid DSL is passed by client as a query */
 public class InvalidDsl extends ClientSideMilvusException {
 public class InvalidDsl extends ClientSideMilvusException {
-  private String dsl;
+  private final String dsl;
 
 
   public InvalidDsl(String dsl, String message) {
   public InvalidDsl(String dsl, String message) {
     super(null, message);
     super(null, message);

+ 2 - 2
src/main/java/io/milvus/client/exception/MilvusException.java

@@ -2,8 +2,8 @@ package io.milvus.client.exception;
 
 
 /** General Milvus exception */
 /** General Milvus exception */
 public abstract class MilvusException extends RuntimeException {
 public abstract class MilvusException extends RuntimeException {
-  private String target;
-  private boolean fillInStackTrace;
+  private final String target;
+  private final boolean fillInStackTrace;
 
 
   MilvusException(String target, boolean fillInStackTrace) {
   MilvusException(String target, boolean fillInStackTrace) {
     this(target, fillInStackTrace, null, null);
     this(target, fillInStackTrace, null, null);

+ 2 - 2
src/main/java/io/milvus/client/exception/ServerSideMilvusException.java

@@ -5,8 +5,8 @@ import io.milvus.grpc.Status;
 
 
 /** Milvus exception from server side */
 /** Milvus exception from server side */
 public class ServerSideMilvusException extends MilvusException {
 public class ServerSideMilvusException extends MilvusException {
-  private ErrorCode errorCode;
-  private String reason;
+  private final ErrorCode errorCode;
+  private final String reason;
 
 
   public ServerSideMilvusException(String target, Status status) {
   public ServerSideMilvusException(String target, Status status) {
     super(target, false);
     super(target, false);

+ 5 - 4
src/main/java/io/milvus/client/exception/UnsupportedServerVersion.java

@@ -4,8 +4,8 @@ import io.milvus.client.MilvusClient;
 
 
 /** Milvus exception where client and server versions do not match */
 /** Milvus exception where client and server versions do not match */
 public class UnsupportedServerVersion extends ClientSideMilvusException {
 public class UnsupportedServerVersion extends ClientSideMilvusException {
-  private String expect;
-  private String actual;
+  private final String expect;
+  private final String actual;
 
 
   public UnsupportedServerVersion(String target, String expect, String actual) {
   public UnsupportedServerVersion(String target, String expect, String actual) {
     super(target);
     super(target);
@@ -15,7 +15,8 @@ public class UnsupportedServerVersion extends ClientSideMilvusException {
 
 
   @Override
   @Override
   public String getErrorMessage() {
   public String getErrorMessage() {
-    return String.format("Milvus client %s is expected to work with Milvus server %s, but the version of the connected server is %s",
+    return String.format(
+        "Milvus client %s is expected to work with Milvus server %s, but the version of the connected server is %s",
         MilvusClient.clientVersion, expect, actual);
         MilvusClient.clientVersion, expect, actual);
   }
   }
-}
+}

+ 265 - 202
src/test/java/io/milvus/client/MilvusGrpcClientTest.java

@@ -19,6 +19,11 @@
 
 
 package io.milvus.client;
 package io.milvus.client;
 
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSet;
 import io.grpc.NameResolverProvider;
 import io.grpc.NameResolverProvider;
 import io.grpc.NameResolverRegistry;
 import io.grpc.NameResolverRegistry;
@@ -28,15 +33,6 @@ import io.milvus.client.exception.ClientSideMilvusException;
 import io.milvus.client.exception.ServerSideMilvusException;
 import io.milvus.client.exception.ServerSideMilvusException;
 import io.milvus.client.exception.UnsupportedServerVersion;
 import io.milvus.client.exception.UnsupportedServerVersion;
 import io.milvus.grpc.ErrorCode;
 import io.milvus.grpc.ErrorCode;
-import org.apache.commons.lang3.RandomStringUtils;
-import org.json.JSONArray;
-import org.json.JSONObject;
-import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
-import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-
 import java.net.InetSocketAddress;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -52,23 +48,27 @@ import java.util.stream.Collectors;
 import java.util.stream.DoubleStream;
 import java.util.stream.DoubleStream;
 import java.util.stream.IntStream;
 import java.util.stream.IntStream;
 import java.util.stream.LongStream;
 import java.util.stream.LongStream;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
 
 
 @Testcontainers
 @Testcontainers
 @EnabledIfSystemProperty(named = "with-containers", matches = "true")
 @EnabledIfSystemProperty(named = "with-containers", matches = "true")
 class ContainerMilvusClientTest extends MilvusClientTest {
 class ContainerMilvusClientTest extends MilvusClientTest {
   @Container
   @Container
-  private GenericContainer milvusContainer =
-      new GenericContainer(System.getProperty("docker_image_name", "milvusdb/milvus:0.11.0-cpu-d101620-4c44c0"))
+  private static final GenericContainer milvusContainer2 =
+      new GenericContainer(
+              System.getProperty("docker_image_name", "milvusdb/milvus:0.11.0-cpu-d101620-4c44c0"))
           .withExposedPorts(19530);
           .withExposedPorts(19530);
-
   @Container
   @Container
-  private static GenericContainer milvusContainer2 =
-      new GenericContainer(System.getProperty("docker_image_name", "milvusdb/milvus:0.11.0-cpu-d101620-4c44c0"))
+  private final GenericContainer milvusContainer =
+      new GenericContainer(
+              System.getProperty("docker_image_name", "milvusdb/milvus:0.11.0-cpu-d101620-4c44c0"))
           .withExposedPorts(19530);
           .withExposedPorts(19530);
 
 
   @Override
   @Override
@@ -78,18 +78,23 @@ class ContainerMilvusClientTest extends MilvusClientTest {
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void loadBalancing() {
   void loadBalancing() {
-    NameResolverProvider testNameResolverProvider = new StaticNameResolverProvider(
-        new InetSocketAddress(milvusContainer.getHost(), milvusContainer.getFirstMappedPort()),
-        new InetSocketAddress(milvusContainer2.getHost(), milvusContainer2.getFirstMappedPort()));
+    NameResolverProvider testNameResolverProvider =
+        new StaticNameResolverProvider(
+            new InetSocketAddress(milvusContainer.getHost(), milvusContainer.getFirstMappedPort()),
+            new InetSocketAddress(
+                milvusContainer2.getHost(), milvusContainer2.getFirstMappedPort()));
 
 
     NameResolverRegistry.getDefaultRegistry().register(testNameResolverProvider);
     NameResolverRegistry.getDefaultRegistry().register(testNameResolverProvider);
 
 
-    ConnectParam connectParam = connectParamBuilder()
-        .withTarget(testNameResolverProvider.getDefaultScheme() + ":///test")
-        .build();
+    ConnectParam connectParam =
+        connectParamBuilder()
+            .withTarget(testNameResolverProvider.getDefaultScheme() + ":///test")
+            .build();
 
 
     MilvusClient loadBalancingClient = new MilvusGrpcClient(connectParam).withLogging();
     MilvusClient loadBalancingClient = new MilvusGrpcClient(connectParam).withLogging();
-    assertEquals(50, IntStream.range(0, 100)
+    assertEquals(
+        50,
+        IntStream.range(0, 100)
             .filter(i -> loadBalancingClient.hasCollection(randomCollectionName))
             .filter(i -> loadBalancingClient.hasCollection(randomCollectionName))
             .count());
             .count());
   }
   }
@@ -99,34 +104,11 @@ class ContainerMilvusClientTest extends MilvusClientTest {
 @DisabledIfSystemProperty(named = "with-containers", matches = "true")
 @DisabledIfSystemProperty(named = "with-containers", matches = "true")
 class MilvusClientTest {
 class MilvusClientTest {
 
 
-  private MilvusClient client;
-
   protected String randomCollectionName;
   protected String randomCollectionName;
+  private MilvusClient client;
   private int size;
   private int size;
   private int dimension;
   private int dimension;
 
 
-  protected ConnectParam.Builder connectParamBuilder() {
-    return connectParamBuilder("localhost", 19530);
-  }
-
-  protected ConnectParam.Builder connectParamBuilder(GenericContainer milvusContainer) {
-    return connectParamBuilder(milvusContainer.getHost(), milvusContainer.getFirstMappedPort());
-  }
-
-  protected ConnectParam.Builder connectParamBuilder(String host, int port) {
-    return new ConnectParam.Builder().withHost(host).withPort(port);
-  }
-
-  protected void assertErrorCode(ErrorCode errorCode, Runnable runnable) {
-    assertEquals(errorCode, assertThrows(ServerSideMilvusException.class, runnable::run).getErrorCode());
-  }
-
-  protected void assertGrpcStatusCode(Status.Code statusCode, Runnable runnable) {
-    ClientSideMilvusException error = assertThrows(ClientSideMilvusException.class, runnable::run);
-    assertTrue(error.getCause() instanceof StatusRuntimeException);
-    assertEquals(statusCode, ((StatusRuntimeException) error.getCause()).getStatus().getCode());
-  }
-
   // Helper function that generates random float vectors
   // Helper function that generates random float vectors
   static List<List<Float>> generateFloatVectors(int vectorCount, int dimension) {
   static List<List<Float>> generateFloatVectors(int vectorCount, int dimension) {
     SplittableRandom splittableRandom = new SplittableRandom();
     SplittableRandom splittableRandom = new SplittableRandom();
@@ -171,7 +153,8 @@ class MilvusClientTest {
             + "    \"vector\": {"
             + "    \"vector\": {"
             + "        \"float_vec\": {"
             + "        \"float_vec\": {"
             + "            \"topk\": %d, \"metric_type\": \"L2\", \"type\": \"float\", \"query\": %s, \"params\": {\"nprobe\": 20}"
             + "            \"topk\": %d, \"metric_type\": \"L2\", \"type\": \"float\", \"query\": %s, \"params\": {\"nprobe\": 20}"
-            + "    }}}]}}", topK, query);
+            + "    }}}]}}",
+        topK, query);
   }
   }
 
 
   // Helper function that generate a complex DSL statement with scalar field filtering
   // Helper function that generate a complex DSL statement with scalar field filtering
@@ -199,6 +182,29 @@ class MilvusClientTest {
         topK, query);
         topK, query);
   }
   }
 
 
+  protected ConnectParam.Builder connectParamBuilder() {
+    return connectParamBuilder("localhost", 19530);
+  }
+
+  protected ConnectParam.Builder connectParamBuilder(GenericContainer milvusContainer) {
+    return connectParamBuilder(milvusContainer.getHost(), milvusContainer.getFirstMappedPort());
+  }
+
+  protected ConnectParam.Builder connectParamBuilder(String host, int port) {
+    return new ConnectParam.Builder().withHost(host).withPort(port);
+  }
+
+  protected void assertErrorCode(ErrorCode errorCode, Runnable runnable) {
+    assertEquals(
+        errorCode, assertThrows(ServerSideMilvusException.class, runnable::run).getErrorCode());
+  }
+
+  protected void assertGrpcStatusCode(Status.Code statusCode, Runnable runnable) {
+    ClientSideMilvusException error = assertThrows(ClientSideMilvusException.class, runnable::run);
+    assertTrue(error.getCause() instanceof StatusRuntimeException);
+    assertEquals(statusCode, ((StatusRuntimeException) error.getCause()).getStatus().getCode());
+  }
+
   @org.junit.jupiter.api.BeforeEach
   @org.junit.jupiter.api.BeforeEach
   void setUp() throws Exception {
   void setUp() throws Exception {
 
 
@@ -209,15 +215,16 @@ class MilvusClientTest {
     size = 100000;
     size = 100000;
     dimension = 128;
     dimension = 128;
 
 
-    CollectionMapping collectionMapping = CollectionMapping
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64)
-        .addField("float", DataType.FLOAT)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension)
-        .setParamsInJson(new JsonBuilder()
-            .param("segment_row_limit", 50000)
-            .param("auto_id", false)
-            .build());
+    CollectionMapping collectionMapping =
+        CollectionMapping.create(randomCollectionName)
+            .addField("int64", DataType.INT64)
+            .addField("float", DataType.FLOAT)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension)
+            .setParamsInJson(
+                new JsonBuilder()
+                    .param("segment_row_limit", 50000)
+                    .param("auto_id", false)
+                    .build());
 
 
     client.createCollection(collectionMapping);
     client.createCollection(collectionMapping);
   }
   }
@@ -230,9 +237,7 @@ class MilvusClientTest {
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void idleTest() throws InterruptedException {
   void idleTest() throws InterruptedException {
-    ConnectParam connectParam = connectParamBuilder()
-        .withIdleTimeout(1, TimeUnit.SECONDS)
-        .build();
+    ConnectParam connectParam = connectParamBuilder().withIdleTimeout(1, TimeUnit.SECONDS).build();
     MilvusClient client = new MilvusGrpcClient(connectParam).withLogging();
     MilvusClient client = new MilvusGrpcClient(connectParam).withLogging();
     TimeUnit.SECONDS.sleep(2);
     TimeUnit.SECONDS.sleep(2);
     // A new RPC would take the channel out of idle mode
     // A new RPC would take the channel out of idle mode
@@ -281,8 +286,7 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void unsupportedServerVersion() {
   void unsupportedServerVersion() {
     GenericContainer unsupportedMilvusContainer =
     GenericContainer unsupportedMilvusContainer =
-        new GenericContainer("milvusdb/milvus:0.9.1-cpu-d052920-e04ed5")
-            .withExposedPorts(19530);
+        new GenericContainer("milvusdb/milvus:0.9.1-cpu-d052920-e04ed5").withExposedPorts(19530);
     try {
     try {
       unsupportedMilvusContainer.start();
       unsupportedMilvusContainer.start();
       ConnectParam connectParam = connectParamBuilder(unsupportedMilvusContainer).build();
       ConnectParam connectParam = connectParamBuilder(unsupportedMilvusContainer).build();
@@ -296,35 +300,38 @@ class MilvusClientTest {
   void grpcTimeout() {
   void grpcTimeout() {
     insert();
     insert();
     MilvusClient timeoutClient = client.withTimeout(1, TimeUnit.MILLISECONDS);
     MilvusClient timeoutClient = client.withTimeout(1, TimeUnit.MILLISECONDS);
-    Index index = Index.create(randomCollectionName, "float_vec")
-        .setIndexType(IndexType.IVF_FLAT)
-        .setMetricType(MetricType.L2)
-        .setParamsInJson(new JsonBuilder().param("nlist", 2048).build());
+    Index index =
+        Index.create(randomCollectionName, "float_vec")
+            .setIndexType(IndexType.IVF_FLAT)
+            .setMetricType(MetricType.L2)
+            .setParamsInJson(new JsonBuilder().param("nlist", 2048).build());
     assertGrpcStatusCode(Status.Code.DEADLINE_EXCEEDED, () -> timeoutClient.createIndex(index));
     assertGrpcStatusCode(Status.Code.DEADLINE_EXCEEDED, () -> timeoutClient.createIndex(index));
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void createInvalidCollection() {
   void createInvalidCollection() {
     // invalid collection name
     // invalid collection name
-    CollectionMapping invalidCollectionName = CollectionMapping
-        .create("╯°□°)╯")
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension);
+    CollectionMapping invalidCollectionName =
+        CollectionMapping.create("╯°□°)╯")
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension);
 
 
-    assertErrorCode(ErrorCode.ILLEGAL_COLLECTION_NAME, () -> client.createCollection(invalidCollectionName));
+    assertErrorCode(
+        ErrorCode.ILLEGAL_COLLECTION_NAME, () -> client.createCollection(invalidCollectionName));
 
 
     // invalid field
     // invalid field
     CollectionMapping withoutField = CollectionMapping.create("validCollectionName");
     CollectionMapping withoutField = CollectionMapping.create("validCollectionName");
     assertThrows(ClientSideMilvusException.class, () -> client.createCollection(withoutField));
     assertThrows(ClientSideMilvusException.class, () -> client.createCollection(withoutField));
 
 
     // invalid segment_row_limit
     // invalid segment_row_limit
-    CollectionMapping invalidSegmentRowCount = CollectionMapping
-        .create("validCollectionName")
-        .addField("int64", DataType.INT64)
-        .addField("float", DataType.FLOAT)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension)
-        .setParamsInJson(new JsonBuilder().param("segment_row_limit", -1000).build());
+    CollectionMapping invalidSegmentRowCount =
+        CollectionMapping.create("validCollectionName")
+            .addField("int64", DataType.INT64)
+            .addField("float", DataType.FLOAT)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension)
+            .setParamsInJson(new JsonBuilder().param("segment_row_limit", -1000).build());
 
 
-    assertErrorCode(ErrorCode.ILLEGAL_ARGUMENT, () -> client.createCollection(invalidSegmentRowCount));
+    assertErrorCode(
+        ErrorCode.ILLEGAL_ARGUMENT, () -> client.createCollection(invalidSegmentRowCount));
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -335,7 +342,8 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void dropCollection() {
   void dropCollection() {
     String nonExistingCollectionName = RandomStringUtils.randomAlphabetic(10);
     String nonExistingCollectionName = RandomStringUtils.randomAlphabetic(10);
-    assertErrorCode(ErrorCode.COLLECTION_NOT_EXISTS, () -> client.dropCollection(nonExistingCollectionName));
+    assertErrorCode(
+        ErrorCode.COLLECTION_NOT_EXISTS, () -> client.dropCollection(nonExistingCollectionName));
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -358,23 +366,23 @@ class MilvusClientTest {
     }
     }
 
 
     List<Long> entityIds1 = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> entityIds1 = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(entityIds1)
-        .setPartitionTag(tag1);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(entityIds1)
+            .setPartitionTag(tag1);
     client.insert(insertParam);
     client.insert(insertParam);
 
 
     List<Long> entityIds2 = LongStream.range(size, size * 2).boxed().collect(Collectors.toList());
     List<Long> entityIds2 = LongStream.range(size, size * 2).boxed().collect(Collectors.toList());
-    insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(entityIds2)
-        .setPartitionTag(tag2);
+    insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(entityIds2)
+            .setPartitionTag(tag2);
     client.insert(insertParam);
     client.insert(insertParam);
 
 
     client.flush(randomCollectionName);
     client.flush(randomCollectionName);
@@ -387,10 +395,10 @@ class MilvusClientTest {
     List<List<Float>> vectorsToSearch1 = vectors.subList(0, searchSize);
     List<List<Float>> vectorsToSearch1 = vectors.subList(0, searchSize);
     List<String> partitionTags1 = new ArrayList<>();
     List<String> partitionTags1 = new ArrayList<>();
     partitionTags1.add(tag1);
     partitionTags1.add(tag1);
-    SearchParam searchParam1 = SearchParam
-        .create(randomCollectionName)
-        .setDsl(generateSimpleDSL(topK, vectorsToSearch1.toString()))
-        .setPartitionTags(partitionTags1);
+    SearchParam searchParam1 =
+        SearchParam.create(randomCollectionName)
+            .setDsl(generateSimpleDSL(topK, vectorsToSearch1.toString()))
+            .setPartitionTags(partitionTags1);
     SearchResult searchResult1 = client.search(searchParam1);
     SearchResult searchResult1 = client.search(searchParam1);
     List<List<Long>> resultIdsList1 = searchResult1.getResultIdsList();
     List<List<Long>> resultIdsList1 = searchResult1.getResultIdsList();
     assertEquals(searchSize, resultIdsList1.size());
     assertEquals(searchSize, resultIdsList1.size());
@@ -399,9 +407,10 @@ class MilvusClientTest {
     List<List<Float>> vectorsToSearch2 = vectors.subList(0, searchSize);
     List<List<Float>> vectorsToSearch2 = vectors.subList(0, searchSize);
     List<String> partitionTags2 = new ArrayList<>();
     List<String> partitionTags2 = new ArrayList<>();
     partitionTags2.add(tag2);
     partitionTags2.add(tag2);
-    SearchParam searchParam2 = SearchParam.create(randomCollectionName)
-        .setDsl(generateSimpleDSL(topK, vectorsToSearch2.toString()))
-        .setPartitionTags(partitionTags2);
+    SearchParam searchParam2 =
+        SearchParam.create(randomCollectionName)
+            .setDsl(generateSimpleDSL(topK, vectorsToSearch2.toString()))
+            .setPartitionTags(partitionTags2);
     SearchResult searchResult2 = client.search(searchParam2);
     SearchResult searchResult2 = client.search(searchParam2);
     List<List<Long>> resultIdsList2 = searchResult2.getResultIdsList();
     List<List<Long>> resultIdsList2 = searchResult2.getResultIdsList();
     assertEquals(searchSize, resultIdsList2.size());
     assertEquals(searchSize, resultIdsList2.size());
@@ -423,10 +432,11 @@ class MilvusClientTest {
     insert();
     insert();
     client.flush(randomCollectionName);
     client.flush(randomCollectionName);
 
 
-    Index index = Index.create(randomCollectionName, "float_vec")
-        .setIndexType(IndexType.IVF_SQ8)
-        .setMetricType(MetricType.L2)
-        .setParamsInJson(new JsonBuilder().param("nlist", 2048).build());
+    Index index =
+        Index.create(randomCollectionName, "float_vec")
+            .setIndexType(IndexType.IVF_SQ8)
+            .setMetricType(MetricType.L2)
+            .setParamsInJson(new JsonBuilder().param("nlist", 2048).build());
 
 
     client.createIndex(index);
     client.createIndex(index);
 
 
@@ -445,12 +455,12 @@ class MilvusClientTest {
     }
     }
 
 
     List<Long> entityIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> entityIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(entityIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(entityIds);
 
 
     assertEquals(entityIds, client.insert(insertParam));
     assertEquals(entityIds, client.insert(insertParam));
   }
   }
@@ -461,22 +471,23 @@ class MilvusClientTest {
 
 
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
 
 
-    CollectionMapping collectionMapping = CollectionMapping
-        .create(binaryCollectionName)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension);
+    CollectionMapping collectionMapping =
+        CollectionMapping.create(binaryCollectionName)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension);
 
 
     client.createCollection(collectionMapping);
     client.createCollection(collectionMapping);
 
 
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
-    InsertParam insertParam = InsertParam
-        .create(binaryCollectionName)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors);
+    InsertParam insertParam =
+        InsertParam.create(binaryCollectionName)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors);
     assertEquals(size, client.insert(insertParam).size());
     assertEquals(size, client.insert(insertParam).size());
 
 
-    Index index = Index.create(binaryCollectionName, "binary_vec")
-        .setIndexType(IndexType.BIN_IVF_FLAT)
-        .setMetricType(MetricType.JACCARD)
-        .setParamsInJson(new JsonBuilder().param("nlist", 100).build());
+    Index index =
+        Index.create(binaryCollectionName, "binary_vec")
+            .setIndexType(IndexType.BIN_IVF_FLAT)
+            .setMetricType(MetricType.JACCARD)
+            .setParamsInJson(new JsonBuilder().param("nlist", 100).build());
 
 
     client.createIndex(index);
     client.createIndex(index);
 
 
@@ -498,12 +509,12 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
 
 
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(insertIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
@@ -513,11 +524,13 @@ class MilvusClientTest {
     List<List<Float>> vectorsToSearch = vectors.subList(0, searchSize);
     List<List<Float>> vectorsToSearch = vectors.subList(0, searchSize);
 
 
     final long topK = 10;
     final long topK = 10;
-    SearchParam searchParam = SearchParam
-        .create(randomCollectionName)
-        .setDsl(generateComplexDSL(topK, vectorsToSearch.toString()))
-        .setParamsInJson(new JsonBuilder().param("fields",
-            new ArrayList<>(Arrays.asList("int64", "float_vec"))).build());
+    SearchParam searchParam =
+        SearchParam.create(randomCollectionName)
+            .setDsl(generateComplexDSL(topK, vectorsToSearch.toString()))
+            .setParamsInJson(
+                new JsonBuilder()
+                    .param("fields", new ArrayList<>(Arrays.asList("int64", "float_vec")))
+                    .build());
     SearchResult searchResult = client.search(searchParam);
     SearchResult searchResult = client.search(searchParam);
     List<List<Long>> resultIdsList = searchResult.getResultIdsList();
     List<List<Long>> resultIdsList = searchResult.getResultIdsList();
     assertEquals(searchSize, resultIdsList.size());
     assertEquals(searchSize, resultIdsList.size());
@@ -541,11 +554,11 @@ class MilvusClientTest {
     final int binaryDimension = 64;
     final int binaryDimension = 64;
 
 
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
-    CollectionMapping collectionMapping = CollectionMapping
-        .create(binaryCollectionName)
-        .addField("int64", DataType.INT64)
-        .addField("float", DataType.FLOAT)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension);
+    CollectionMapping collectionMapping =
+        CollectionMapping.create(binaryCollectionName)
+            .addField("int64", DataType.INT64)
+            .addField("float", DataType.FLOAT)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension);
 
 
     client.createCollection(collectionMapping);
     client.createCollection(collectionMapping);
 
 
@@ -558,25 +571,26 @@ class MilvusClientTest {
     }
     }
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
 
 
-    InsertParam insertParam = InsertParam
-        .create(binaryCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors);
+    InsertParam insertParam =
+        InsertParam.create(binaryCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
     client.flush(binaryCollectionName);
     client.flush(binaryCollectionName);
 
 
     final int searchSize = 5;
     final int searchSize = 5;
-    List<String> vectorsToSearch = vectors.subList(0, searchSize)
-        .stream().map(byteBuffer -> Arrays.toString(byteBuffer.array()))
-        .collect(Collectors.toList());
+    List<String> vectorsToSearch =
+        vectors.subList(0, searchSize).stream()
+            .map(byteBuffer -> Arrays.toString(byteBuffer.array()))
+            .collect(Collectors.toList());
 
 
     final long topK = 10;
     final long topK = 10;
-    SearchParam searchParam = SearchParam
-        .create(binaryCollectionName)
-        .setDsl(generateComplexDSLBinary(topK, vectorsToSearch.toString()));
+    SearchParam searchParam =
+        SearchParam.create(binaryCollectionName)
+            .setDsl(generateComplexDSLBinary(topK, vectorsToSearch.toString()));
     SearchResult searchResult = client.search(searchParam);
     SearchResult searchResult = client.search(searchParam);
     List<List<Long>> resultIdsList = searchResult.getResultIdsList();
     List<List<Long>> resultIdsList = searchResult.getResultIdsList();
     assertEquals(searchSize, resultIdsList.size());
     assertEquals(searchSize, resultIdsList.size());
@@ -594,6 +608,54 @@ class MilvusClientTest {
     client.dropCollection(binaryCollectionName);
     client.dropCollection(binaryCollectionName);
   }
   }
 
 
+  @org.junit.jupiter.api.Test
+  void searchEmptyResult() {
+    List<Long> intValues = new ArrayList<>(size);
+    List<Float> floatValues = new ArrayList<>(size);
+    List<List<Float>> vectors = generateFloatVectors(size, dimension);
+    for (int i = 0; i < size; i++) {
+      intValues.add((long) i);
+      floatValues.add((float) i);
+    }
+    vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
+
+    List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
+    List<Long> entityIds = client.insert(insertParam);
+    assertEquals(size, entityIds.size());
+
+    client.flush(randomCollectionName);
+
+    final int searchSize = 5;
+    List<List<Float>> vectorsToSearch = vectors.subList(0, searchSize);
+
+    final long topK = 10;
+    final String tag = "tag";
+    List<String> partitionTags = new ArrayList<>();
+    partitionTags.add(tag);
+    SearchParam searchParam =
+        SearchParam.create(randomCollectionName)
+            .setDsl(generateComplexDSL(topK, vectorsToSearch.toString()))
+            .setPartitionTags(partitionTags)
+            .setParamsInJson(
+                new JsonBuilder()
+                    .param("fields", new ArrayList<>(Arrays.asList("int64", "float_vec")))
+                    .build());
+    SearchResult searchResult = client.search(searchParam);
+    assertEquals(searchSize, searchResult.getQueryResultsList().size());
+    List<List<Long>> resultIdsList = searchResult.getResultIdsList();
+    assertEquals(searchSize, resultIdsList.size());
+    assertEquals(0, resultIdsList.get(0).size());
+    List<List<Float>> resultDistancesList = searchResult.getResultDistancesList();
+    assertEquals(searchSize, resultDistancesList.size());
+    assertEquals(0, resultDistancesList.get(0).size());
+  }
+
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void getCollectionInfo() {
   void getCollectionInfo() {
     CollectionMapping collectionMapping = client.getCollectionInfo(randomCollectionName);
     CollectionMapping collectionMapping = client.getCollectionInfo(randomCollectionName);
@@ -607,7 +669,8 @@ class MilvusClientTest {
     }
     }
 
 
     String nonExistingCollectionName = RandomStringUtils.randomAlphabetic(10);
     String nonExistingCollectionName = RandomStringUtils.randomAlphabetic(10);
-    assertErrorCode(ErrorCode.COLLECTION_NOT_EXISTS, () -> client.getCollectionInfo(nonExistingCollectionName));
+    assertErrorCode(
+        ErrorCode.COLLECTION_NOT_EXISTS, () -> client.getCollectionInfo(nonExistingCollectionName));
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -619,12 +682,14 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void serverStatus() {
   void serverStatus() {
     JSONObject serverStatus = new JSONObject(client.getServerStatus());
     JSONObject serverStatus = new JSONObject(client.getServerStatus());
-    assertEquals(ImmutableSet.of("indexing", "require_restart", "server_time", "uptime"), serverStatus.keySet());
+    assertEquals(
+        ImmutableSet.of("indexing", "require_restart", "server_time", "uptime"),
+        serverStatus.keySet());
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void serverVersion() {
   void serverVersion() {
-    assertEquals("0.11.0", client.getServerVersion());
+    assertEquals("0.11.1", client.getServerVersion());
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -670,12 +735,12 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
 
 
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(insertIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
@@ -699,19 +764,19 @@ class MilvusClientTest {
     final int binaryDimension = 64;
     final int binaryDimension = 64;
 
 
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
     String binaryCollectionName = RandomStringUtils.randomAlphabetic(10);
-    CollectionMapping collectionMapping = CollectionMapping
-        .create(binaryCollectionName)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension)
-        .setParamsInJson(new JsonBuilder().param("auto_id", false).build());
+    CollectionMapping collectionMapping =
+        CollectionMapping.create(binaryCollectionName)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, binaryDimension)
+            .setParamsInJson(new JsonBuilder().param("auto_id", false).build());
 
 
     client.createCollection(collectionMapping);
     client.createCollection(collectionMapping);
 
 
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
     List<ByteBuffer> vectors = generateBinaryVectors(size, binaryDimension);
     List<Long> entityIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> entityIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(binaryCollectionName)
-        .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors)
-        .setEntityIds(entityIds);
+    InsertParam insertParam =
+        InsertParam.create(binaryCollectionName)
+            .addVectorField("binary_vec", DataType.VECTOR_BINARY, vectors)
+            .setEntityIds(entityIds);
     assertEquals(size, client.insert(insertParam).size());
     assertEquals(size, client.insert(insertParam).size());
 
 
     client.deleteEntityByID(binaryCollectionName, entityIds.subList(10, 20));
     client.deleteEntityByID(binaryCollectionName, entityIds.subList(10, 20));
@@ -737,11 +802,12 @@ class MilvusClientTest {
 
 
     String collectionStats = client.getCollectionStats(randomCollectionName);
     String collectionStats = client.getCollectionStats(randomCollectionName);
     JSONObject jsonInfo = new JSONObject(collectionStats);
     JSONObject jsonInfo = new JSONObject(collectionStats);
-    JSONObject segmentInfo = jsonInfo
-        .getJSONArray("partitions")
-        .getJSONObject(0)
-        .getJSONArray("segments")
-        .getJSONObject(0);
+    JSONObject segmentInfo =
+        jsonInfo
+            .getJSONArray("partitions")
+            .getJSONObject(0)
+            .getJSONArray("segments")
+            .getJSONObject(0);
 
 
     List<Long> entityIds = client.listIDInSegment(randomCollectionName, segmentInfo.getLong("id"));
     List<Long> entityIds = client.listIDInSegment(randomCollectionName, segmentInfo.getLong("id"));
     assertFalse(entityIds.isEmpty());
     assertFalse(entityIds.isEmpty());
@@ -759,12 +825,12 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
 
 
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(insertIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
@@ -797,12 +863,12 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
 
 
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(insertIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
@@ -810,10 +876,8 @@ class MilvusClientTest {
 
 
     String collectionStats = client.getCollectionStats(randomCollectionName);
     String collectionStats = client.getCollectionStats(randomCollectionName);
     JSONObject jsonInfo = new JSONObject(collectionStats);
     JSONObject jsonInfo = new JSONObject(collectionStats);
-    long previousSegmentSize = jsonInfo
-        .getJSONArray("partitions")
-        .getJSONObject(0)
-        .getLong("data_size");
+    long previousSegmentSize =
+        jsonInfo.getJSONArray("partitions").getJSONObject(0).getLong("data_size");
 
 
     client.deleteEntityByID(randomCollectionName, entityIds.subList(0, size / 2));
     client.deleteEntityByID(randomCollectionName, entityIds.subList(0, size / 2));
     client.flush(randomCollectionName);
     client.flush(randomCollectionName);
@@ -822,10 +886,8 @@ class MilvusClientTest {
     collectionStats = client.getCollectionStats(randomCollectionName);
     collectionStats = client.getCollectionStats(randomCollectionName);
 
 
     jsonInfo = new JSONObject(collectionStats);
     jsonInfo = new JSONObject(collectionStats);
-    long currentSegmentSize = jsonInfo
-        .getJSONArray("partitions")
-        .getJSONObject(0)
-        .getLong("data_size");
+    long currentSegmentSize =
+        jsonInfo.getJSONArray("partitions").getJSONObject(0).getLong("data_size");
     assertTrue(currentSegmentSize < previousSegmentSize);
     assertTrue(currentSegmentSize < previousSegmentSize);
   }
   }
 
 
@@ -841,12 +903,12 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
 
 
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
     List<Long> insertIds = LongStream.range(0, size).boxed().collect(Collectors.toList());
-    InsertParam insertParam = InsertParam
-        .create(randomCollectionName)
-        .addField("int64", DataType.INT64, intValues)
-        .addField("float", DataType.FLOAT, floatValues)
-        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
-        .setEntityIds(insertIds);
+    InsertParam insertParam =
+        InsertParam.create(randomCollectionName)
+            .addField("int64", DataType.INT64, intValues)
+            .addField("float", DataType.FLOAT, floatValues)
+            .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors)
+            .setEntityIds(insertIds);
     List<Long> entityIds = client.insert(insertParam);
     List<Long> entityIds = client.insert(insertParam);
     assertEquals(size, entityIds.size());
     assertEquals(size, entityIds.size());
 
 
@@ -869,11 +931,12 @@ class MilvusClientTest {
 
 
     collectionStats = client.getCollectionStats(randomCollectionName);
     collectionStats = client.getCollectionStats(randomCollectionName);
     jsonInfo = new JSONObject(collectionStats);
     jsonInfo = new JSONObject(collectionStats);
-    segmentInfo = jsonInfo
-        .getJSONArray("partitions")
-        .getJSONObject(0)
-        .getJSONArray("segments")
-        .getJSONObject(0);
+    segmentInfo =
+        jsonInfo
+            .getJSONArray("partitions")
+            .getJSONObject(0)
+            .getJSONArray("segments")
+            .getJSONObject(0);
 
 
     long currentSegmentSize = segmentInfo.getLong("data_size");
     long currentSegmentSize = segmentInfo.getLong("data_size");
     assertFalse(currentSegmentSize < previousSegmentSize); // threshold 0.8 > 0.5, no compact
     assertFalse(currentSegmentSize < previousSegmentSize); // threshold 0.8 > 0.5, no compact

+ 6 - 7
src/test/java/io/milvus/client/StaticNameResolverProvider.java

@@ -4,7 +4,6 @@ import io.grpc.Attributes;
 import io.grpc.EquivalentAddressGroup;
 import io.grpc.EquivalentAddressGroup;
 import io.grpc.NameResolver;
 import io.grpc.NameResolver;
 import io.grpc.NameResolverProvider;
 import io.grpc.NameResolverProvider;
-
 import java.net.SocketAddress;
 import java.net.SocketAddress;
 import java.net.URI;
 import java.net.URI;
 import java.util.Arrays;
 import java.util.Arrays;
@@ -13,7 +12,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 public class StaticNameResolverProvider extends NameResolverProvider {
 public class StaticNameResolverProvider extends NameResolverProvider {
-  private List<SocketAddress> addresses;
+  private final List<SocketAddress> addresses;
 
 
   public StaticNameResolverProvider(SocketAddress... addresses) {
   public StaticNameResolverProvider(SocketAddress... addresses) {
     this.addresses = Arrays.asList(addresses);
     this.addresses = Arrays.asList(addresses);
@@ -47,9 +46,10 @@ public class StaticNameResolverProvider extends NameResolverProvider {
 
 
       @Override
       @Override
       public void start(Listener2 listener) {
       public void start(Listener2 listener) {
-        List<EquivalentAddressGroup> addrs = addresses.stream()
-            .map(addr -> new EquivalentAddressGroup(Collections.singletonList(addr)))
-            .collect(Collectors.toList());
+        List<EquivalentAddressGroup> addrs =
+            addresses.stream()
+                .map(addr -> new EquivalentAddressGroup(Collections.singletonList(addr)))
+                .collect(Collectors.toList());
 
 
         listener.onResult(
         listener.onResult(
             ResolutionResult.newBuilder()
             ResolutionResult.newBuilder()
@@ -59,8 +59,7 @@ public class StaticNameResolverProvider extends NameResolverProvider {
       }
       }
 
 
       @Override
       @Override
-      public void shutdown() {
-      }
+      public void shutdown() {}
     };
     };
   }
   }
 }
 }

+ 333 - 0
src/test/java/io/milvus/client/dsl/SearchDslTest.java

@@ -0,0 +1,333 @@
+package io.milvus.client.dsl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.milvus.client.ConnectParam;
+import io.milvus.client.IndexType;
+import io.milvus.client.JsonBuilder;
+import io.milvus.client.MetricType;
+import io.milvus.client.MilvusClient;
+import io.milvus.client.MilvusGrpcClient;
+import io.milvus.client.SearchParam;
+import io.milvus.client.SearchResult;
+import io.milvus.client.exception.InvalidDsl;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.RandomUtils;
+import org.junit.Ignore;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+@Testcontainers
+public class SearchDslTest {
+
+  @Container
+  private final GenericContainer milvusContainer =
+      new GenericContainer(
+              System.getProperty("docker_image_name", "milvusdb/milvus:0.11.0-cpu-d101620-4c44c0"))
+          .withExposedPorts(19530);
+
+  private final TestFloatSchema floatSchema = new TestFloatSchema();
+  private final TestBinarySchema binarySchema = new TestBinarySchema();
+  private final String collectionName = "test_collection";
+  private final int size = 1000;
+
+  private ConnectParam connectParam(GenericContainer milvusContainer) {
+    return new ConnectParam.Builder()
+        .withHost(milvusContainer.getHost())
+        .withPort(milvusContainer.getFirstMappedPort())
+        .build();
+  }
+
+  private void withMilvusServiceFloat(Consumer<MilvusService> test) {
+    try (MilvusClient client = new MilvusGrpcClient(connectParam(milvusContainer))) {
+      test.accept(new MilvusService(client, collectionName, floatSchema));
+    }
+  }
+
+  private void withMilvusServiceBinary(Consumer<MilvusService> test) {
+    try (MilvusClient client = new MilvusGrpcClient(connectParam(milvusContainer))) {
+      test.accept(new MilvusService(client, collectionName, binarySchema));
+    }
+  }
+
+  private List<Float> randomFloatVector(int dimension) {
+    return Stream.generate(RandomUtils::nextFloat).limit(dimension).collect(Collectors.toList());
+  }
+
+  private List<List<Float>> randomFloatVectors(int size, int dimension) {
+    return Stream.generate(() -> randomFloatVector(dimension))
+        .limit(size)
+        .collect(Collectors.toList());
+  }
+
+  private ByteBuffer randomBinaryVector(int dimension) {
+    return ByteBuffer.wrap(RandomUtils.nextBytes(dimension / 8));
+  }
+
+  private List<ByteBuffer> randomBinaryVectors(int size, int dimension) {
+    return Stream.generate(() -> randomBinaryVector(dimension))
+        .limit(size)
+        .collect(Collectors.toList());
+  }
+
+  @Test
+  public void testCreateCollectionFloat() {
+    withMilvusServiceFloat(
+        service -> {
+          service.createCollection(new JsonBuilder().param("auto_id", false).build());
+          assertTrue(service.hasCollection(collectionName));
+        });
+  }
+
+  @Test
+  public void testCreateCollectionBinary() {
+    withMilvusServiceBinary(
+        service -> {
+          service.createCollection(new JsonBuilder().param("auto_id", false).build());
+          assertTrue(service.hasCollection(collectionName));
+        });
+  }
+
+  @Test
+  public void testInsertFloat() {
+    testCreateCollectionFloat();
+
+    withMilvusServiceFloat(
+        service -> {
+          service.insert(
+              insertParam ->
+                  insertParam
+                      .withIds(LongStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          floatSchema.intField,
+                          IntStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          floatSchema.longField,
+                          LongStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          floatSchema.floatField,
+                          IntStream.range(0, size)
+                              .boxed()
+                              .map(Number::floatValue)
+                              .collect(Collectors.toList()))
+                      .with(
+                          floatSchema.doubleField,
+                          IntStream.range(0, size)
+                              .boxed()
+                              .map(Number::doubleValue)
+                              .collect(Collectors.toList()))
+                      .with(
+                          floatSchema.floatVectorField,
+                          randomFloatVectors(size, floatSchema.floatVectorField.dimension)));
+
+          service.flush();
+
+          assertEquals(size, service.countEntities());
+        });
+  }
+
+  @Test
+  public void testInsertBinary() {
+    testCreateCollectionBinary();
+
+    withMilvusServiceBinary(
+        service -> {
+          service.insert(
+              insertParam ->
+                  insertParam
+                      .withIds(LongStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          binarySchema.intField,
+                          IntStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          binarySchema.longField,
+                          LongStream.range(0, size).boxed().collect(Collectors.toList()))
+                      .with(
+                          binarySchema.floatField,
+                          IntStream.range(0, size)
+                              .boxed()
+                              .map(Number::floatValue)
+                              .collect(Collectors.toList()))
+                      .with(
+                          binarySchema.doubleField,
+                          IntStream.range(0, size)
+                              .boxed()
+                              .map(Number::doubleValue)
+                              .collect(Collectors.toList()))
+                      .with(
+                          binarySchema.binaryVectorField,
+                          randomBinaryVectors(size, binarySchema.binaryVectorField.dimension)));
+
+          service.flush();
+
+          assertEquals(size, service.countEntities());
+        });
+  }
+
+  @Test
+  public void testCreateIndexFloat() {
+    testInsertFloat();
+
+    withMilvusServiceFloat(
+        service -> {
+          service.createIndex(
+              floatSchema.floatVectorField, IndexType.IVF_SQ8, MetricType.L2, "{\"nlist\": 256}");
+        });
+  }
+
+  @Test
+  public void testCreateIndexBinary() {
+    testInsertBinary();
+
+    withMilvusServiceBinary(
+        service -> {
+          service.createIndex(
+              binarySchema.binaryVectorField, IndexType.BIN_FLAT, MetricType.JACCARD, "{}");
+        });
+  }
+
+  @Test
+  public void testGetEntityByIdFloat() {
+    withMilvusServiceFloat(
+        service -> {
+          testInsertFloat();
+
+          Map<Long, Schema.Entity> entities =
+              service.getEntityByID(
+                  LongStream.range(0, 10).boxed().collect(Collectors.toList()),
+                  Arrays.asList(floatSchema.intField, floatSchema.longField));
+
+          LongStream.range(0, 10)
+              .forEach(
+                  i -> {
+                    assertEquals((int) i, entities.get(i).get(floatSchema.intField));
+                    assertEquals(i, entities.get(i).get(floatSchema.longField));
+                  });
+        });
+  }
+
+  @Test
+  public void testGetEntityByIdBinary() {
+    withMilvusServiceBinary(
+        service -> {
+          testInsertBinary();
+
+          Map<Long, Schema.Entity> entities =
+              service.getEntityByID(
+                  LongStream.range(0, 10).boxed().collect(Collectors.toList()),
+                  Arrays.asList(binarySchema.intField, binarySchema.longField));
+
+          LongStream.range(0, 10)
+              .forEach(
+                  i -> {
+                    assertEquals((int) i, entities.get(i).get(binarySchema.intField));
+                    assertEquals(i, entities.get(i).get(binarySchema.longField));
+                  });
+        });
+  }
+
+  @Test
+  public void testFloatVectorQuery() {
+    withMilvusServiceFloat(
+        service -> {
+          testCreateIndexFloat();
+
+          List<Long> entityIds = LongStream.range(0, 10).boxed().collect(Collectors.toList());
+
+          Map<Long, Schema.Entity> entities = service.getEntityByID(entityIds);
+
+          List<List<Float>> vectors =
+              entities.values().stream()
+                  .map(e -> e.get(floatSchema.floatVectorField))
+                  .collect(Collectors.toList());
+
+          Query query =
+              Query.bool(
+                  Query.must(
+                      floatSchema.floatVectorField.query(vectors).param("nprobe", 16).top(1)));
+
+          SearchParam searchParam =
+              service
+                  .buildSearchParam(query)
+                  .setParamsInJson(
+                      new JsonBuilder()
+                          .param("fields", Arrays.asList("int64", "float_vec"))
+                          .build());
+
+          SearchResult searchResult = service.search(searchParam);
+          assertEquals(
+              entityIds,
+              searchResult.getResultIdsList().stream()
+                  .map(ids -> ids.get(0))
+                  .collect(Collectors.toList()));
+        });
+  }
+
+  @Test
+  public void testBinaryVectorQuery() {
+    withMilvusServiceBinary(
+        service -> {
+          testCreateIndexBinary();
+
+          List<Long> entityIds = LongStream.range(0, 10).boxed().collect(Collectors.toList());
+
+          Map<Long, Schema.Entity> entities = service.getEntityByID(entityIds);
+
+          List<ByteBuffer> vectors =
+              entities.values().stream()
+                  .map(e -> e.get(binarySchema.binaryVectorField))
+                  .collect(Collectors.toList());
+
+          Query query =
+              Query.bool(Query.must(binarySchema.binaryVectorField.query(vectors).top(1)));
+
+          SearchParam searchParam = service.buildSearchParam(query);
+
+          SearchResult searchResult = service.search(searchParam);
+          assertEquals(
+              entityIds,
+              searchResult.getResultIdsList().stream()
+                  .map(ids -> ids.get(0))
+                  .collect(Collectors.toList()));
+        });
+  }
+
+  @Test
+  @Ignore
+  public void testMultipleVectorsQuery() {
+    withMilvusServiceFloat(
+        service -> {
+          testCreateIndexFloat();
+
+          List<Long> entityIds = LongStream.range(0, 10).boxed().collect(Collectors.toList());
+
+          Map<Long, Schema.Entity> entities = service.getEntityByID(entityIds);
+
+          List<List<Float>> vectors =
+              entities.values().stream()
+                  .map(e -> e.get(floatSchema.floatVectorField))
+                  .collect(Collectors.toList());
+
+          Query query =
+              Query.bool(
+                  Query.must(
+                      floatSchema.floatVectorField.query(vectors).param("nprobe", 16).top(1),
+                      floatSchema.floatVectorField.query(vectors).param("nprobe", 16).top(1)));
+
+          assertThrows(InvalidDsl.class, () -> service.buildSearchParam(query));
+        });
+  }
+}

+ 9 - 0
src/test/java/io/milvus/client/dsl/TestBinarySchema.java

@@ -0,0 +1,9 @@
+package io.milvus.client.dsl;
+
+public class TestBinarySchema extends Schema {
+  public final Int32Field intField = new Int32Field("int32");
+  public final Int64Field longField = new Int64Field("int64");
+  public final FloatField floatField = new FloatField("float");
+  public final DoubleField doubleField = new DoubleField("double");
+  public final BinaryVectorField binaryVectorField = new BinaryVectorField("binary_vec", 64);
+}

+ 9 - 0
src/test/java/io/milvus/client/dsl/TestFloatSchema.java

@@ -0,0 +1,9 @@
+package io.milvus.client.dsl;
+
+public class TestFloatSchema extends Schema {
+  public final Int32Field intField = new Int32Field("int32");
+  public final Int64Field longField = new Int64Field("int64");
+  public final FloatField floatField = new FloatField("float");
+  public final DoubleField doubleField = new DoubleField("double");
+  public final FloatVectorField floatVectorField = new FloatVectorField("float_vec", 64);
+}