Browse Source

Update to v0.9.0

Signed-off-by: sahuang <xiaohai.xu@zilliz.com>
sahuang 4 years ago
parent
commit
c39fd0a7ed
40 changed files with 2190 additions and 3247 deletions
  1. 12 1
      CHANGELOG.md
  2. 11 8
      README.md
  3. 3 8
      examples/pom.xml
  4. 119 120
      examples/src/main/java/MilvusClientExample.java
  5. 31 59
      pom.xml
  6. 102 66
      src/main/java/io/milvus/client/CollectionMapping.java
  7. 19 4
      src/main/java/io/milvus/client/CompactParam.java
  8. 2 14
      src/main/java/io/milvus/client/ConnectParam.java
  9. 0 54
      src/main/java/io/milvus/client/CountEntitiesResponse.java
  10. 29 19
      src/main/java/io/milvus/client/DataType.java
  11. 0 64
      src/main/java/io/milvus/client/GetCollectionInfoResponse.java
  12. 0 41
      src/main/java/io/milvus/client/GetEntityByIDResponse.java
  13. 0 61
      src/main/java/io/milvus/client/GetIndexInfoResponse.java
  14. 0 53
      src/main/java/io/milvus/client/HasCollectionResponse.java
  15. 0 53
      src/main/java/io/milvus/client/HasPartitionResponse.java
  16. 69 75
      src/main/java/io/milvus/client/Index.java
  17. 15 54
      src/main/java/io/milvus/client/IndexType.java
  18. 70 91
      src/main/java/io/milvus/client/InsertParam.java
  19. 20 25
      src/main/java/io/milvus/client/JsonBuilder.java
  20. 0 57
      src/main/java/io/milvus/client/ListCollectionsResponse.java
  21. 0 30
      src/main/java/io/milvus/client/ListIDInSegmentResponse.java
  22. 80 0
      src/main/java/io/milvus/client/LoggingAdapter.java
  23. 7 60
      src/main/java/io/milvus/client/MetricType.java
  24. 139 177
      src/main/java/io/milvus/client/MilvusClient.java
  25. 382 1136
      src/main/java/io/milvus/client/MilvusGrpcClient.java
  26. 0 115
      src/main/java/io/milvus/client/Response.java
  27. 125 122
      src/main/java/io/milvus/client/SearchParam.java
  28. 0 137
      src/main/java/io/milvus/client/SearchResponse.java
  29. 72 0
      src/main/java/io/milvus/client/SearchResult.java
  30. 16 0
      src/main/java/io/milvus/client/exception/ClientSideMilvusException.java
  31. 0 16
      src/main/java/io/milvus/client/exception/InitializationException.java
  32. 15 0
      src/main/java/io/milvus/client/exception/InvalidDsl.java
  33. 14 8
      src/main/java/io/milvus/client/exception/MilvusException.java
  34. 33 0
      src/main/java/io/milvus/client/exception/ServerSideMilvusException.java
  35. 7 0
      src/main/java/io/milvus/client/exception/UnsupportedDataType.java
  36. 7 9
      src/main/java/io/milvus/client/exception/UnsupportedServerVersion.java
  37. 300 84
      src/main/proto/milvus.proto
  38. 490 423
      src/test/java/io/milvus/client/MilvusGrpcClientTest.java
  39. 1 3
      src/test/java/io/milvus/client/StaticNameResolverProvider.java
  40. 0 0
      src/test/resources/log4j2-test.xml

+ 12 - 1
CHANGELOG.md

@@ -1,16 +1,27 @@
 # Changelog   
 
+## milvus-sdk-java 0.9.0 (2020-10-16)
+
+### Feature
+
+- \#2976 Scalar-field filtering support
+
+### Improvement
+
+- \#134 - Simplify the client code
+
 ## milvus-sdk-java 0.8.5 (2020-08-26)
 
 ### Feature
 
 - \#128 - GRPC timeout support
+- \#129 - Support GRPC name resolver and load balancing
 
 ## milvus-sdk-java 0.8.3 (2020-07-15)
 
 ### Improvement
 
-- \#117 - Remove isConnect() API
+- \#118 - Remove isConnect() API
 
 ## milvus-sdk-java 0.8.0 (2020-05-15)
 

+ 11 - 8
README.md

@@ -15,7 +15,7 @@ The following table shows compatibilities between Milvus and Java SDK.
 
 | Milvus version | Java SDK version |
 | :------------: | :--------------: |
-|     0.10.x     |    0.8.5         |
+|     0.11.0     |    0.9.0         |
 |     0.10.2     |    0.8.4         |
 |     0.10.1     |    0.8.3         |
 |     0.10.0     |    0.8.2         |
@@ -37,14 +37,14 @@ You can use **Apache Maven** or **Gradle**/**Grails** to download the SDK.
         <dependency>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>0.8.5</version>
+            <version>0.9.0</version>
         </dependency>
        ```
 
    - Gradle/Grails
 
         ```gradle
-        compile 'io.milvus:milvus-sdk-java:0.8.5'
+        compile 'io.milvus:milvus-sdk-java:0.9.0'
         ```
 
 ### Examples
@@ -57,11 +57,13 @@ Please refer to [examples](https://github.com/milvus-io/milvus-sdk-java/tree/mas
 
 ### Troubleshooting
 
-- If you encounter the following error when running your application:
+- If you encounter the following error when running `MilvusClientExample.java`:
     ```
-    Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
+    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
+    SLF4J: Defaulting to no-operation (NOP) logger implementation
+    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
     ```
-  This is because SLF4J jar files need to be added into your application's classpath. SLF4J is required 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.
                                                                                                          
@@ -70,13 +72,14 @@ Please refer to [examples](https://github.com/milvus-io/milvus-sdk-java/tree/mas
         ```xml
          <dependency>
              <groupId>org.slf4j</groupId>
-             <artifactId>slf4j-api</artifactId>
+             <artifactId>slf4j-simple</artifactId>
              <version>1.7.30</version>
+             <scope>test</scope>
          </dependency>
         ```
     
     - Gradle/Grails
     
          ```gradle
-         compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'
+         test compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.30'
          ```

+ 3 - 8
examples/pom.xml

@@ -25,7 +25,7 @@
 
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java-examples</artifactId>
-    <version>0.8.5</version>
+    <version>0.9.0</version>
     <build>
         <plugins>
             <plugin>
@@ -63,16 +63,11 @@
         <dependency>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>0.8.5</version>
-        </dependency>
-        <dependency>
-            <groupId>com.google.code.gson</groupId>
-            <artifactId>gson</artifactId>
-            <version>2.8.6</version>
+            <version>0.9.0</version>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
+            <artifactId>slf4j-simple</artifactId>
             <version>1.7.30</version>
         </dependency>
     </dependencies>

+ 119 - 120
examples/src/main/java/MilvusClientExample.java

@@ -17,29 +17,31 @@
  * under the License.
  */
 
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import com.google.gson.JsonObject;
 import io.milvus.client.*;
+
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.SplittableRandom;
-import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 import java.util.stream.DoubleStream;
+import java.util.stream.LongStream;
 
-// This is a simple example demonstrating how to use Milvus Java SDK.
-// For detailed API document, please refer to
+// This is a simple example demonstrating how to use Milvus Java SDK v0.9.0.
+// For detailed API documentation, please refer to
 // 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/
+// You can also find more information on https://milvus.io/docs/overview.md
 public class MilvusClientExample {
 
   // Helper function that generates random vectors
   static List<List<Float>> generateVectors(int vectorCount, int dimension) {
-    SplittableRandom splitcollectionRandom = new SplittableRandom();
+    SplittableRandom splitCollectionRandom = new SplittableRandom();
     List<List<Float>> vectors = new ArrayList<>(vectorCount);
     for (int i = 0; i < vectorCount; ++i) {
-      splitcollectionRandom = splitcollectionRandom.split();
-      DoubleStream doubleStream = splitcollectionRandom.doubles(dimension);
+      splitCollectionRandom = splitCollectionRandom.split();
+      DoubleStream doubleStream = splitCollectionRandom.doubles(dimension);
       List<Float> vector =
           doubleStream.boxed().map(Double::floatValue).collect(Collectors.toList());
       vectors.add(vector);
@@ -47,8 +49,7 @@ public class MilvusClientExample {
     return vectors;
   }
 
-  // Helper function that normalizes a vector if you are using IP (Inner Product) as your metric
-  // type
+  // Helper function that normalizes a vector if you are using IP (Inner Product) as your metric type
   static List<Float> normalizeVector(List<Float> vector) {
     float squareSum = vector.stream().map(x -> x * x).reduce((float) 0, Float::sum);
     final float norm = (float) Math.sqrt(squareSum);
@@ -56,158 +57,156 @@ public class MilvusClientExample {
     return vector;
   }
 
-  public static void main(String[] args) throws InterruptedException {
-
-    // You may need to change the following to the host and port of your Milvus server
-    String host = "localhost";
-    int port = 19530;
-    if (args.length >= 2) {
-      host = args[0];
-      port = Integer.parseInt(args[1]);
+  public static void main(String[] args) {
+    try {
+      ConnectParam connectParam = new ConnectParam.Builder().build();
+      run(connectParam);
+    } catch (Exception e) {
+      e.printStackTrace();
     }
+  }
 
-    ConnectParam connectParam = new ConnectParam.Builder().withHost(host).withPort(port).build();
-    MilvusClient client = new MilvusGrpcClient(connectParam);
+  public static void run(ConnectParam connectParam) {
+    // Create Milvus client
+    MilvusClient client = new MilvusGrpcClient(connectParam).withLogging();
 
     // Create a collection with the following collection mapping
-    final String collectionName = "example"; // collection name
+    final String collectionName = "example_collection"; // collection name
     final int dimension = 128; // dimension of each vector
-    final int indexFileSize = 1024; // maximum size (in MB) of each index file
-    final MetricType metricType = MetricType.IP; // we choose IP (Inner Product) as our metric type
-    CollectionMapping collectionMapping =
-        new CollectionMapping.Builder(collectionName, dimension)
-            .withIndexFileSize(indexFileSize)
-            .withMetricType(metricType)
-            .build();
-    Response createCollectionResponse = client.createCollection(collectionMapping);
-
-    // Check whether the collection exists
-    HasCollectionResponse hasCollectionResponse = client.hasCollection(collectionName);
+    // we choose IP (Inner Product) as our metric type
+    CollectionMapping collectionMapping = CollectionMapping
+        .create(collectionName)
+        .addField("int64", DataType.INT64)
+        .addField("float", DataType.FLOAT)
+        .addVectorField("float_vec", DataType.VECTOR_FLOAT, dimension)
+        .setParamsInJson("{\"segment_row_limit\": 50000, \"auto_id\": true}");
+
+    client.createCollection(collectionMapping);
+
+    if (!client.hasCollection(collectionName)) {
+      throw new AssertionError("Collection not found");
+    }
+
+    System.out.println(collectionMapping.toString());
 
     // Get collection info
-    GetCollectionInfoResponse getCollectionInfoResponse = client.getCollectionInfo(collectionName);
+    CollectionMapping collectionInfo = client.getCollectionInfo(collectionName);
 
-    // Insert randomly generated vectors to collection
+    // Insert randomly generated field values to collection
     final int vectorCount = 100000;
-    List<List<Float>> vectors = generateVectors(vectorCount, dimension);
-    vectors =
-        vectors.stream().map(MilvusClientExample::normalizeVector).collect(Collectors.toList());
-    InsertParam insertParam =
-        new InsertParam.Builder(collectionName).withFloatVectors(vectors).build();
-    InsertResponse insertResponse = client.insert(insertParam);
-    // Insert returns a list of vector ids that you will be using (if you did not supply them
-    // yourself) to reference the vectors you just inserted
-    List<Long> vectorIds = insertResponse.getVectorIds();
+
+    List<Long> longValues = LongStream.range(0, vectorCount).boxed().collect(Collectors.toList());
+    List<Float> floatValues = LongStream.range(0, vectorCount).boxed().map(Long::floatValue).collect(Collectors.toList());
+    List<List<Float>> vectors = generateVectors(vectorCount, dimension).stream()
+        .map(MilvusClientExample::normalizeVector)
+        .collect(Collectors.toList());
+
+    InsertParam insertParam = InsertParam
+        .create(collectionName)
+        .addField("int64", DataType.INT64, longValues)
+        .addField("float", DataType.FLOAT, floatValues)
+        .addVectorField("float_vec", DataType.VECTOR_FLOAT, vectors);
+
+    // Insert returns a list of entity ids that you will be using (if you did not supply them
+    // yourself) to reference the entities you just inserted
+    List<Long> vectorIds = client.insert(insertParam);
 
     // Flush data in collection
-    Response flushResponse = client.flush(collectionName);
+    client.flush(collectionName);
 
     // Get current entity count of collection
-    CountEntitiesResponse ountEntitiesResponse = client.countEntities(collectionName);
+    long entityCount = client.countEntities(collectionName);
 
     // Create index for the collection
-    // We choose IVF_SQ8 as our index type here. Refer to IndexType javadoc for a
-    // complete explanation of different index types
-    final IndexType indexType = IndexType.IVF_SQ8;
-    // Each index type has its optional parameters you can set. Refer to the Milvus documentation
-    // for how to set the optimal parameters based on your needs.
-    JsonObject indexParamsJson = new JsonObject();
-    indexParamsJson.addProperty("nlist", 16384);
-    Index index =
-        new Index.Builder(collectionName, indexType)
-            .withParamsInJson(indexParamsJson.toString())
-            .build();
-    Response createIndexResponse = client.createIndex(index);
-
-    // Get index info for your collection
-    GetIndexInfoResponse getIndexInfoResponse = client.getIndexInfo(collectionName);
-    System.out.format("Index Info: %s\n", getIndexInfoResponse.getIndex().get().toString());
+    // We choose IVF_SQ8 as our index type here. Refer to Milvus documentation for a
+    // complete explanation of different index types and their relative parameters.
+    Index index = Index
+        .create(collectionName, "float_vec")
+        .setIndexType(IndexType.IVF_SQ8)
+        .setMetricType(MetricType.L2)
+        .setParamsInJson(new JsonBuilder().param("nlist", 2048).build());
+
+    client.createIndex(index);
 
     // Get collection info
-    Response getCollectionStatsResponse = client.getCollectionStats(collectionName);
-    if (getCollectionStatsResponse.ok()) {
-      // Collection info is sent back with JSON type string
-      String jsonString = getCollectionStatsResponse.getMessage();
-      System.out.format("Collection Stats: %s\n", jsonString);
-    }
+    String collectionStats = client.getCollectionStats(collectionName);
+    System.out.format("Collection Stats: %s\n", collectionStats);
 
     // Check whether a partition exists in collection
     // Obviously we do not have partition "tag" now
-    HasPartitionResponse testHasPartition = client.hasPartition(collectionName, "tag");
-    if (testHasPartition.ok() && testHasPartition.hasPartition()) {
-      throw new AssertionError("Wrong results!");
+    if (client.hasPartition(collectionName, "tag")) {
+      throw new AssertionError("Unexpected partition found!");
     }
 
-    // Search vectors
-    // Searching the first 5 vectors of the vectors we just inserted
+    // Search entities using DSL statement.
+    // Searching the first 5 entities we just inserted by including them in DSL.
     final int searchBatchSize = 5;
     List<List<Float>> vectorsToSearch = vectors.subList(0, searchBatchSize);
     final long topK = 10;
     // Based on the index you created, the available search parameters will be different. Refer to
     // the Milvus documentation for how to set the optimal parameters based on your needs.
-    JsonObject searchParamsJson = new JsonObject();
-    searchParamsJson.addProperty("nprobe", 20);
-    SearchParam searchParam =
-        new SearchParam.Builder(collectionName)
-            .withFloatVectors(vectorsToSearch)
-            .withTopK(topK)
-            .withParamsInJson(searchParamsJson.toString())
-            .build();
-    SearchResponse searchResponse = client.search(searchParam);
-    if (searchResponse.ok()) {
-      List<List<SearchResponse.QueryResult>> queryResultsList =
-          searchResponse.getQueryResultsList();
-      final double epsilon = 0.001;
-      for (int i = 0; i < searchBatchSize; i++) {
-        // Since we are searching for vector that is already present in the collection,
-        // the first result vector should be itself and the distance (inner product) should be
-        // very close to 1 (some precision is lost during the process)
-        SearchResponse.QueryResult firstQueryResult = queryResultsList.get(i).get(0);
-        if (firstQueryResult.getVectorId() != vectorIds.get(i)
-            || Math.abs(1 - firstQueryResult.getDistance()) > epsilon) {
-          throw new AssertionError("Wrong results!");
-        }
+    String dsl = String.format(
+        "{\"bool\": {"
+            + "\"must\": [{"
+            + "    \"range\": {"
+            + "        \"float\": {\"GT\": -10, \"LT\": 100}"
+            + "    }},{"
+            + "    \"vector\": {"
+            + "        \"float_vec\": {"
+            + "            \"topk\": %d, \"metric_type\": \"IP\", \"type\": \"float\", \"query\": "
+            + "%s, \"params\": {\"nprobe\": 50}"
+            + "    }}}]}}",
+        topK, vectorsToSearch.toString());
+    SearchParam searchParam = SearchParam
+        .create(collectionName)
+        .setDsl(dsl)
+        .setParamsInJson("{\"fields\": [\"int64\", \"float\"]}");
+    SearchResult searchResult = client.search(searchParam);
+    List<List<SearchResult.QueryResult>> queryResultsList = searchResult.getQueryResultsList();
+    final double epsilon = 0.01;
+    for (int i = 0; i < searchBatchSize; i++) {
+      // Since we are searching for vector that is already present in the collection,
+      // the first result vector should be itself and the distance (inner product) should be
+      // very close to 1 (some precision is lost during the process)
+      SearchResult.QueryResult firstQueryResult = queryResultsList.get(i).get(0);
+      if (firstQueryResult.getEntityId() != vectorIds.get(i)
+          || Math.abs(1 - firstQueryResult.getDistance()) > epsilon) {
+        throw new AssertionError("Wrong results!");
       }
     }
+
     // You can also get result ids and distances separately
-    List<List<Long>> resultIds = searchResponse.getResultIdsList();
-    List<List<Float>> resultDistances = searchResponse.getResultDistancesList();
+    List<List<Long>> resultIds = searchResult.getResultIdsList();
+    List<List<Float>> resultDistances = searchResult.getResultDistancesList();
 
     // You can send search request asynchronously, which returns a ListenableFuture object
-    ListenableFuture<SearchResponse> searchResponseFuture = client.searchAsync(searchParam);
-    try {
-      // Get search response immediately. Obviously you will want to do more complicated stuff with
-      // ListenableFuture
-      searchResponseFuture.get();
-    } catch (ExecutionException e) {
-      e.printStackTrace();
-    }
-
-    // Delete the first 5 vectors you just searched
-    Response deleteByIdsResponse =
-        client.deleteEntityByID(collectionName, vectorIds.subList(0, searchBatchSize));
-    flushResponse = client.flush(collectionName);
-
-    // Try to get the corresponding vector of the first id you just deleted.
-    GetEntityByIDResponse getEntityByIDResponse =
-        client.getEntityByID(collectionName, vectorIds.subList(0, searchBatchSize));
-    // Obviously you won't get anything
-    if (!getEntityByIDResponse.getFloatVectors().get(0).isEmpty()) {
-      throw new AssertionError("This can never happen!");
+    ListenableFuture<SearchResult> searchResponseFuture = client.searchAsync(searchParam);
+    // Get search response immediately. Obviously you will want to do more complicated stuff with
+    // ListenableFuture
+    Futures.getUnchecked(searchResponseFuture);
+
+    // Delete the first 5 entities you just searched
+    client.deleteEntityByID(collectionName, vectorIds.subList(0, searchBatchSize));
+    client.flush(collectionName);
+
+    // After deleting them, we call getEntityByID and obviously all 5 entities should not be returned.
+    Map<Long, Map<String, Object>> entities = client.getEntityByID(collectionName, vectorIds.subList(0, searchBatchSize));
+    if (!entities.isEmpty()) {
+      throw new AssertionError("Unexpected entity count!");
     }
 
     // Compact the collection, erase 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.
-    Response compactResponse = client.compact(collectionName);
+    client.compact(CompactParam.create(collectionName).setThreshold(0.2));
 
     // Drop index for the collection
-    Response dropIndexResponse = client.dropIndex(collectionName);
+    client.dropIndex(collectionName, "float_vec");
 
     // Drop collection
-    Response dropCollectionResponse = client.dropCollection(collectionName);
+    client.dropCollection(collectionName);
 
-    // Disconnect from Milvus server
+    // Close connection
     client.close();
   }
 }

+ 31 - 59
pom.xml

@@ -25,7 +25,7 @@
 
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java</artifactId>
-    <version>0.8.5</version>
+    <version>0.9.0</version>
     <packaging>jar</packaging>
 
     <name>io.milvus:milvus-sdk-java</name>
@@ -71,6 +71,7 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <grpc.version>1.30.2</grpc.version>
+        <testcontainers.version>1.14.3</testcontainers.version>
         <protobuf.version>3.11.0</protobuf.version>
         <protoc.version>3.11.0</protoc.version>
         <maven.compiler.source>1.8</maven.compiler.source>
@@ -90,63 +91,21 @@
     </dependencyManagement>
 
     <dependencies>
-        <dependency>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-gpg-plugin</artifactId>
-            <version>1.6</version>
-        </dependency>
-        <dependency>
-            <groupId>io.grpc</groupId>
-            <artifactId>grpc-netty-shaded</artifactId>
-            <version>1.30.2</version>
-            <scope>runtime</scope>
-        </dependency>
         <dependency>
             <groupId>io.grpc</groupId>
             <artifactId>grpc-protobuf</artifactId>
-            <version>1.30.2</version>
+            <version>${grpc.version}</version>
         </dependency>
         <dependency>
             <groupId>io.grpc</groupId>
             <artifactId>grpc-stub</artifactId>
-            <version>1.30.2</version>
-        </dependency>
-        <dependency>
-            <groupId>javax.annotation</groupId>
-            <artifactId>javax.annotation-api</artifactId>
-            <version>1.2</version>
-            <scope>provided</scope> <!-- not needed at runtime -->
+            <version>${grpc.version}</version>
         </dependency>
         <dependency>
             <groupId>io.grpc</groupId>
-            <artifactId>grpc-testing</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.google.protobuf</groupId>
-            <artifactId>protobuf-java-util</artifactId>
-            <version>${protobuf.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>com.google.errorprone</groupId>
-            <artifactId>error_prone_annotations</artifactId>
-            <version>2.3.4</version> <!-- prefer to use 2.3.3 or later -->
-        </dependency>
-        <dependency>
-            <groupId>org.junit.jupiter</groupId>
-            <artifactId>junit-jupiter</artifactId>
-            <version>5.6.2</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-text</artifactId>
-            <version>1.6</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-collections4</artifactId>
-            <version>4.4</version>
+            <artifactId>grpc-netty-shaded</artifactId>
+            <version>${grpc.version}</version>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.json</groupId>
@@ -159,20 +118,38 @@
             <version>1.7.30</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-slf4j-impl</artifactId>
-            <version>2.12.1</version>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.10</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>5.6.2</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>testcontainers</artifactId>
-            <version>1.14.3</version>
+            <version>${testcontainers.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>junit-jupiter</artifactId>
-            <version>1.14.3</version>
+            <version>${testcontainers.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>2.12.1</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -311,13 +288,8 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.19.1</version>
+                <version>2.22.2</version>
                 <dependencies>
-                    <dependency>
-                        <groupId>org.junit.platform</groupId>
-                        <artifactId>junit-platform-surefire-provider</artifactId>
-                        <version>1.1.0</version>
-                    </dependency>
                     <dependency>
                         <groupId>org.junit.jupiter</groupId>
                         <artifactId>junit-jupiter-engine</artifactId>

+ 102 - 66
src/main/java/io/milvus/client/CollectionMapping.java

@@ -19,90 +19,126 @@
 
 package io.milvus.client;
 
-import javax.annotation.Nonnull;
+import com.google.common.collect.ImmutableMap;
+import io.milvus.grpc.FieldParam;
+import io.milvus.grpc.KeyValuePair;
+import io.milvus.grpc.Mapping;
+import org.json.JSONObject;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /** Represents a collection mapping */
-// Builder Pattern
 public class CollectionMapping {
-  private final String collectionName;
-  private final long dimension;
-  private final long indexFileSize;
-  private final MetricType metricType;
-
-  private CollectionMapping(@Nonnull Builder builder) {
-    collectionName = builder.collectionName;
-    dimension = builder.dimension;
-    indexFileSize = builder.indexFileSize;
-    metricType = builder.metricType;
+  private final Mapping.Builder builder;
+
+  public static CollectionMapping create(String collectionName) {
+    return new CollectionMapping(collectionName);
   }
 
-  public String getCollectionName() {
-    return collectionName;
+  CollectionMapping(Mapping mapping) {
+    this.builder = mapping.toBuilder();
   }
 
-  public long getDimension() {
-    return dimension;
+  private CollectionMapping(String collectionName) {
+    this.builder = Mapping.newBuilder();
+    builder.setCollectionName(collectionName);
   }
 
-  public long getIndexFileSize() {
-    return indexFileSize;
+  /**
+   * add a scalar field
+   *
+   * @param name the field name
+   * @param type the field data type
+   * @return this CollectionMapping
+   */
+  public CollectionMapping addField(String name, DataType type) {
+    builder.addFields(FieldParam.newBuilder().setName(name).setTypeValue(type.getVal()).build());
+    return this;
   }
 
-  public MetricType getMetricType() {
-    return metricType;
+  /**
+   * add a vector field
+   *
+   * @param name the field name
+   * @param type the field data type
+   * @param dimension the vector dimension
+   * @return this CollectionMapping
+   */
+  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();
+    builder.addFields(field);
+    return this;
   }
 
-  @Override
-  public String toString() {
-    return String.format(
-        "CollectionMapping = {collectionName = %s, dimension = %d, indexFileSize = %d, metricType = %s}",
-        collectionName, dimension, indexFileSize, metricType.name());
+  public List<Map<String, Object>> getFields() {
+    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();
+        })
+        .collect(Collectors.toList());
   }
 
-  /** Builder for <code>CollectionMapping</code> */
-  public static class Builder {
-    // Required parameters
-    private final String collectionName;
-    private final long dimension;
-
-    // Optional parameters - initialized to default values
-    private long indexFileSize = 1024;
-    private MetricType metricType = MetricType.L2;
-
-    /**
-     * @param collectionName collection name
-     * @param dimension vector dimension
-     */
-    public Builder(@Nonnull String collectionName, long dimension) {
-      this.collectionName = collectionName;
-      this.dimension = dimension;
-    }
+  /**
+   * 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.
+   * @return this CollectionMapping
+   */
+  public CollectionMapping setParamsInJson(String paramsInJson) {
+    builder.addExtraParams(KeyValuePair.newBuilder()
+        .setKey(MilvusClient.extraParamKey)
+        .setValue(paramsInJson)
+        .build());
+    return this;
+  }
 
-    /**
-     * Optional. Default to 1024 MB.
-     *
-     * @param indexFileSize in megabytes.
-     * @return <code>Builder</code>
-     */
-    public Builder withIndexFileSize(long indexFileSize) {
-      this.indexFileSize = indexFileSize;
-      return this;
-    }
+  public String getParamsInJson() {
+    return getParamsInJson(builder.getExtraParamsList());
+  }
 
-    /**
-     * Optional. Default to MetricType.L2
-     *
-     * @param metricType a <code>MetricType</code> value
-     * @return <code>Builder</code>
-     * @see MetricType
-     */
-    public Builder withMetricType(@Nonnull MetricType metricType) {
-      this.metricType = metricType;
-      return this;
-    }
+  public String getCollectionName() {
+    return builder.getCollectionName();
+  }
 
-    public CollectionMapping build() {
-      return new CollectionMapping(this);
+  Mapping grpc() {
+    if (builder.getFieldsCount() == 0) {
+      throw new IllegalArgumentException("Fields must not be empty.");
     }
+    return builder.build();
+  }
+
+  @Override
+  public String toString() {
+    return String.format(
+        "CollectionMapping = {collectionName = %s, fields = %s, params = %s}",
+        getCollectionName(), getFields(), getParamsInJson());
+  }
+
+  private String getParamsInJson(List<KeyValuePair> extraParams) {
+    return extraParams.stream()
+        .filter(kv -> MilvusClient.extraParamKey.equals(kv.getKey()))
+        .map(KeyValuePair::getValue)
+        .findFirst()
+        .orElse(null);
   }
 }

+ 19 - 4
src/main/java/io/milvus/client/ConnectFailedException.java → src/main/java/io/milvus/client/CompactParam.java

@@ -19,10 +19,25 @@
 
 package io.milvus.client;
 
-/** Thrown when client failed to connect to server */
-public class ConnectFailedException extends Exception {
+/** Contains parameters for <code>compact</code> */
+public class CompactParam {
+  private io.milvus.grpc.CompactParam.Builder builder;
 
-  public ConnectFailedException(String message) {
-    super(message);
+  public static CompactParam create(String collectionName) {
+    return new CompactParam(collectionName);
+  }
+
+  private CompactParam(String collectionName) {
+    builder = io.milvus.grpc.CompactParam.newBuilder();
+    builder.setCollectionName(collectionName).setThreshold(0.2);
+  }
+
+  public CompactParam setThreshold(double threshold) {
+    builder.setThreshold(threshold);
+    return this;
+  }
+
+  io.milvus.grpc.CompactParam grpc() {
+    return builder.build();
   }
 }

+ 2 - 14
src/main/java/io/milvus/client/ConnectParam.java

@@ -27,8 +27,6 @@ import java.util.concurrent.TimeUnit;
 /** Contains parameters for connecting to Milvus server */
 public class ConnectParam {
   private final String target;
-  private final String host;
-  private final int port;
   private final String defaultLoadBalancingPolicy;
   private final long connectTimeoutNanos;
   private final long keepAliveTimeNanos;
@@ -37,9 +35,7 @@ public class ConnectParam {
   private final long idleTimeoutNanos;
 
   private ConnectParam(@Nonnull Builder builder) {
-    this.target = builder.target;
-    this.host = builder.host;
-    this.port = builder.port;
+    this.target = builder.target != null ? builder.target : String.format("dns:///%s:%d", builder.host, builder.port);
     this.defaultLoadBalancingPolicy = builder.defaultLoadBalancingPolicy;
     this.connectTimeoutNanos = builder.connectTimeoutNanos;
     this.keepAliveTimeNanos = builder.keepAliveTimeNanos;
@@ -52,14 +48,6 @@ public class ConnectParam {
     return target;
   }
 
-  public String getHost() {
-    return host;
-  }
-
-  public int getPort() {
-    return port;
-  }
-
   public String getDefaultLoadBalancingPolicy() {
     return defaultLoadBalancingPolicy;
   }
@@ -187,7 +175,7 @@ public class ConnectParam {
      * expires without any read activity on the connection, the connection is considered dead. An
      * 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>This value should be at least multiple times the RTT to allow for lost packets.</p>
      *
      * @see <a
      *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveTimeout-long-java.util.concurrent.TimeUnit-">

+ 0 - 54
src/main/java/io/milvus/client/CountEntitiesResponse.java

@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-/**
- * Contains the returned <code>response</code> and <code>collectionEntityCount</code> for <code>
- * countEntities</code>
- */
-public class CountEntitiesResponse {
-  private final Response response;
-  private final long collectionEntityCount;
-
-  CountEntitiesResponse(Response response, long collectionEntityCount) {
-    this.response = response;
-    this.collectionEntityCount = collectionEntityCount;
-  }
-
-  public long getCollectionEntityCount() {
-    return collectionEntityCount;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "CountCollectionResponse {%s, collection entity count = %d}",
-        response.toString(), collectionEntityCount);
-  }
-}

+ 29 - 19
src/main/java/io/milvus/client/ListPartitionsResponse.java → src/main/java/io/milvus/client/DataType.java

@@ -19,33 +19,43 @@
 
 package io.milvus.client;
 
-import java.util.List;
+import java.util.Arrays;
+import java.util.Optional;
 
 /**
- * Contains the returned <code>response</code> and <code>partitionList</code> for <code>
- * listPartitions
- * </code>
+ * Represents available data types.
  */
-public class ListPartitionsResponse {
-  private final Response response;
-  private final List<String> partitionList;
+public enum DataType {
+  NONE(0),
+  BOOL(1),
+  INT8(2),
+  INT16(3),
+  INT32(4),
+  INT64(5),
 
-  ListPartitionsResponse(Response response, List<String> partitionList) {
-    this.response = response;
-    this.partitionList = partitionList;
-  }
+  FLOAT(10),
+  DOUBLE(11),
+
+  STRING(20),
+
+  VECTOR_BINARY(100),
+  VECTOR_FLOAT(101),
+
+  UNKNOWN(-1);
+
+  private final int val;
 
-  /** @return a <code>List</code> of partition tags. */
-  public List<String> getPartitionList() {
-    return partitionList;
+  DataType(int val) {
+    this.val = val;
   }
 
-  public Response getResponse() {
-    return response;
+  public static DataType valueOf(int val) {
+    Optional<DataType> search =
+        Arrays.stream(values()).filter(dataType -> dataType.val == val).findFirst();
+    return search.orElse(UNKNOWN);
   }
 
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
+  public int getVal() {
+    return val;
   }
 }

+ 0 - 64
src/main/java/io/milvus/client/GetCollectionInfoResponse.java

@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-import javax.annotation.Nullable;
-import java.util.Optional;
-
-/**
- * Contains the returned <code>response</code> and <code>collectionMapping</code> for <code>
- * getCollectionInfo
- * </code>
- */
-public class GetCollectionInfoResponse {
-  private final Response response;
-  private final CollectionMapping collectionMapping;
-
-  GetCollectionInfoResponse(Response response, @Nullable CollectionMapping collectionMapping) {
-    this.response = response;
-    this.collectionMapping = collectionMapping;
-  }
-
-  /**
-   * @return an <code>Optional</code> object which may or may not contain a <code>CollectionMapping
-   *     </code> object
-   * @see Optional
-   */
-  public Optional<CollectionMapping> getCollectionMapping() {
-    return Optional.ofNullable(collectionMapping);
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "GetCollectionInfoResponse {%s, %s}",
-        response.toString(),
-        collectionMapping == null ? "Collection mapping = None" : collectionMapping.toString());
-  }
-}

+ 0 - 41
src/main/java/io/milvus/client/GetEntityByIDResponse.java

@@ -1,41 +0,0 @@
-package io.milvus.client;
-
-import java.nio.ByteBuffer;
-import java.util.List;
-
-/**
- * Contains the returned <code>response</code> and either a <code>List</code> of <code>floatVectors
- * </code> or <code>
- * binaryVectors</code> for <code>getEntityByID</code>. If the id does not exist, both float and
- * binary vectors corresponding to the id will be empty.
- */
-public class GetEntityByIDResponse {
-  private final Response response;
-  private final List<List<Float>> floatVectors;
-  private final List<ByteBuffer> binaryVectors;
-
-  GetEntityByIDResponse(
-      Response response, List<List<Float>> floatVectors, List<ByteBuffer> binaryVectors) {
-    this.response = response;
-    this.floatVectors = floatVectors;
-    this.binaryVectors = binaryVectors;
-  }
-
-  public List<List<Float>> getFloatVectors() {
-    return floatVectors;
-  }
-
-  /** @return a <code>List</code> of <code>ByteBuffer</code> object */
-  public List<ByteBuffer> getBinaryVectors() {
-    return binaryVectors;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-}

+ 0 - 61
src/main/java/io/milvus/client/GetIndexInfoResponse.java

@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-import javax.annotation.Nullable;
-import java.util.Optional;
-
-/**
- * Contains the returned <code>response</code> and <code>index</code> for <code>getIndexInfo</code>
- */
-public class GetIndexInfoResponse {
-  private final Response response;
-  private final Index index;
-
-  GetIndexInfoResponse(Response response, @Nullable Index index) {
-    this.response = response;
-    this.index = index;
-  }
-
-  /**
-   * @return an <code>Optional</code> object which may or may not contain an <code>Index</code>
-   *     object
-   * @see Optional
-   */
-  public Optional<Index> getIndex() {
-    return Optional.ofNullable(index);
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "GetIndexInfoResponse {%s, %s}",
-        response.toString(), index == null ? "Index = Null" : index.toString());
-  }
-}

+ 0 - 53
src/main/java/io/milvus/client/HasCollectionResponse.java

@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-/**
- * Contains the returned <code>response</code> and <code>hasCollection</code> for <code>
- * hasCollection</code>
- */
-public class HasCollectionResponse {
-  private final Response response;
-  private final boolean hasCollection;
-
-  HasCollectionResponse(Response response, boolean hasCollection) {
-    this.response = response;
-    this.hasCollection = hasCollection;
-  }
-
-  public boolean hasCollection() {
-    return hasCollection;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "HasCollectionResponse {%s, has collection = %s}", response.toString(), hasCollection);
-  }
-}

+ 0 - 53
src/main/java/io/milvus/client/HasPartitionResponse.java

@@ -1,53 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-/**
- * Contains the returned <code>response</code> and <code>hasPartition</code> for <code>
- * hasPartition</code>
- */
-public class HasPartitionResponse {
-    private final Response response;
-    private final boolean hasPartition;
-
-    HasPartitionResponse(Response response, boolean hasPartition) {
-        this.response = response;
-        this.hasPartition = hasPartition;
-    }
-
-    public boolean hasPartition() {
-        return hasPartition;
-    }
-
-    public Response getResponse() {
-        return response;
-    }
-
-    /** @return <code>true</code> if the response status equals SUCCESS */
-    public boolean ok() {
-        return response.ok();
-    }
-
-    @Override
-    public String toString() {
-        return String.format(
-                "HasPartitionResponse {%s, has partition = %s}", response.toString(), hasPartition);
-    }
-}

+ 69 - 75
src/main/java/io/milvus/client/Index.java

@@ -19,100 +19,94 @@
 
 package io.milvus.client;
 
+import io.milvus.grpc.IndexParam;
+import io.milvus.grpc.KeyValuePair;
+
 import javax.annotation.Nonnull;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
-/** Represents an index containing <code>indexType</code> and <code>nList</code> */
+/** Represents an index containing <code>fieldName</code>, <code>indexName</code> and
+ * <code>paramsInJson</code>, which contains index_type, params etc.
+ */
 public class Index {
-  private final String collectionName;
-  private final IndexType indexType;
-  private final String paramsInJson;
-
-  private Index(@Nonnull Builder builder) {
-    this.collectionName = builder.collectionName;
-    this.indexType = builder.indexType;
-    this.paramsInJson = builder.paramsInJson;
+  private final IndexParam.Builder builder;
+
+  public static Index create(@Nonnull String collectionName, @Nonnull String fieldName) {
+    return new Index(collectionName, fieldName);
+  }
+
+  private Index(String collectionName, String fieldName) {
+    this.builder = IndexParam.newBuilder()
+        .setCollectionName(collectionName)
+        .setFieldName(fieldName);
   }
 
   public String getCollectionName() {
-    return collectionName;
+    return builder.getCollectionName();
+  }
+
+  public Index setCollectionName(@Nonnull String collectionName) {
+    builder.setCollectionName(collectionName);
+    return this;
+  }
+
+  public String getFieldName() {
+    return builder.getFieldName();
+  }
+
+  public Index setFieldName(@Nonnull String collectionName) {
+    builder.setFieldName(collectionName);
+    return this;
   }
 
-  public IndexType getIndexType() {
-    return indexType;
+  public String getIndexName() {
+    return builder.getIndexName();
   }
 
-  public String getParamsInJson() {
-    return paramsInJson;
+  public Map<String, String> getExtraParams() {
+    return toMap(builder.getExtraParamsList());
+  }
+
+  public Index setIndexType(IndexType indexType) {
+    return addParam("index_type", indexType.name());
+  }
+
+  public Index setMetricType(MetricType metricType) {
+    return addParam("metric_type", metricType.name());
+  }
+
+  public Index setParamsInJson(String paramsInJson) {
+    return addParam(MilvusClient.extraParamKey, paramsInJson);
+  }
+
+  private Index addParam(String key, Object value) {
+    builder.addExtraParams(
+        KeyValuePair.newBuilder()
+            .setKey(key)
+            .setValue(String.valueOf(value))
+            .build());
+    return this;
   }
 
   @Override
   public String toString() {
     return "Index {"
         + "collectionName="
-        + collectionName
-        + ", indexType="
-        + indexType
+        + getCollectionName()
+        + ", fieldName="
+        + getFieldName()
         + ", params="
-        + paramsInJson
+        + getExtraParams()
         + '}';
   }
 
-  /** Builder for <code>Index</code> */
-  public static class Builder {
-    // Required parameters
-    private final String collectionName;
-    private final IndexType indexType;
-
-    // Optional parameters - initialized to default values
-    private String paramsInJson;
-
-    /**
-     * @param collectionName collection to create index on
-     * @param indexType a <code>IndexType</code> object
-     */
-    public Builder(@Nonnull String collectionName, @Nonnull IndexType indexType) {
-      this.collectionName = collectionName;
-      this.indexType = indexType;
-    }
-
-    /**
-     * Optional. Default to empty <code>String</code>. Index parameters are different for different
-     * index types. Refer to <a
-     * href="https://milvus.io/docs/milvus_operation.md">https://milvus.io/docs/milvus_operation.md</a>
-     * for more information.
-     *
-     * <pre>
-     * FLAT/IVFLAT/SQ8: {"nlist": 16384}
-     * nlist range:[1, 999999]
-     *
-     * IVFPQ: {"nlist": 16384, "m": 12}
-     * nlist range:[1, 999999]
-     * m is decided by dim and have a couple of results.
-     *
-     * NSG: {"search_length": 45, "out_degree": 50, "candidate_pool_size": 300, "knng": 100}
-     * search_length range:[10, 300]
-     * out_degree range:[5, 300]
-     * candidate_pool_size range:[50, 1000]
-     * knng range:[5, 300]
-     *
-     * HNSW: {"M": 16, "efConstruction": 500}
-     * M range:[5, 48]
-     * efConstruction range:[100, 500]
-     *
-     * ANNOY: {"n_trees": 4}
-     * n_trees range: [1, 16384)
-     * </pre>
-     *
-     * @param paramsInJson extra parameters in JSON format
-     * @return <code>Builder</code>
-     */
-    public Builder withParamsInJson(@Nonnull String paramsInJson) {
-      this.paramsInJson = paramsInJson;
-      return this;
-    }
-
-    public Index build() {
-      return new Index(this);
-    }
+  IndexParam grpc() {
+    return builder.build();
+  }
+
+  private Map<String, String> toMap(List<KeyValuePair> extraParams) {
+    return extraParams.stream().collect(Collectors.toMap(KeyValuePair::getKey, KeyValuePair::getValue));
   }
 }

+ 15 - 54
src/main/java/io/milvus/client/IndexType.java

@@ -1,58 +1,19 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
 package io.milvus.client;
 
-import java.util.Arrays;
-import java.util.Optional;
-
-/**
- * Represents different types of indexing method to query the collection. Refer to <a
- * href="https://milvus.io/docs/index.md">https://milvus.io/docs/index.md</a>
- * for more information.
- */
 public enum IndexType {
-  INVALID(0),
-  FLAT(1),
-  IVFLAT(2),
-  IVF_SQ8(3),
-  RNSG(4),
-  IVF_SQ8_H(5),
-  IVF_PQ(6),
-  HNSW(11),
-  ANNOY(12),
-
-  UNKNOWN(-1);
-
-  private final int val;
-
-  IndexType(int val) {
-    this.val = val;
-  }
-
-  public static IndexType valueOf(int val) {
-    Optional<IndexType> search =
-        Arrays.stream(values()).filter(indexType -> indexType.val == val).findFirst();
-    return search.orElse(UNKNOWN);
-  }
-
-  public int getVal() {
-    return val;
-  }
+  ANNOY,
+  BIN_FLAT,
+  BIN_IVF_FLAT,
+  FLAT,
+  HNSW,
+  IVF_FLAT,
+  IVF_PQ,
+  IVF_SQ8,
+  IVF_SQ8_HYBRID,
+  NSG,
+  RHNSW_FLAT,
+  RHNSW_PQ,
+  RHNSW_SQ,
+  SPTAG_BKT_RNT,
+  SPTAG_KDT_RNT
 }

+ 70 - 91
src/main/java/io/milvus/client/InsertParam.java

@@ -19,114 +19,93 @@
 
 package io.milvus.client;
 
-import javax.annotation.Nonnull;
+import com.google.protobuf.ByteString;
+import io.milvus.client.exception.UnsupportedDataType;
+import io.milvus.grpc.AttrRecord;
+import io.milvus.grpc.FieldValue;
+import io.milvus.grpc.VectorRecord;
+import io.milvus.grpc.VectorRowRecord;
+
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /** Contains parameters for <code>insert</code> */
 public class InsertParam {
-  private final String collectionName;
-  private final List<List<Float>> floatVectors;
-  private final List<ByteBuffer> binaryVectors;
-  private final List<Long> vectorIds;
-  private final String partitionTag;
+  private io.milvus.grpc.InsertParam.Builder builder;
 
-  private InsertParam(@Nonnull Builder builder) {
-    this.collectionName = builder.collectionName;
-    this.floatVectors = builder.floatVectors;
-    this.binaryVectors = builder.binaryVectors;
-    this.vectorIds = builder.vectorIds;
-    this.partitionTag = builder.partitionTag;
+  public static InsertParam create(String collectionName) {
+    return new InsertParam(collectionName);
   }
 
-  public String getCollectionName() {
-    return collectionName;
+  private InsertParam(String collectionName) {
+    this.builder = io.milvus.grpc.InsertParam.newBuilder();
+    builder.setCollectionName(collectionName);
   }
 
-  public List<List<Float>> getFloatVectors() {
-    return floatVectors;
+  public InsertParam setEntityIds(List<Long> entityIds) {
+    builder.addAllEntityIdArray(entityIds);
+    return this;
   }
 
-  public List<ByteBuffer> getBinaryVectors() {
-    return binaryVectors;
+  public <T> InsertParam addField(String name, DataType type, List<T> values) {
+    AttrRecord.Builder record = AttrRecord.newBuilder();
+    switch (type) {
+      case INT32:
+        record.addAllInt32Value((List<Integer>) values);
+        break;
+      case INT64:
+        record.addAllInt64Value((List<Long>) values);
+        break;
+      case FLOAT:
+        record.addAllFloatValue((List<Float>) values);
+        break;
+      case DOUBLE:
+        record.addAllDoubleValue((List<Double>) values);
+        break;
+      default:
+        throw new UnsupportedDataType("Unsupported data type: " + type.name());
+    }
+    builder.addFields(FieldValue.newBuilder()
+        .setFieldName(name)
+        .setTypeValue(type.getVal())
+        .setAttrRecord(record.build())
+        .build());
+    return this;
   }
 
-  public List<Long> getVectorIds() {
-    return vectorIds;
+  public <T> InsertParam addVectorField(String name, DataType type, List<T> values) {
+    VectorRecord.Builder record = VectorRecord.newBuilder();
+    switch (type) {
+      case VECTOR_FLOAT:
+        record.addAllRecords(
+            ((List<List<Float>>) values).stream()
+                .map(row -> VectorRowRecord.newBuilder().addAllFloatData(row).build())
+                .collect(Collectors.toList()));
+        break;
+      case VECTOR_BINARY:
+        record.addAllRecords(
+            ((List<ByteBuffer>) values).stream()
+                .map(row -> VectorRowRecord.newBuilder().setBinaryData(ByteString.copyFrom(row.slice())).build())
+                .collect(Collectors.toList()));
+        break;
+      default:
+        throw new UnsupportedDataType("Unsupported data type: " + type.name());
+    }
+    builder.addFields(FieldValue.newBuilder()
+        .setFieldName(name)
+        .setTypeValue(type.getVal())
+        .setVectorRecord(record.build())
+        .build());
+    return this;
   }
 
-  public String getPartitionTag() {
-    return partitionTag;
+  public InsertParam setPartitionTag(String partitionTag) {
+    builder.setPartitionTag(partitionTag);
+    return this;
   }
 
-  /** Builder for <code>InsertParam</code> */
-  public static class Builder {
-    // Required parameters
-    private final String collectionName;
-
-    // Optional parameters - initialized to default values
-    private List<List<Float>> floatVectors = new ArrayList<>();
-    private List<ByteBuffer> binaryVectors = new ArrayList<>();
-    private List<Long> vectorIds = new ArrayList<>();
-    private String partitionTag = "";
-
-    /** @param collectionName collection to insert vectors to */
-    public Builder(@Nonnull String collectionName) {
-      this.collectionName = collectionName;
-    }
-
-    /**
-     * Default to an empty <code>ArrayList</code>. You can only insert either float or binary
-     * vectors to a collection, not both.
-     *
-     * @param floatVectors a <code>List</code> of float vectors to insert. Each inner <code>List
-     *     </code> represents a float vector.
-     * @return <code>Builder</code>
-     */
-    public Builder withFloatVectors(@Nonnull List<List<Float>> floatVectors) {
-      this.floatVectors = floatVectors;
-      return this;
-    }
-
-    /**
-     * Default to an empty <code>ArrayList</code>. You can only insert either float or binary
-     * vectors to a collection, not both.
-     *
-     * @param binaryVectors a <code>List</code> of binary vectors to insert. Each <code>ByteBuffer
-     *     </code> object represents a binary vector, with every 8 bits constituting a byte.
-     * @return <code>Builder</code>
-     * @see ByteBuffer
-     */
-    public Builder withBinaryVectors(@Nonnull List<ByteBuffer> binaryVectors) {
-      this.binaryVectors = binaryVectors;
-      return this;
-    }
-
-    /**
-     * Optional. Default to an empty <code>ArrayList</code>
-     *
-     * @param vectorIds a <code>List</code> of ids associated with the vectors to insert
-     * @return <code>Builder</code>
-     */
-    public Builder withVectorIds(@Nonnull List<Long> vectorIds) {
-      this.vectorIds = vectorIds;
-      return this;
-    }
-
-    /**
-     * Optional. Default to an empty <code>String</code>
-     *
-     * @param partitionTag partition tag
-     * @return <code>Builder</code>
-     */
-    public Builder withPartitionTag(@Nonnull String partitionTag) {
-      this.partitionTag = partitionTag;
-      return this;
-    }
-
-    public InsertParam build() {
-      return new InsertParam(this);
-    }
+  io.milvus.grpc.InsertParam grpc() {
+    return builder.build();
   }
 }

+ 20 - 25
src/main/java/io/milvus/client/InsertResponse.java → src/main/java/io/milvus/client/JsonBuilder.java

@@ -19,36 +19,31 @@
 
 package io.milvus.client;
 
-import java.util.List;
+import org.json.JSONObject;
 
-/**
- * Contains the returned <code>response</code> and <code>vectorIds</code> for <code>insert</code>
- */
-public class InsertResponse {
-  private final Response response;
-  private final List<Long> vectorIds;
-
-  InsertResponse(Response response, List<Long> vectorIds) {
-    this.response = response;
-    this.vectorIds = vectorIds;
-  }
-
-  public List<Long> getVectorIds() {
-    return vectorIds;
-  }
+/** Contains Json Parameter Builder */
+public class JsonBuilder {
+  private String paramsInJson;
 
-  public Response getResponse() {
-    return response;
+  public JsonBuilder() {
+    this.paramsInJson = "{}";
   }
 
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
+  /**
+   * Add key-value pair to paramsInJson.
+   *
+   * @param key The param key
+   * @param value The param value
+   * @return <code>JsonBuilder</code>
+   */
+  public JsonBuilder param(String key, Object value) {
+    JSONObject jsonObject = new JSONObject(this.paramsInJson);
+    jsonObject.put(key, value);
+    this.paramsInJson = jsonObject.toString();
+    return this;
   }
 
-  @Override
-  public String toString() {
-    return String.format(
-        "InsertResponse {%s, returned %d vector ids}", response.toString(), this.vectorIds.size());
+  public String build() {
+    return this.paramsInJson;
   }
 }

+ 0 - 57
src/main/java/io/milvus/client/ListCollectionsResponse.java

@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-import java.util.List;
-
-/**
- * Contains the returned <code>response</code> and <code>collectionNames</code> for <code>
- * listCollections
- * </code>
- */
-public class ListCollectionsResponse {
-  private final Response response;
-  private final List<String> collectionNames;
-
-  ListCollectionsResponse(Response response, List<String> collectionNames) {
-    this.response = response;
-    this.collectionNames = collectionNames;
-  }
-
-  public List<String> getCollectionNames() {
-    return collectionNames;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "ListCollectionsResponse {%s, collection names = %s}",
-        response, collectionNames.toString());
-  }
-}

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

@@ -1,30 +0,0 @@
-package io.milvus.client;
-
-import java.util.List;
-
-/**
- * Contains the returned <code>response</code> and a <code>List</code> of ids present in a segment
- * for <code>listIDInSegment</code>.
- */
-public class ListIDInSegmentResponse {
-  private final Response response;
-  private final List<Long> ids;
-
-  ListIDInSegmentResponse(Response response, List<Long> ids) {
-    this.response = response;
-    this.ids = ids;
-  }
-
-  public List<Long> getIds() {
-    return ids;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-}

+ 80 - 0
src/main/java/io/milvus/client/LoggingAdapter.java

@@ -0,0 +1,80 @@
+package io.milvus.client;
+
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.MessageOrBuilder;
+import com.google.protobuf.TextFormat;
+import io.grpc.MethodDescriptor;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class LoggingAdapter {
+  public static final LoggingAdapter DEFAULT_LOGGING_ADAPTER = new LoggingAdapter();
+  private static final AtomicLong traceId = new AtomicLong(0);
+
+  protected LoggingAdapter() {
+  }
+
+  protected String getTraceId() {
+    return Long.toHexString(traceId.getAndIncrement());
+  }
+
+  protected void logRequest(Logger logger, String traceId, MethodDescriptor method, Object message) {
+    if (logger.isTraceEnabled()) {
+      logger.trace("TraceId: {}, Method: {}, Request: {}", traceId, method.getFullMethodName(), trace(message));
+    } else if (logger.isInfoEnabled()) {
+      logger.info("TraceId: {}, Method: {}, Request: {}", traceId, method.getFullMethodName(), info(message));
+    }
+  }
+
+  protected void logResponse(Logger logger, String traceId, MethodDescriptor method, Object message) {
+    if (logger.isTraceEnabled()) {
+      logger.trace("TraceId: {}, Method: {}, Response: {}", traceId, method.getFullMethodName(), trace(message));
+    } else if (logger.isInfoEnabled()) {
+      logger.info("TraceId: {}, Method: {}, Response: {}", traceId, method.getFullMethodName(), info(message));
+    }
+  }
+
+  protected String info(Object message) {
+    if (message instanceof MessageOrBuilder) {
+      MessageOrBuilder msg = (MessageOrBuilder) message;
+      StringBuilder output = new StringBuilder(msg.getDescriptorForType().getName());
+      write((MessageOrBuilder) message, output);
+      return output.toString();
+    }
+    return message.toString();
+  }
+
+  protected String trace(Object message) {
+    if (message instanceof MessageOrBuilder) {
+      return TextFormat.printer().printToString((MessageOrBuilder) message);
+    }
+    return message.toString();
+  }
+
+  protected void write(MessageOrBuilder message, StringBuilder output) {
+    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(", ");
+      }
+    });
+    output.setLength(output.length() - 2);
+    output.append(" } ");
+  }
+}

+ 7 - 60
src/main/java/io/milvus/client/MetricType.java

@@ -1,64 +1,11 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
 package io.milvus.client;
 
-import java.util.Arrays;
-import java.util.Optional;
-
-/**
- * Represents available metric types. Refer to <a
- * href="https://milvus.io/docs/metric.md">https://milvus.io/docs/metric.md</a>
- * for more information.
- */
 public enum MetricType {
-  INVALID(0),
-  /** Euclidean distance. For float vectors only */
-  L2(1),
-  /** Inner product. For float vectors only */
-  IP(2),
-  /** Hamming distance. For binary vectors only */
-  HAMMING(3),
-  /** Jaccard distance. For binary vectors only */
-  JACCARD(4),
-  /** Tanimoto distance. For binary vectors only */
-  TANIMOTO(5),
-  /** Substructure. For binary vectors only */
-  SUBSTRUCTURE(6),
-  /** Superstructure. For binary vectors only */
-  SUPERSTRUCTURE(7),
-
-  UNKNOWN(-1);
-
-  private final int val;
-
-  MetricType(int val) {
-    this.val = val;
-  }
-
-  public static MetricType valueOf(int val) {
-    Optional<MetricType> search =
-        Arrays.stream(values()).filter(metricType -> metricType.val == val).findFirst();
-    return search.orElse(UNKNOWN);
-  }
-
-  public int getVal() {
-    return val;
-  }
+  HAMMING,
+  IP,
+  JACCARD,
+  L2,
+  SUBSTRUCTURE,
+  SUPERSTRUCTURE,
+  TANIMOTO
 }

+ 139 - 177
src/main/java/io/milvus/client/MilvusClient.java

@@ -20,12 +20,12 @@
 package io.milvus.client;
 
 import com.google.common.util.concurrent.ListenableFuture;
-import io.grpc.Metadata;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
@@ -33,37 +33,28 @@ import java.util.function.Supplier;
 /** The Milvus Client Interface */
 public interface MilvusClient {
 
+  String extraParamKey = "params";
+
   String clientVersion = new Supplier<String>() {
+
     @Override
+    /** @return current Milvus client version */
     public String get() {
       Properties properties = new Properties();
-      InputStream inputStream = MilvusClient.class
-          .getClassLoader().getResourceAsStream("milvus-client.properties");
-      try {
+      try (InputStream inputStream =
+               MilvusClient.class.getClassLoader()
+                   .getResourceAsStream("milvus-client.properties")) {
         properties.load(inputStream);
       } catch (IOException ex) {
         ExceptionUtils.wrapAndThrow(ex);
-      } finally {
-        try {
-          inputStream.close();
-        } catch (IOException ex) {
-        }
       }
       return properties.getProperty("version");
     }
   }.get();
 
-  Metadata commonHeaders = new Supplier<Metadata>() {
-    @Override
-    public Metadata get() {
-      Metadata metadata = new Metadata();
-      Metadata.Key<String> key = Metadata.Key.of("Milvus-Client-Version", Metadata.ASCII_STRING_MARSHALLER);
-      metadata.put(key, clientVersion);
-      return metadata;
-    }
-  }.get();
+  String target();
 
-  /** @return current Milvus client version */
+  /** @return current Milvus client version: 0.9.0 */
   default String getClientVersion() {
     return clientVersion;
   }
@@ -86,296 +77,253 @@ public interface MilvusClient {
    * Creates collection specified by <code>collectionMapping</code>
    *
    * @param collectionMapping the <code>CollectionMapping</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
-   * CollectionMapping collectionMapping = new CollectionMapping.Builder(collectionName, dimension)
-   *                                          .withIndexFileSize(1024)
-   *                                          .withMetricType(MetricType.IP)
-   *                                          .build();
+   * CollectionMapping collectionMapping = new CollectionMapping.Builder(collectionName)
+   *                                                            .withFields(fields)
+   *                                                            .withParamsInJson("{"segment_row_limit": 100000}")
+   *                                                            .build();
    * </code>
+   * Refer to <code>withFields</code> method for example <code>fields</code> usage.
    * </pre>
    *
-   * @return <code>Response</code>
    * @see CollectionMapping
-   * @see MetricType
-   * @see Response
    */
-  Response createCollection(CollectionMapping collectionMapping);
+  void createCollection(CollectionMapping collectionMapping);
 
   /**
    * Checks whether the collection exists
    *
    * @param collectionName collection to check
-   * @return <code>HasCollectionResponse</code>
-   * @see HasCollectionResponse
-   * @see Response
+   * @return true if the collection exists, false otherwise.
    */
-  HasCollectionResponse hasCollection(String collectionName);
+  boolean hasCollection(String collectionName);
 
   /**
    * Drops collection
    *
    * @param collectionName collection to drop
-   * @return <code>Response</code>
-   * @see Response
    */
-  Response dropCollection(String collectionName);
+  void dropCollection(String collectionName);
 
   /**
    * Creates index specified by <code>index</code>
    *
    * @param index the <code>Index</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
-   * Index index = new Index.Builder(collectionName, IndexType.IVF_SQ8)
-   *                        .withParamsInJson("{\"nlist\": 16384}")
+   * Index index = new Index.Builder(collectionName, fieldName)
+   *                        .withParamsInJson(
+   *                            "{"index_type": "IVF_FLAT", "metric_type": "L2",
+   *                              "params": {"nlist": 16384}}")
    *                        .build();
    * </code>
    * </pre>
    *
-   * @return <code>Response</code>
    * @see Index
-   * @see IndexType
-   * @see Response
    */
-  Response createIndex(Index index);
+  void createIndex(Index index);
 
   /**
    * Creates index specified by <code>index</code> asynchronously
    *
    * @param index the <code>Index</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
-   * Index index = new Index.Builder(collectionName, IndexType.IVF_SQ8)
-   *                        .withParamsInJson("{\"nlist\": 16384}")
+   * Index index = new Index.Builder(collectionName, fieldName)
+   *                        .withParamsInJson(
+   *                            "{"index_type": "IVF_FLAT", "metric_type": "L2",
+   *                              "params\": {"nlist": 16384}}")
    *                        .build();
    * </code>
    * </pre>
    *
    * @return a <code>ListenableFuture</code> object which holds the <code>Response</code>
    * @see Index
-   * @see IndexType
-   * @see Response
    * @see ListenableFuture
    */
-  ListenableFuture<Response> createIndexAsync(Index index);
+  ListenableFuture<Void> createIndexAsync(Index index);
 
   /**
    * Creates a partition specified by <code>collectionName</code> and <code>tag</code>
    *
    * @param collectionName collection name
    * @param tag partition tag
-   * @return <code>Response</code>
-   * @see Response
    */
-  Response createPartition(String collectionName, String tag);
+  void createPartition(String collectionName, String tag);
 
   /**
    * Checks whether the partition exists
    *
    * @param collectionName collection name
    * @param tag partition tag
-   * @return <code>HasPartitionResponse</code>
-   * @see Response
+   * @return true if the partition exists, false otherwise.
    */
-  HasPartitionResponse hasPartition(String collectionName, String tag);
+  boolean hasPartition(String collectionName, String tag);
 
   /**
    * Lists current partitions of a collection
    *
    * @param collectionName collection name
-   * @return <code>ListPartitionsResponse</code>
-   * @see ListPartitionsResponse
-   * @see Response
+   * @return a list of partition names
    */
-  ListPartitionsResponse listPartitions(String collectionName);
+  List<String> listPartitions(String collectionName);
 
   /**
    * Drops partition specified by <code>collectionName</code> and <code>tag</code>
    *
    * @param collectionName collection name
    * @param tag partition tag
-   * @see Response
    */
-  Response dropPartition(String collectionName, String tag);
+  void dropPartition(String collectionName, String tag);
 
   /**
    * Inserts data specified by <code>insertParam</code>
    *
    * @param insertParam the <code>InsertParam</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
    * InsertParam insertParam = new InsertParam.Builder(collectionName)
-   *                                          .withFloatVectors(floatVectors)
-   *                                          .withVectorIds(vectorIds)
+   *                                          .withFields(fields)
+   *                                          .withEntityIds(entityIds)
    *                                          .withPartitionTag(tag)
    *                                          .build();
    * </code>
    * </pre>
    *
-   * @return <code>InsertResponse</code>
+   * @return a list of ids for the inserted entities
    * @see InsertParam
-   * @see InsertResponse
-   * @see Response
    */
-  InsertResponse insert(InsertParam insertParam);
+  List<Long> insert(InsertParam insertParam);
 
   /**
    * Inserts data specified by <code>insertParam</code> asynchronously
    *
    * @param insertParam the <code>InsertParam</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
    * InsertParam insertParam = new InsertParam.Builder(collectionName)
-   *                                          .withFloatVectors(floatVectors)
-   *                                          .withVectorIds(vectorIds)
+   *                                          .withFields(fields)
+   *                                          .withEntityIds(entityIds)
    *                                          .withPartitionTag(tag)
    *                                          .build();
    * </code>
    * </pre>
    *
-   * @return a <code>ListenableFuture</code> object which holds the <code>InsertResponse</code>
+   * @return a <code>ListenableFuture</code> object which holds the list of ids for the inserted entities.
    * @see InsertParam
-   * @see InsertResponse
-   * @see Response
    * @see ListenableFuture
    */
-  ListenableFuture<InsertResponse> insertAsync(InsertParam insertParam);
+  ListenableFuture<List<Long>> insertAsync(InsertParam insertParam);
 
   /**
-   * Searches vectors specified by <code>searchParam</code>
+   * Searches entities specified by <code>searchParam</code>
    *
    * @param searchParam the <code>SearchParam</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
    * SearchParam searchParam = new SearchParam.Builder(collectionName)
-   *                                          .withFloatVectors(floatVectors)
-   *                                          .withTopK(topK)
+   *                                          .withDSL(dslStatement)
    *                                          .withPartitionTags(partitionTagsList)
-   *                                          .withParamsInJson("{\"nprobe\": 20}")
+   *                                          .withParamsInJson("{"fields": ["B"]}")
    *                                          .build();
    * </code>
    * </pre>
    *
-   * @return <code>SearchResponse</code>
+   * @return <code>SearchResult</code>
    * @see SearchParam
-   * @see SearchResponse
-   * @see SearchResponse.QueryResult
-   * @see Response
+   * @see SearchResult
    */
-  SearchResponse search(SearchParam searchParam);
+  SearchResult search(SearchParam searchParam);
 
   /**
-   * Searches vectors specified by <code>searchParam</code> asynchronously
+   * Searches entities specified by <code>searchParam</code> asynchronously
    *
    * @param searchParam the <code>SearchParam</code> object
-   *     <pre>
+   * <pre>
    * example usage:
    * <code>
    * SearchParam searchParam = new SearchParam.Builder(collectionName)
-   *                                          .withFloatVectors(floatVectors)
-   *                                          .withTopK(topK)
+   *                                          .withDSL(dslStatement)
    *                                          .withPartitionTags(partitionTagsList)
-   *                                          .withParamsInJson("{\"nprobe\": 20}")
+   *                                          .withParamsInJson("{"fields": ["B"]}")
    *                                          .build();
    * </code>
    * </pre>
    *
-   * @return a <code>ListenableFuture</code> object which holds the <code>SearchResponse</code>
+   * @return a <code>ListenableFuture</code> object which holds the <code>SearchResult</code>
    * @see SearchParam
-   * @see SearchResponse
-   * @see SearchResponse.QueryResult
-   * @see Response
+   * @see SearchResult
    * @see ListenableFuture
    */
-  ListenableFuture<SearchResponse> searchAsync(SearchParam searchParam);
+  ListenableFuture<SearchResult> searchAsync(SearchParam searchParam);
 
   /**
    * Gets collection info
    *
    * @param collectionName collection to describe
-   * @see GetCollectionInfoResponse
+   * @return <code>CollectionMapping</code>
    * @see CollectionMapping
-   * @see Response
    */
-  GetCollectionInfoResponse getCollectionInfo(String collectionName);
+  CollectionMapping getCollectionInfo(String collectionName);
 
   /**
    * Lists current collections
    *
-   * @return <code>ListCollectionsResponse</code>
-   * @see ListCollectionsResponse
-   * @see Response
+   * @return a list of collection names
    */
-  ListCollectionsResponse listCollections();
+  List<String> listCollections();
 
   /**
    * Gets current entity count of a collection
    *
-   * @param collectionName collection to count entities
-   * @return <code>CountEntitiesResponse</code>
-   * @see CountEntitiesResponse
-   * @see Response
+   * @param collectionName collection name
+   * @return a count of entities in the collection
    */
-  CountEntitiesResponse countEntities(String collectionName);
+  long countEntities(String collectionName);
 
   /**
-   * Get server status
+   * Gets server status
    *
-   * @return <code>Response</code>
-   * @see Response
+   * @return a server status string
    */
-  Response getServerStatus();
+  String getServerStatus();
 
   /**
-   * Get server version
+   * Gets server version
    *
-   * @return <code>Response</code>
-   * @see Response
+   * @return a server version string.
    */
-  Response getServerVersion();
+  String getServerVersion();
 
   /**
    * Sends a command to server
    *
-   * @return <code>Response</code> command's response will be return in <code>message</code>
-   * @see Response
+   * @return a message string
    */
-  Response command(String command);
+  String command(String command);
 
   /**
    * Pre-loads collection to memory
    *
    * @param collectionName collection to load
-   * @return <code>Response</code>
-   * @see Response
-   */
-  Response loadCollection(String collectionName);
-
-  /**
-   * Gets collection index information
-   *
-   * @param collectionName collection to get info from
-   * @see GetIndexInfoResponse
-   * @see Index
-   * @see Response
    */
-  GetIndexInfoResponse getIndexInfo(String collectionName);
+  void loadCollection(String collectionName);
 
   /**
    * Drops collection index
    *
-   * @param collectionName collection to drop index of
-   * @return <code>Response</code>
-   * @see Response
+   * @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.
    */
-  Response dropIndex(String collectionName);
+  void dropIndex(String collectionName, String fieldName);
 
   /**
    * Shows collection information. A collection consists of one or multiple partitions (including
@@ -384,105 +332,119 @@ public interface MilvusClient {
    * result will be returned as JSON string.
    *
    * @param collectionName collection to show info from
-   * @return <code>Response</code>
-   * @see Response
    */
-  Response getCollectionStats(String collectionName);
+  String getCollectionStats(String collectionName);
+
+  /**
+   * Gets entities data by id array
+   *
+   * @param collectionName collection to get entities from
+   * @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.
+   * @return a map of entity id to entity properties
+   */
+  Map<Long, Map<String, Object>> getEntityByID(String collectionName, List<Long> ids, List<String> fieldNames);
 
   /**
-   * Gets vectors data by id array
+   * Gets entities data by id array
    *
-   * @param collectionName collection to get vectors from
-   * @param ids a <code>List</code> of vector ids
-   * @return <code>GetEntityByIDResponse</code>
-   * @see GetEntityByIDResponse
-   * @see Response
+   * @param collectionName collection to get entities from
+   * @param ids a <code>List</code> of entity ids
+   * @return a map of entity id to entity properties
    */
-  GetEntityByIDResponse getEntityByID(String collectionName, List<Long> ids);
+  Map<Long, Map<String, Object>> getEntityByID(String collectionName, List<Long> ids);
 
   /**
-   * Gets all vector ids in a segment
+   * Gets all entity ids in a segment
    *
-   * @param collectionName collection to get vector ids from
-   * @param segmentName segment name in the collection
-   * @return <code>ListIDInSegmentResponse</code>
-   * @see ListIDInSegmentResponse
-   * @see Response
+   * @param collectionName collection to get entity ids from
+   * @param segmentId segment id in the collection
+   * @return a list of entity ids in the segment
    */
-  ListIDInSegmentResponse listIDInSegment(String collectionName, String segmentName);
+  List<Long> listIDInSegment(String collectionName, Long segmentId);
 
   /**
    * Deletes data in a collection by a list of ids
    *
    * @param collectionName collection to delete ids from
-   * @param ids a <code>List</code> of vector ids to delete
-   * @return <code>Response</code>
-   * @see Response
+   * @param ids a <code>List</code> of entity ids to delete
    */
-  Response deleteEntityByID(String collectionName, List<Long> ids);
+  void deleteEntityByID(String collectionName, List<Long> ids);
 
   /**
    * Flushes data in a list collections. Newly inserted or modifications on data will be visible
    * after <code>flush</code> returned
    *
    * @param collectionNames a <code>List</code> of collections to flush
-   * @return <code>Response</code>
-   * @see Response
    */
-  Response flush(List<String> collectionNames);
+  void flush(List<String> collectionNames);
 
   /**
    * Flushes data in a list collections asynchronously. Newly inserted or modifications on data will
    * be visible after <code>flush</code> returned
    *
    * @param collectionNames a <code>List</code> of collections to flush
-   * @return a <code>ListenableFuture</code> object which holds the <code>Response</code>
-   * @see Response
+   * @return <code>ListenableFuture</code>
    * @see ListenableFuture
    */
-  ListenableFuture<Response> flushAsync(List<String> collectionNames);
+  ListenableFuture<Void> flushAsync(List<String> collectionNames);
 
   /**
    * Flushes data in a collection. Newly inserted or modifications on data will be visible after
    * <code>flush</code> returned
    *
    * @param collectionName name of collection to flush
-   * @return <code>Response</code>
-   * @see Response
    */
-  Response flush(String collectionName);
+  void flush(String collectionName);
 
   /**
    * Flushes data in a collection asynchronously. Newly inserted or modifications on data will be
    * visible after <code>flush</code> returned
    *
    * @param collectionName name of collection to flush
-   * @return a <code>ListenableFuture</code> object which holds the <code>Response</code>
-   * @see Response
+   * @return <code>ListenableFuture</code>
    * @see ListenableFuture
    */
-  ListenableFuture<Response> flushAsync(String collectionName);
+  ListenableFuture<Void> flushAsync(String 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 collectionName name of collection to compact
-   * @return <code>Response</code>
-   * @see Response
+   * @param compactParam the <code>CompactParam</code> object
+   * <pre>
+   * example usage:
+   * <code>
+   * CompactParam compactParam = new CompactParam.Builder(collectionName)
+   *                                             .withThreshold(0.3)
+   *                                             .build();
+   * </code>
+   * </pre>
+   *
+   * @see CompactParam
    */
-  Response compact(String collectionName);
+  void compact(CompactParam compactParam);
 
   /**
-   * Compacts the collection asynchronously, 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.
+   * 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 compactParam the <code>CompactParam</code> object
+   * <pre>
+   * example usage:
+   * <code>
+   * CompactParam compactParam = new CompactParam.Builder(collectionName)
+   *                                             .withThreshold(0.3)
+   *                                             .build();
+   * </code>
+   * </pre>
    *
-   * @param collectionName name of collection to compact
    * @return a <code>ListenableFuture</code> object which holds the <code>Response</code>
-   * @see Response
+   * @see CompactParam
    * @see ListenableFuture
    */
-  ListenableFuture<Response> compactAsync(String collectionName);
+  ListenableFuture<Void> compactAsync(CompactParam compactParam);
 }

+ 382 - 1136
src/main/java/io/milvus/client/MilvusGrpcClient.java

@@ -19,46 +19,54 @@
 
 package io.milvus.client;
 
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
-import com.google.protobuf.ByteString;
 import io.grpc.CallOptions;
 import io.grpc.Channel;
 import io.grpc.ClientCall;
 import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall;
+import io.grpc.ForwardingClientCallListener;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
+import io.grpc.Metadata;
 import io.grpc.MethodDescriptor;
-import io.grpc.StatusRuntimeException;
-import io.grpc.stub.MetadataUtils;
-import io.milvus.client.exception.InitializationException;
+import io.milvus.client.exception.ClientSideMilvusException;
+import io.milvus.client.exception.MilvusException;
+import io.milvus.client.exception.ServerSideMilvusException;
 import io.milvus.client.exception.UnsupportedServerVersion;
 import io.milvus.grpc.*;
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nonnull;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-import javax.annotation.Nonnull;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
+/** Actual implementation of interface <code>MilvusClient</code> */
 public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
-  private static String SUPPORTED_SERVER_VERSION = "0.10";
+
+  private static final Logger logger = LoggerFactory.getLogger(MilvusGrpcClient.class);
+  private static final String SUPPORTED_SERVER_VERSION = "0.11";
+
+  private final String target;
   private final ManagedChannel channel;
   private final MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub;
   private final MilvusServiceGrpc.MilvusServiceFutureStub futureStub;
 
   public MilvusGrpcClient(ConnectParam connectParam) {
-    ManagedChannelBuilder builder = connectParam.getTarget() != null
-        ? ManagedChannelBuilder.forTarget(connectParam.getTarget())
-        : ManagedChannelBuilder.forAddress(connectParam.getHost(), connectParam.getPort());
-
-    channel = builder.usePlaintext()
+    target = connectParam.getTarget();
+    channel = ManagedChannelBuilder
+        .forTarget(connectParam.getTarget())
+        .usePlaintext()
         .maxInboundMessageSize(Integer.MAX_VALUE)
         .defaultLoadBalancingPolicy(connectParam.getDefaultLoadBalancingPolicy())
         .keepAliveTime(connectParam.getKeepAliveTime(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
@@ -66,18 +74,12 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
         .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
         .idleTimeout(connectParam.getIdleTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
         .build();
-    blockingStub = MetadataUtils.attachHeaders(MilvusServiceGrpc.newBlockingStub(channel), commonHeaders);
-    futureStub = MetadataUtils.attachHeaders(MilvusServiceGrpc.newFutureStub(channel), commonHeaders);
-
+    blockingStub = MilvusServiceGrpc.newBlockingStub(channel);
+    futureStub = MilvusServiceGrpc.newFutureStub(channel);
     try {
-      Response response = getServerVersion();
-      if (response.ok()) {
-        String serverVersion = response.getMessage();
-        if (!serverVersion.matches("^" + SUPPORTED_SERVER_VERSION + "(\\..*)?$")) {
-          throw new UnsupportedServerVersion(connectParam.getHost(), SUPPORTED_SERVER_VERSION, serverVersion);
-        }
-      } else {
-        throw new InitializationException(connectParam.getHost(), response.getMessage());
+      String serverVersion = getServerVersion();
+      if (!serverVersion.matches("^" + SUPPORTED_SERVER_VERSION + "(\\..*)?$")) {
+        throw new UnsupportedServerVersion(connectParam.getTarget(), SUPPORTED_SERVER_VERSION, serverVersion);
       }
     } catch (Throwable t) {
       channel.shutdownNow();
@@ -85,6 +87,11 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
     }
   }
 
+  @Override
+  public String target() {
+    return target;
+  }
+
   @Override
   protected MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub() {
     return blockingStub;
@@ -95,40 +102,43 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
     return futureStub;
   }
 
-  @Override
-  protected boolean maybeAvailable() {
-    switch (channel.getState(false)) {
-      case IDLE:
-      case CONNECTING:
-      case READY:
-        return true;
-      default:
-        return false;
-    }
-  }
-
   @Override
   public void close(long maxWaitSeconds) {
     channel.shutdown();
     try {
-      if (!channel.awaitTermination(maxWaitSeconds, TimeUnit.SECONDS)) {
-        channel.shutdownNow();
-      }
+      channel.awaitTermination(maxWaitSeconds, TimeUnit.SECONDS);
     } catch (InterruptedException ex) {
+      logger.warn("Milvus client close interrupted");
       channel.shutdownNow();
       Thread.currentThread().interrupt();
     }
   }
 
+  public MilvusClient withLogging() {
+    return withLogging(LoggingAdapter.DEFAULT_LOGGING_ADAPTER);
+  }
+
+  public MilvusClient withLogging(LoggingAdapter loggingAdapter) {
+    return withInterceptors(new LoggingInterceptor(loggingAdapter));
+  }
+
   public MilvusClient withTimeout(long timeout, TimeUnit timeoutUnit) {
     final long timeoutMillis = timeoutUnit.toMillis(timeout);
     final TimeoutInterceptor timeoutInterceptor = new TimeoutInterceptor(timeoutMillis);
+    return withInterceptors(timeoutInterceptor);
+  }
+
+  private MilvusClient withInterceptors(ClientInterceptor... interceptors) {
     final MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub =
-        this.blockingStub.withInterceptors(timeoutInterceptor);
+        this.blockingStub.withInterceptors(interceptors);
     final MilvusServiceGrpc.MilvusServiceFutureStub futureStub =
-        this.futureStub.withInterceptors(timeoutInterceptor);
+        this.futureStub.withInterceptors(interceptors);
 
     return new AbstractMilvusGrpcClient() {
+      @Override
+      public String target() {
+        return MilvusGrpcClient.this.target();
+      }
 
       @Override
       protected MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub() {
@@ -140,11 +150,6 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
         return futureStub;
       }
 
-      @Override
-      protected boolean maybeAvailable() {
-        return MilvusGrpcClient.this.maybeAvailable();
-      }
-
       @Override
       public void close(long maxWaitSeconds) {
         MilvusGrpcClient.this.close(maxWaitSeconds);
@@ -170,1216 +175,457 @@ public class MilvusGrpcClient extends AbstractMilvusGrpcClient {
       return next.newCall(method, callOptions.withDeadlineAfter(timeoutMillis, TimeUnit.MILLISECONDS));
     }
   }
-}
-
-abstract class AbstractMilvusGrpcClient implements MilvusClient {
-  private static final Logger logger = LoggerFactory.getLogger(AbstractMilvusGrpcClient.class);
 
-  private final String extraParamKey = "params";
-
-  protected abstract MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub();
-
-  protected abstract MilvusServiceGrpc.MilvusServiceFutureStub futureStub();
+  private static class LoggingInterceptor implements ClientInterceptor {
+    private LoggingAdapter loggingAdapter;
 
-  protected abstract boolean maybeAvailable();
-
-  @Override
-  public Response createCollection(@Nonnull CollectionMapping collectionMapping) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+    LoggingInterceptor(LoggingAdapter loggingAdapter) {
+      this.loggingAdapter = loggingAdapter;
     }
 
-    CollectionSchema request =
-        CollectionSchema.newBuilder()
-            .setCollectionName(collectionMapping.getCollectionName())
-            .setDimension(collectionMapping.getDimension())
-            .setIndexFileSize(collectionMapping.getIndexFileSize())
-            .setMetricType(collectionMapping.getMetricType().getVal())
-            .build();
+    @Override
+    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
+        MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
+      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {
+        private String traceId = loggingAdapter.getTraceId();
 
-    Status response;
+        @Override
+        public void sendMessage(ReqT message) {
+          loggingAdapter.logRequest(logger, traceId, method, message);
+          super.sendMessage(message);
+        }
 
-    try {
-      response = blockingStub().createCollection(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Created collection successfully!\n{}", collectionMapping.toString());
-        return new Response(Response.Status.SUCCESS);
-      } else if (response.getReason().contentEquals("Collection already exists")) {
-        logWarning("Collection `{}` already exists", collectionMapping.getCollectionName());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      } else {
-        logError(
-            "Create collection failed\n{}\n{}", collectionMapping.toString(), response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("createCollection RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
+        @Override
+        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);
+        }
+      };
     }
   }
+}
 
-  @Override
-  public HasCollectionResponse hasCollection(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new HasCollectionResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), false);
-    }
+abstract class AbstractMilvusGrpcClient implements MilvusClient {
+  protected abstract MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub();
+  protected abstract MilvusServiceGrpc.MilvusServiceFutureStub futureStub();
 
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    BoolReply response;
+  private void translateExceptions(Runnable body) {
+    translateExceptions(() -> {
+      body.run();
+      return null;
+    });
+  }
 
+  @SuppressWarnings("unchecked")
+  private <T> T translateExceptions(Supplier<T> body) {
     try {
-      response = blockingStub().hasCollection(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("hasCollection `{}` = {}", collectionName, response.getBoolReply());
-        return new HasCollectionResponse(
-            new Response(Response.Status.SUCCESS), response.getBoolReply());
-      } else {
-        logError("hasCollection `{}` failed:\n{}", collectionName, response.toString());
-        return new HasCollectionResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            false);
+      T result = body.get();
+      if (result instanceof ListenableFuture) {
+        ListenableFuture futureResult = (ListenableFuture) result;
+        result = (T) Futures.catching(
+            futureResult, Throwable.class, this::translate, MoreExecutors.directExecutor());
       }
-    } catch (StatusRuntimeException e) {
-      logError("hasCollection RPC failed:\n{}", e.getStatus().toString());
-      return new HasCollectionResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), false);
+      return result;
+    } catch (Throwable e) {
+      return translate(e);
     }
   }
 
-  @Override
-  public Response dropCollection(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+  private <R> R translate(Throwable e) {
+    if (e instanceof MilvusException) {
+      throw (MilvusException) e;
+    } else if (e.getCause() == null || e.getCause() == e) {
+      throw new ClientSideMilvusException(target(), e);
+    } else {
+      return translate(e.getCause());
     }
+  }
 
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    Status response;
-
-    try {
-      response = blockingStub().dropCollection(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Dropped collection `{}` successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Drop collection `{}` failed:\n{}", collectionName, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("dropCollection RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
+  private Void checkResponseStatus(Status status) {
+    if (status.getErrorCode() != ErrorCode.SUCCESS) {
+      throw new ServerSideMilvusException(target(), status);
     }
+    return null;
   }
 
   @Override
-  public Response createIndex(@Nonnull Index index) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    KeyValuePair extraParam =
-        KeyValuePair.newBuilder().setKey(extraParamKey).setValue(index.getParamsInJson()).build();
-    IndexParam request =
-        IndexParam.newBuilder()
-            .setCollectionName(index.getCollectionName())
-            .setIndexType(index.getIndexType().getVal())
-            .addExtraParams(extraParam)
-            .build();
-
-    Status response;
-
-    try {
-      response = blockingStub().createIndex(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Created index successfully!\n{}", index.toString());
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Create index failed:\n{}\n{}", index.toString(), response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("createIndex RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public void createCollection(@Nonnull CollectionMapping collectionMapping) {
+    translateExceptions(() -> {
+      Status response = blockingStub().createCollection(collectionMapping.grpc());
+      checkResponseStatus(response);
+    });
   }
 
   @Override
-  public ListenableFuture<Response> createIndexAsync(@Nonnull Index index) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return Futures.immediateFuture(new Response(Response.Status.CLIENT_NOT_CONNECTED));
-    }
-
-    KeyValuePair extraParam =
-        KeyValuePair.newBuilder().setKey(extraParamKey).setValue(index.getParamsInJson()).build();
-    IndexParam request =
-        IndexParam.newBuilder()
-            .setCollectionName(index.getCollectionName())
-            .setIndexType(index.getIndexType().getVal())
-            .addExtraParams(extraParam)
-            .build();
-
-    ListenableFuture<Status> response;
-
-    response = futureStub().createIndex(request);
-
-    Futures.addCallback(
-        response,
-        new FutureCallback<Status>() {
-          @Override
-          public void onSuccess(Status result) {
-            if (result.getErrorCode() == ErrorCode.SUCCESS) {
-              logInfo("Created index successfully!\n{}", index.toString());
-            } else {
-              logError("CreateIndexAsync failed:\n{}\n{}", index.toString(), result.toString());
-            }
-          }
-
-          @Override
-          public void onFailure(Throwable t) {
-            logError("CreateIndexAsync failed:\n{}", t.getMessage());
-          }
-        },
-        MoreExecutors.directExecutor());
-
-    return Futures.transform(
-        response, transformStatusToResponseFunc::apply, MoreExecutors.directExecutor());
+  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();
+    });
   }
 
   @Override
-  public Response createPartition(String collectionName, String tag) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    PartitionParam request =
-        PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-
-    Status response;
-
-    try {
-      response = blockingStub().createPartition(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Created partition `{}` in collection `{}` successfully!", tag, collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError(
-            "Create partition `{}` in collection `{}` failed: {}",
-            tag,
-            collectionName,
-            response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("createPartition RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public void dropCollection(@Nonnull String collectionName) {
+    translateExceptions(() -> {
+      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
+      Status response = blockingStub().dropCollection(request);
+      checkResponseStatus(response);
+    });
   }
 
   @Override
-  public HasPartitionResponse hasPartition(String collectionName, String tag) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new HasPartitionResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), false);
-    }
-
-    PartitionParam request =
-        PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-    BoolReply response;
-
-    try {
-      response = blockingStub().hasPartition(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo(
-            "hasPartition with tag `{}` in `{}` = {}",
-            tag,
-            collectionName,
-            response.getBoolReply());
-        return new HasPartitionResponse(
-            new Response(Response.Status.SUCCESS), response.getBoolReply());
-      } else {
-        logError(
-            "hasPartition with tag `{}` in `{}` failed:\n{}",
-            tag,
-            collectionName,
-            response.toString());
-        return new HasPartitionResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            false);
-      }
-    } catch (StatusRuntimeException e) {
-      logError("hasPartition RPC failed:\n{}", e.getStatus().toString());
-      return new HasPartitionResponse(new Response(Response.Status.RPC_ERROR, e.toString()), false);
-    }
+  public void createIndex(@Nonnull Index index) {
+    translateExceptions(() -> {
+      Futures.getUnchecked(createIndexAsync(index));
+    });
   }
 
   @Override
-  public ListPartitionsResponse listPartitions(String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new ListPartitionsResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList());
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    PartitionList response;
-
-    try {
-      response = blockingStub().showPartitions(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo(
-            "Current partitions of collection {}: {}",
-            collectionName,
-            response.getPartitionTagArrayList());
-        return new ListPartitionsResponse(
-            new Response(Response.Status.SUCCESS), response.getPartitionTagArrayList());
-      } else {
-        logError("List partitions failed:\n{}", response.toString());
-        return new ListPartitionsResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            Collections.emptyList());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("listPartitions RPC failed:\n{}", e.getStatus().toString());
-      return new ListPartitionsResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), Collections.emptyList());
-    }
+  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());
+    });
   }
 
   @Override
-  public Response dropPartition(String collectionName, String tag) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    PartitionParam request =
-        PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
-    Status response;
-
-    try {
-      response = blockingStub().dropPartition(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Dropped partition `{}` in collection `{}` successfully!", tag, collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError(
-            "Drop partition `{}` in collection `{}` failed:\n{}",
-            tag,
-            collectionName,
-            response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("dropPartition RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public void createPartition(String collectionName, String tag) {
+    translateExceptions(() -> {
+      PartitionParam request = PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
+      Status response = blockingStub().createPartition(request);
+      checkResponseStatus(response);
+    });
   }
 
   @Override
-  public InsertResponse insert(@Nonnull InsertParam insertParam) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new InsertResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList());
-    }
-
-    List<RowRecord> rowRecordList =
-        buildRowRecordList(insertParam.getFloatVectors(), insertParam.getBinaryVectors());
-
-    io.milvus.grpc.InsertParam request =
-        io.milvus.grpc.InsertParam.newBuilder()
-            .setCollectionName(insertParam.getCollectionName())
-            .addAllRowRecordArray(rowRecordList)
-            .addAllRowIdArray(insertParam.getVectorIds())
-            .setPartitionTag(insertParam.getPartitionTag())
-            .build();
-    VectorIds response;
-
-    try {
-      response = blockingStub().insert(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo(
-            "Inserted {} vectors to collection `{}` successfully!",
-            response.getVectorIdArrayCount(),
-            insertParam.getCollectionName());
-        return new InsertResponse(
-            new Response(Response.Status.SUCCESS), response.getVectorIdArrayList());
-      } else {
-        logError("Insert vectors failed:\n{}", response.getStatus().toString());
-        return new InsertResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            Collections.emptyList());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("insert RPC failed:\n{}", e.getStatus().toString());
-      return new InsertResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), Collections.emptyList());
-    }
+  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();
+    });
   }
 
   @Override
-  public ListenableFuture<InsertResponse> insertAsync(@Nonnull InsertParam insertParam) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return Futures.immediateFuture(
-          new InsertResponse(
-              new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList()));
-    }
-
-    List<RowRecord> rowRecordList =
-        buildRowRecordList(insertParam.getFloatVectors(), insertParam.getBinaryVectors());
-
-    io.milvus.grpc.InsertParam request =
-        io.milvus.grpc.InsertParam.newBuilder()
-            .setCollectionName(insertParam.getCollectionName())
-            .addAllRowRecordArray(rowRecordList)
-            .addAllRowIdArray(insertParam.getVectorIds())
-            .setPartitionTag(insertParam.getPartitionTag())
-            .build();
-
-    ListenableFuture<VectorIds> response;
-
-    response = futureStub().insert(request);
-
-    Futures.addCallback(
-        response,
-        new FutureCallback<VectorIds>() {
-          @Override
-          public void onSuccess(VectorIds result) {
-            if (result.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-              logInfo(
-                  "Inserted {} vectors to collection `{}` successfully!",
-                  result.getVectorIdArrayCount(),
-                  insertParam.getCollectionName());
-            } else {
-              logError("InsertAsync failed:\n{}", result.getStatus().toString());
-            }
-          }
-
-          @Override
-          public void onFailure(Throwable t) {
-            logError("InsertAsync failed:\n{}", t.getMessage());
-          }
-        },
-        MoreExecutors.directExecutor());
-
-    Function<VectorIds, InsertResponse> transformFunc =
-        vectorIds -> {
-          if (vectorIds.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-            return new InsertResponse(
-                new Response(Response.Status.SUCCESS), vectorIds.getVectorIdArrayList());
-          } else {
-            return new InsertResponse(
-                new Response(
-                    Response.Status.valueOf(vectorIds.getStatus().getErrorCodeValue()),
-                    vectorIds.getStatus().getReason()),
-                Collections.emptyList());
-          }
-        };
-
-    return Futures.transform(response, transformFunc::apply, MoreExecutors.directExecutor());
+  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();
+    });
   }
 
   @Override
-  public SearchResponse search(@Nonnull SearchParam searchParam) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      SearchResponse searchResponse = new SearchResponse();
-      searchResponse.setResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED));
-      return searchResponse;
-    }
-
-    List<RowRecord> rowRecordList =
-        buildRowRecordList(searchParam.getFloatVectors(), searchParam.getBinaryVectors());
-
-    KeyValuePair extraParam =
-        KeyValuePair.newBuilder()
-            .setKey(extraParamKey)
-            .setValue(searchParam.getParamsInJson())
-            .build();
-
-    io.milvus.grpc.SearchParam request =
-        io.milvus.grpc.SearchParam.newBuilder()
-            .setCollectionName(searchParam.getCollectionName())
-            .addAllQueryRecordArray(rowRecordList)
-            .addAllPartitionTagArray(searchParam.getPartitionTags())
-            .setTopk(searchParam.getTopK())
-            .addExtraParams(extraParam)
-            .build();
-
-    TopKQueryResult response;
-
-    try {
-      response = blockingStub().search(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        SearchResponse searchResponse = buildSearchResponse(response);
-        searchResponse.setResponse(new Response(Response.Status.SUCCESS));
-        logInfo(
-            "Search completed successfully! Returned results for {} queries",
-            searchResponse.getNumQueries());
-        return searchResponse;
-      } else {
-        logError("Search failed:\n{}", response.getStatus().toString());
-        SearchResponse searchResponse = new SearchResponse();
-        searchResponse.setResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()));
-        return searchResponse;
-      }
-    } catch (StatusRuntimeException e) {
-      logError("search RPC failed:\n{}", e.getStatus().toString());
-      SearchResponse searchResponse = new SearchResponse();
-      searchResponse.setResponse(new Response(Response.Status.RPC_ERROR, e.toString()));
-      return searchResponse;
-    }
+  public void dropPartition(String collectionName, String tag) {
+    translateExceptions(() -> {
+      PartitionParam request =
+          PartitionParam.newBuilder().setCollectionName(collectionName).setTag(tag).build();
+      Status response = blockingStub().dropPartition(request);
+      checkResponseStatus(response);
+    });
   }
 
   @Override
-  public ListenableFuture<SearchResponse> searchAsync(@Nonnull SearchParam searchParam) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      SearchResponse searchResponse = new SearchResponse();
-      searchResponse.setResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED));
-      return Futures.immediateFuture(searchResponse);
-    }
-
-    List<RowRecord> rowRecordList =
-        buildRowRecordList(searchParam.getFloatVectors(), searchParam.getBinaryVectors());
-
-    KeyValuePair extraParam =
-        KeyValuePair.newBuilder()
-            .setKey(extraParamKey)
-            .setValue(searchParam.getParamsInJson())
-            .build();
-
-    io.milvus.grpc.SearchParam request =
-        io.milvus.grpc.SearchParam.newBuilder()
-            .setCollectionName(searchParam.getCollectionName())
-            .addAllQueryRecordArray(rowRecordList)
-            .addAllPartitionTagArray(searchParam.getPartitionTags())
-            .setTopk(searchParam.getTopK())
-            .addExtraParams(extraParam)
-            .build();
-
-    ListenableFuture<TopKQueryResult> response;
-
-    response = futureStub().search(request);
-
-    Futures.addCallback(
-        response,
-        new FutureCallback<TopKQueryResult>() {
-          @Override
-          public void onSuccess(TopKQueryResult result) {
-            if (result.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-              logInfo(
-                  "SearchAsync completed successfully! Returned results for {} queries",
-                  result.getRowNum());
-            } else {
-              logError("SearchAsync failed:\n{}", result.getStatus().toString());
-            }
-          }
-
-          @Override
-          public void onFailure(Throwable t) {
-            logError("SearchAsync failed:\n{}", t.getMessage());
-          }
-        },
-        MoreExecutors.directExecutor());
-
-    Function<TopKQueryResult, SearchResponse> transformFunc =
-        topKQueryResult -> {
-          if (topKQueryResult.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-            SearchResponse searchResponse = buildSearchResponse(topKQueryResult);
-            searchResponse.setResponse(new Response(Response.Status.SUCCESS));
-            return searchResponse;
-          } else {
-            SearchResponse searchResponse = new SearchResponse();
-            searchResponse.setResponse(
-                new Response(
-                    Response.Status.valueOf(topKQueryResult.getStatus().getErrorCodeValue()),
-                    topKQueryResult.getStatus().getReason()));
-            return searchResponse;
-          }
-        };
-
-    return Futures.transform(response, transformFunc::apply, MoreExecutors.directExecutor());
+  public List<Long> insert(@Nonnull InsertParam insertParam) {
+    return translateExceptions(() -> Futures.getUnchecked(insertAsync(insertParam)));
   }
 
   @Override
-  public GetCollectionInfoResponse getCollectionInfo(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new GetCollectionInfoResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    CollectionSchema response;
-
-    try {
-      response = blockingStub().describeCollection(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        CollectionMapping collectionMapping =
-            new CollectionMapping.Builder(response.getCollectionName(), response.getDimension())
-                .withIndexFileSize(response.getIndexFileSize())
-                .withMetricType(MetricType.valueOf(response.getMetricType()))
-                .build();
-        logInfo("Get Collection Info `{}` returned:\n{}", collectionName, collectionMapping);
-        return new GetCollectionInfoResponse(
-            new Response(Response.Status.SUCCESS), collectionMapping);
-      } else {
-        logError(
-            "Get Collection Info `{}` failed:\n{}",
-            collectionName,
-            response.getStatus().toString());
-        return new GetCollectionInfoResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            null);
-      }
-    } catch (StatusRuntimeException e) {
-      logError("getCollectionInfo RPC failed:\n{}", e.getStatus().toString());
-      return new GetCollectionInfoResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), null);
-    }
+  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());
+    });
   }
 
   @Override
-  public ListCollectionsResponse listCollections() {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new ListCollectionsResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList());
-    }
-
-    Command request = Command.newBuilder().setCmd("").build();
-    CollectionNameList response;
-
-    try {
-      response = blockingStub().showCollections(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        List<String> collectionNames = response.getCollectionNamesList();
-        logInfo("Current collections: {}", collectionNames.toString());
-        return new ListCollectionsResponse(new Response(Response.Status.SUCCESS), collectionNames);
-      } else {
-        logError("List collections failed:\n{}", response.getStatus().toString());
-        return new ListCollectionsResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            Collections.emptyList());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("listCollections RPC failed:\n{}", e.getStatus().toString());
-      return new ListCollectionsResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), Collections.emptyList());
-    }
+  public SearchResult search(@Nonnull SearchParam searchParam) {
+    return translateExceptions(() -> Futures.getUnchecked(searchAsync(searchParam)));
   }
 
   @Override
-  public CountEntitiesResponse countEntities(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new CountEntitiesResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), 0);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    CollectionRowCount response;
-
-    try {
-      response = blockingStub().countCollection(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        long collectionRowCount = response.getCollectionRowCount();
-        logInfo("Collection `{}` has {} entities", collectionName, collectionRowCount);
-        return new CountEntitiesResponse(new Response(Response.Status.SUCCESS), collectionRowCount);
-      } else {
-        logError(
-            "Get collection `{}` entity count failed:\n{}",
-            collectionName,
-            response.getStatus().toString());
-        return new CountEntitiesResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            0);
-      }
-    } catch (StatusRuntimeException e) {
-      logError("countEntities RPC failed:\n{}", e.getStatus().toString());
-      return new CountEntitiesResponse(new Response(Response.Status.RPC_ERROR, e.toString()), 0);
-    }
+  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());
+    });
   }
 
   @Override
-  public Response getServerStatus() {
-    return command("status");
+  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);
+    });
   }
 
   @Override
-  public Response getServerVersion() {
-    return command("version");
+  public List<String> listCollections() {
+    return translateExceptions(() -> {
+      Command request = Command.newBuilder().setCmd("").build();
+      CollectionNameList response = blockingStub().showCollections(request);
+      checkResponseStatus(response.getStatus());
+      return response.getCollectionNamesList();
+    });
   }
 
-  public Response command(@Nonnull String command) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    Command request = Command.newBuilder().setCmd(command).build();
-    StringReply response;
-
-    try {
-      response = blockingStub().cmd(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Command `{}`: {}", command, response.getStringReply());
-        return new Response(Response.Status.SUCCESS, response.getStringReply());
-      } else {
-        logError("Command `{}` failed:\n{}", command, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-            response.getStatus().getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("Command RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  @Override
+  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();
+    });
   }
 
   @Override
-  public Response loadCollection(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    Status response;
-
-    try {
-      response = blockingStub().preloadCollection(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Loaded collection `{}` successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Load collection `{}` failed:\n{}", collectionName, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("loadCollection RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public String getServerStatus() {
+    return command("status");
   }
 
   @Override
-  public GetIndexInfoResponse getIndexInfo(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new GetIndexInfoResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    IndexParam response;
-
-    try {
-      response = blockingStub().describeIndex(request);
+  public String getServerVersion() {
+    return command("version");
+  }
 
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        String extraParam = "";
-        for (KeyValuePair kv : response.getExtraParamsList()) {
-          if (kv.getKey().contentEquals(extraParamKey)) {
-            extraParam = kv.getValue();
-          }
+  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();
+    });
+  }
+
+  @Override
+  public void loadCollection(@Nonnull String collectionName) {
+    translateExceptions(() -> {
+      CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
+      Status response = blockingStub().preloadCollection(request);
+      checkResponseStatus(response);
+    });
+  }
+
+  @Override
+  public void dropIndex(String collectionName, String fieldName) {
+    translateExceptions(() -> {
+      IndexParam request = IndexParam.newBuilder()
+          .setCollectionName(collectionName)
+          .setFieldName(fieldName)
+          .build();
+      Status response = blockingStub().dropIndex(request);
+      checkResponseStatus(response);
+    });
+  }
+
+  @Override
+  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));
         }
-        Index index =
-            new Index.Builder(
-                    response.getCollectionName(), IndexType.valueOf(response.getIndexType()))
-                .withParamsInJson(extraParam)
-                .build();
-        logInfo(
-            "Get index info for collection `{}` returned:\n{}", collectionName, index.toString());
-        return new GetIndexInfoResponse(new Response(Response.Status.SUCCESS), index);
-      } else {
-        logError(
-            "Get index info for collection `{}` failed:\n{}",
-            collectionName,
-            response.getStatus().toString());
-        return new GetIndexInfoResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            null);
       }
-    } catch (StatusRuntimeException e) {
-      logError("getIndexInfo RPC failed:\n{}", e.getStatus().toString());
-      return new GetIndexInfoResponse(new Response(Response.Status.RPC_ERROR, e.toString()), null);
-    }
+      return entities;
+    });
   }
-
-  @Override
-  public Response dropIndex(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    Status response;
-
-    try {
-      response = blockingStub().dropIndex(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Dropped index for collection `{}` successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Drop index for collection `{}` failed:\n{}", collectionName, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("dropIndex RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  
+  private Map<String, Object> toMap(Map<String, Iterator<?>> fieldIterators) {
+    return fieldIterators.entrySet().stream()
+        .collect(Collectors.toMap(
+            entry -> entry.getKey(),
+            entry -> entry.getValue().next()));
   }
 
-  @Override
-  public Response getCollectionStats(String collectionName) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    io.milvus.grpc.CollectionInfo response;
-
-    try {
-      response = blockingStub().showCollectionInfo(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("getCollectionStats for `{}` returned successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS, response.getJsonInfo());
-      } else {
-        logError(
-            "getCollectionStats for `{}` failed:\n{}",
-            collectionName,
-            response.getStatus().toString());
-        return new Response(
-            Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-            response.getStatus().getReason());
+  private Iterator<?> fieldValueIterator(FieldValue fieldValue) {
+    if (fieldValue.hasAttrRecord()) {
+      AttrRecord record = fieldValue.getAttrRecord();
+      if (record.getInt32ValueCount() > 0) {
+        return record.getInt32ValueList().iterator();
+      } else if (record.getInt64ValueCount() > 0) {
+        return record.getInt64ValueList().iterator();
+      } else if (record.getFloatValueCount() > 0) {
+        return record.getFloatValueList().iterator();
+      } else if (record.getDoubleValueCount() > 0) {
+        return record.getDoubleValueList().iterator();
       }
-    } catch (StatusRuntimeException e) {
-      logError("getCollectionStats RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
     }
+    VectorRecord record = fieldValue.getVectorRecord();
+    return record.getRecordsList().stream()
+        .map(row -> row.getFloatDataCount() > 0 ? row.getFloatDataList() : row.getBinaryData().asReadOnlyByteBuffer())
+        .iterator();
   }
 
   @Override
-  public GetEntityByIDResponse getEntityByID(String collectionName, List<Long> ids) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new GetEntityByIDResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList(), null);
-    }
-
-    VectorsIdentity request =
-        VectorsIdentity.newBuilder().setCollectionName(collectionName).addAllIdArray(ids).build();
-    VectorsData response;
-
-    try {
-      response = blockingStub().getVectorsByID(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-
-        logInfo("getEntityByID in collection `{}` returned successfully!", collectionName);
-
-        List<List<Float>> floatVectors = new ArrayList<>(ids.size());
-        List<ByteBuffer> binaryVectors = new ArrayList<>(ids.size());
-        for (int i = 0; i < ids.size(); i++) {
-          floatVectors.add(response.getVectorsData(i).getFloatDataList());
-          binaryVectors.add(response.getVectorsData(i).getBinaryData().asReadOnlyByteBuffer());
-        }
-        return new GetEntityByIDResponse(
-            new Response(Response.Status.SUCCESS), floatVectors, binaryVectors);
-
-      } else {
-        logError(
-            "getEntityByID in collection `{}` failed:\n{}",
-            collectionName,
-            response.getStatus().toString());
-        return new GetEntityByIDResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            Collections.emptyList(),
-            null);
-      }
-    } catch (StatusRuntimeException e) {
-      logError("getEntityByID RPC failed:\n{}", e.getStatus().toString());
-      return new GetEntityByIDResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), Collections.emptyList(), null);
-    }
+  public Map<Long, Map<String, Object>> getEntityByID(String collectionName, List<Long> ids) {
+    return getEntityByID(collectionName, ids, Collections.emptyList());
   }
 
   @Override
-  public ListIDInSegmentResponse listIDInSegment(String collectionName, String segmentName) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new ListIDInSegmentResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), Collections.emptyList());
-    }
-
-    GetVectorIDsParam request =
-        GetVectorIDsParam.newBuilder()
-            .setCollectionName(collectionName)
-            .setSegmentName(segmentName)
-            .build();
-    VectorIds response;
-
-    try {
-      response = blockingStub().getVectorIDs(request);
-
-      if (response.getStatus().getErrorCode() == ErrorCode.SUCCESS) {
-
-        logInfo(
-            "listIDInSegment in collection `{}`, segment `{}` returned successfully!",
-            collectionName,
-            segmentName);
-        return new ListIDInSegmentResponse(
-            new Response(Response.Status.SUCCESS), response.getVectorIdArrayList());
-      } else {
-        logError(
-            "listIDInSegment in collection `{}`, segment `{}` failed:\n{}",
-            collectionName,
-            segmentName,
-            response.getStatus().toString());
-        return new ListIDInSegmentResponse(
-            new Response(
-                Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            Collections.emptyList());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("listIDInSegment RPC failed:\n{}", e.getStatus().toString());
-      return new ListIDInSegmentResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), Collections.emptyList());
-    }
+  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();
+    });
   }
 
   @Override
-  public Response deleteEntityByID(String collectionName, List<Long> ids) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    DeleteByIDParam request =
-        DeleteByIDParam.newBuilder().setCollectionName(collectionName).addAllIdArray(ids).build();
-    Status response;
-
-    try {
-      response = blockingStub().deleteByID(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("deleteEntityByID in collection `{}` completed successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError(
-            "deleteEntityByID in collection `{}` failed:\n{}", collectionName, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("deleteEntityByID RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  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);
+    });
   }
 
   @Override
-  public Response flush(List<String> collectionNames) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    FlushParam request = FlushParam.newBuilder().addAllCollectionNameArray(collectionNames).build();
-    Status response;
-
-    try {
-      response = blockingStub().flush(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Flushed collection {} successfully!", collectionNames);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Flush collection {} failed:\n{}", collectionNames, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("flush RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public void flush(List<String> collectionNames) {
+    translateExceptions(() -> Futures.getUnchecked(flushAsync(collectionNames)));
   }
 
   @Override
-  public ListenableFuture<Response> flushAsync(@Nonnull List<String> collectionNames) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return Futures.immediateFuture(new Response(Response.Status.CLIENT_NOT_CONNECTED));
-    }
-
-    FlushParam request = FlushParam.newBuilder().addAllCollectionNameArray(collectionNames).build();
-
-    ListenableFuture<Status> response;
-
-    response = futureStub().flush(request);
-
-    Futures.addCallback(
-        response,
-        new FutureCallback<Status>() {
-          @Override
-          public void onSuccess(Status result) {
-            if (result.getErrorCode() == ErrorCode.SUCCESS) {
-              logInfo("Flushed collection {} successfully!", collectionNames);
-            } else {
-              logError("Flush collection {} failed:\n{}", collectionNames, result.toString());
-            }
-          }
-
-          @Override
-          public void onFailure(Throwable t) {
-            logError("FlushAsync failed:\n{}", t.getMessage());
-          }
-        },
-        MoreExecutors.directExecutor());
-
-    return Futures.transform(
-        response, transformStatusToResponseFunc::apply, MoreExecutors.directExecutor());
+  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());
+    });
   }
 
   @Override
-  public Response flush(String collectionName) {
-    List<String> list =
-        new ArrayList<String>() {
-          {
-            add(collectionName);
-          }
-        };
-    return flush(list);
+  public void flush(String collectionName) {
+    flush(Collections.singletonList(collectionName));
   }
 
   @Override
-  public ListenableFuture<Response> flushAsync(String collectionName) {
-    List<String> list =
-        new ArrayList<String>() {
-          {
-            add(collectionName);
-          }
-        };
-    return flushAsync(list);
+  public ListenableFuture<Void> flushAsync(String collectionName) {
+    return flushAsync(Collections.singletonList(collectionName));
   }
 
   @Override
-  public Response compact(String collectionName) {
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return new Response(Response.Status.CLIENT_NOT_CONNECTED);
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-    Status response;
-
-    try {
-      response = blockingStub().compact(request);
-
-      if (response.getErrorCode() == ErrorCode.SUCCESS) {
-        logInfo("Compacted collection `{}` successfully!", collectionName);
-        return new Response(Response.Status.SUCCESS);
-      } else {
-        logError("Compact collection `{}` failed:\n{}", collectionName, response.toString());
-        return new Response(
-            Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
-      }
-    } catch (StatusRuntimeException e) {
-      logError("compact RPC failed:\n{}", e.getStatus().toString());
-      return new Response(Response.Status.RPC_ERROR, e.toString());
-    }
+  public void compact(CompactParam compactParam) {
+    translateExceptions(() -> Futures.getUnchecked(compactAsync(compactParam)));
   }
 
   @Override
-  public ListenableFuture<Response> compactAsync(@Nonnull String collectionName) {
-
-    if (!maybeAvailable()) {
-      logWarning("You are not connected to Milvus server");
-      return Futures.immediateFuture(new Response(Response.Status.CLIENT_NOT_CONNECTED));
-    }
-
-    CollectionName request = CollectionName.newBuilder().setCollectionName(collectionName).build();
-
-    ListenableFuture<Status> response;
+  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());
+    });
+  }
 
-    response = futureStub().compact(request);
+  ///////////////////// Util Functions/////////////////////
+  private SearchResult buildSearchResponse(QueryResult topKQueryResult) {
+    final int numQueries = (int) topKQueryResult.getRowNum();
+    final int topK = numQueries == 0 ? 0 : topKQueryResult.getDistancesCount() / numQueries;
 
-    Futures.addCallback(
-        response,
-        new FutureCallback<Status>() {
-          @Override
-          public void onSuccess(Status result) {
-            if (result.getErrorCode() == ErrorCode.SUCCESS) {
-              logInfo("Compacted collection `{}` successfully!", collectionName);
+    List<List<Long>> resultIdsList = new ArrayList<>(numQueries);
+    List<List<Float>> resultDistancesList = new ArrayList<>(numQueries);
+    List<List<Map<String, Object>>> resultFieldsMap = new ArrayList<>(numQueries);
+
+    Entities entities = topKQueryResult.getEntities();
+    List<Long> queryIdsList = entities.getIdsList();
+    List<Float> queryDistancesList = topKQueryResult.getDistancesList();
+
+    // If fields specified, put it into searchResponse
+    List<Map<String, Object>> fieldsMap = new ArrayList<>();
+    for (int i = 0; i < queryIdsList.size(); i++) {
+      fieldsMap.add(new HashMap<>());
+    }
+    if (entities.getValidRowCount() != 0) {
+      List<FieldValue> fieldValueList = entities.getFieldsList();
+      for (FieldValue fieldValue : fieldValueList) {
+        String fieldName = fieldValue.getFieldName();
+        for (int j = 0; j < queryIdsList.size(); j++) {
+          if (fieldValue.getAttrRecord().getInt32ValueCount() > 0) {
+            fieldsMap.get(j).put(fieldName, fieldValue.getAttrRecord().getInt32ValueList().get(j));
+          } else if (fieldValue.getAttrRecord().getInt64ValueCount() > 0) {
+            fieldsMap.get(j).put(fieldName, fieldValue.getAttrRecord().getInt64ValueList().get(j));
+          } else if (fieldValue.getAttrRecord().getDoubleValueCount() > 0) {
+            fieldsMap.get(j).put(fieldName, fieldValue.getAttrRecord().getDoubleValueList().get(j));
+          } else if (fieldValue.getAttrRecord().getFloatValueCount() > 0) {
+            fieldsMap.get(j).put(fieldName, fieldValue.getAttrRecord().getFloatValueList().get(j));
+          } else {
+            // the object is vector
+            List<VectorRowRecord> vectorRowRecordList =
+                fieldValue.getVectorRecord().getRecordsList();
+            if (vectorRowRecordList.get(j).getFloatDataCount() > 0) {
+              fieldsMap.get(j).put(fieldName, vectorRowRecordList.get(j).getFloatDataList());
             } else {
-              logError("Compact collection `{}` failed:\n{}", collectionName, result.toString());
+              fieldsMap.get(j).put(fieldName, vectorRowRecordList.get(j).getBinaryData().asReadOnlyByteBuffer());
             }
           }
-
-          @Override
-          public void onFailure(Throwable t) {
-            logError("CompactAsync failed:\n{}", t.getMessage());
-          }
-        },
-        MoreExecutors.directExecutor());
-
-    return Futures.transform(
-        response, transformStatusToResponseFunc::apply, MoreExecutors.directExecutor());
-  }
-
-  ///////////////////// Util Functions/////////////////////
-  Function<Status, Response> transformStatusToResponseFunc =
-      status -> {
-        if (status.getErrorCode() == ErrorCode.SUCCESS) {
-          return new Response(Response.Status.SUCCESS);
-        } else {
-          return new Response(
-              Response.Status.valueOf(status.getErrorCodeValue()), status.getReason());
         }
-      };
-
-  private List<RowRecord> buildRowRecordList(
-      @Nonnull List<List<Float>> floatVectors, @Nonnull List<ByteBuffer> binaryVectors) {
-    int largerSize = Math.max(floatVectors.size(), binaryVectors.size());
-    
-    List<RowRecord> rowRecordList = new ArrayList<>(largerSize); 
-
-    for (int i = 0; i < largerSize; ++i) {
-
-      RowRecord.Builder rowRecordBuilder = RowRecord.newBuilder();
-
-      if (i < floatVectors.size()) {
-        rowRecordBuilder.addAllFloatData(floatVectors.get(i));
       }
-      if (i < binaryVectors.size()) {
-        ((Buffer) binaryVectors.get(i)).rewind();
-        rowRecordBuilder.setBinaryData(ByteString.copyFrom(binaryVectors.get(i)));
-      }
-
-      rowRecordList.add(rowRecordBuilder.build());
     }
 
-    return rowRecordList;
-  }
-
-  private SearchResponse buildSearchResponse(TopKQueryResult topKQueryResult) {
-
-    final int numQueries = (int) topKQueryResult.getRowNum();
-    final int topK =
-        numQueries == 0
-            ? 0
-            : topKQueryResult.getIdsCount()
-                / numQueries; // Guaranteed to be divisible from server side
-
-    List<List<Long>> resultIdsList = new ArrayList<>(numQueries);
-    List<List<Float>> resultDistancesList = new ArrayList<>(numQueries);
-
     if (topK > 0) {
       for (int i = 0; i < numQueries; i++) {
         // Process result of query i
         int pos = i * topK;
-        while (pos < i * topK + topK && topKQueryResult.getIdsList().get(pos) != -1) {
+        while (pos < i * topK + topK && queryIdsList.get(pos) != -1) {
           pos++;
         }
-        resultIdsList.add(topKQueryResult.getIdsList().subList(i * topK, pos));
-        resultDistancesList.add(topKQueryResult.getDistancesList().subList(i * topK, pos));
+        resultIdsList.add(queryIdsList.subList(i * topK, pos));
+        resultDistancesList.add(queryDistancesList.subList(i * topK, pos));
+        resultFieldsMap.add(fieldsMap.subList(i * topK, pos));
       }
     }
 
-    SearchResponse searchResponse = new SearchResponse();
-    searchResponse.setNumQueries(numQueries);
-    searchResponse.setTopK(topK);
-    searchResponse.setResultIdsList(resultIdsList);
-    searchResponse.setResultDistancesList(resultDistancesList);
-
-    return searchResponse;
-  }
-
-  ///////////////////// Log Functions//////////////////////
-
-  private void logInfo(String msg, Object... params) {
-    logger.info(msg, params);
-  }
-
-  private void logWarning(String msg, Object... params) {
-    logger.warn(msg, params);
-  }
-
-  private void logError(String msg, Object... params) {
-    logger.error(msg, params);
+    return new SearchResult(numQueries, topK, resultIdsList, resultDistancesList, resultFieldsMap);
   }
 }

+ 0 - 115
src/main/java/io/milvus/client/Response.java

@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-import java.util.Arrays;
-import java.util.Optional;
-
-/**
- * Represents response of a client call. Contains a <code>status</code> and a <code>message</code>
- */
-public class Response {
-
-  private final Status status;
-  private final String message;
-
-  public Response(Status status, String message) {
-    this.status = status;
-    this.message = message;
-  }
-
-  public Response(Status status) {
-    this.status = status;
-    if (status == Status.CLIENT_NOT_CONNECTED) {
-      this.message = "You are not connected to Milvus server";
-    } else {
-      this.message = "Success!";
-    }
-  }
-
-  public Status getStatus() {
-    return status;
-  }
-
-  public String getMessage() {
-    return message;
-  }
-
-  /** @return <code>true</code> if status equals SUCCESS */
-  public boolean ok() {
-    return status == Status.SUCCESS;
-  }
-
-  @Override
-  public String toString() {
-    return String.format("Response {code = %s, message = \"%s\"}", status.name(), this.message);
-  }
-
-  /** Represents server and client side status code */
-  public enum Status {
-    // Server side error
-    SUCCESS(0),
-    UNEXPECTED_ERROR(1),
-    CONNECT_FAILED(2),
-    PERMISSION_DENIED(3),
-    COLLECTION_NOT_EXISTS(4),
-    ILLEGAL_ARGUMENT(5),
-    ILLEGAL_DIMENSION(7),
-    ILLEGAL_INDEX_TYPE(8),
-    ILLEGAL_COLLECTION_NAME(9),
-    ILLEGAL_TOPK(10),
-    ILLEGAL_ROWRECORD(11),
-    ILLEGAL_VECTOR_ID(12),
-    ILLEGAL_SEARCH_RESULT(13),
-    FILE_NOT_FOUND(14),
-    META_FAILED(15),
-    CACHE_FAILED(16),
-    CANNOT_CREATE_FOLDER(17),
-    CANNOT_CREATE_FILE(18),
-    CANNOT_DELETE_FOLDER(19),
-    CANNOT_DELETE_FILE(20),
-    BUILD_INDEX_ERROR(21),
-    ILLEGAL_NLIST(22),
-    ILLEGAL_METRIC_TYPE(23),
-    OUT_OF_MEMORY(24),
-
-    // Client side error
-    RPC_ERROR(-1),
-    CLIENT_NOT_CONNECTED(-2),
-    UNKNOWN(-3),
-    VERSION_MISMATCH(-4);
-
-    private final int code;
-
-    Status(int code) {
-      this.code = code;
-    }
-
-    public static Status valueOf(int val) {
-      Optional<Status> search =
-          Arrays.stream(values()).filter(status -> status.code == val).findFirst();
-      return search.orElse(UNKNOWN);
-    }
-
-    public int getCode() {
-      return code;
-    }
-  }
-}

+ 125 - 122
src/main/java/io/milvus/client/SearchParam.java

@@ -19,152 +19,155 @@
 
 package io.milvus.client;
 
+import com.google.common.collect.ImmutableList;
+import com.google.protobuf.ByteString;
+import com.google.protobuf.UnsafeByteOperations;
+import io.milvus.client.exception.InvalidDsl;
+import io.milvus.grpc.KeyValuePair;
+import io.milvus.grpc.VectorParam;
+import io.milvus.grpc.VectorRecord;
+import io.milvus.grpc.VectorRowRecord;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
-import javax.annotation.Nonnull;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /** Contains parameters for <code>search</code> */
 public class SearchParam {
+  private static final String VECTOR_QUERY_KEY = "vector";
+  private static final String VECTOR_QUERY_PLACEHOLDER = "placeholder";
 
-  private final String collectionName;
-  private final List<List<Float>> floatVectors;
-  private final List<ByteBuffer> binaryVectors;
-  private final List<String> partitionTags;
-  private final long topK;
-  private final String paramsInJson;
-
-  private SearchParam(@Nonnull Builder builder) {
-    this.collectionName = builder.collectionName;
-    this.floatVectors = builder.floatVectors;
-    this.binaryVectors = builder.binaryVectors;
-    this.partitionTags = builder.partitionTags;
-    this.topK = builder.topK;
-    this.paramsInJson = builder.paramsInJson;
-  }
+  private io.milvus.grpc.SearchParam.Builder builder;
 
-  public String getCollectionName() {
-    return collectionName;
+  public static SearchParam create(String collectionName) {
+    return new SearchParam(collectionName);
   }
 
-  public List<List<Float>> getFloatVectors() {
-    return floatVectors;
+  private SearchParam(String collectionName) {
+    builder = io.milvus.grpc.SearchParam.newBuilder();
+    builder.setCollectionName(collectionName);
   }
 
-  public List<ByteBuffer> getBinaryVectors() {
-    return binaryVectors;
+  public SearchParam setDsl(String dsl) {
+    try {
+      JSONObject dslJson = new JSONObject(dsl);
+      JSONObject vectorQueryParent = locateVectorQuery(dslJson)
+          .orElseThrow(() -> new InvalidDsl("A vector query must be specified", dsl));
+      JSONObject vectorQueries = vectorQueryParent.getJSONObject(VECTOR_QUERY_KEY);
+      vectorQueryParent.put(VECTOR_QUERY_KEY, VECTOR_QUERY_PLACEHOLDER);
+      String vectorQueryField = vectorQueries.keys().next();
+      JSONObject vectorQuery = vectorQueries.getJSONObject(vectorQueryField);
+      String vectorQueryType = vectorQuery.getString("type");
+      JSONArray vectorQueryData = vectorQuery.getJSONArray("query");
+
+      VectorRecord vectorRecord;
+      switch (vectorQueryType) {
+        case "float":
+          vectorRecord = toFloatVectorRecord(vectorQueryData);
+          break;
+        case "binary":
+          vectorRecord = toBinaryVectorRecord(vectorQueryData);
+          break;
+        default:
+          throw new InvalidDsl("Unsupported vector type: " + vectorQueryType, dsl);
+      }
+
+      JSONObject json = new JSONObject();
+      vectorQuery.remove("type");
+      vectorQuery.remove("query");
+      json.put("placeholder", vectorQueries);
+      VectorParam vectorParam = VectorParam.newBuilder()
+          .setJson(json.toString())
+          .setRowRecord(vectorRecord)
+          .build();
+
+      builder.setDsl(dslJson.toString())
+          .addAllVectorParam(ImmutableList.of(vectorParam));
+      return this;
+    } catch (JSONException e) {
+      throw new InvalidDsl(e.getMessage(), dsl);
+    }
   }
 
-  public List<String> getPartitionTags() {
-    return partitionTags;
+  public SearchParam setPartitionTags(List<String> partitionTags) {
+    builder.addAllPartitionTagArray(partitionTags);
+    return this;
   }
 
-  public long getTopK() {
-    return topK;
+  public SearchParam setParamsInJson(String paramsInJson) {
+    builder.addExtraParams(KeyValuePair.newBuilder()
+        .setKey(MilvusClient.extraParamKey)
+        .setValue(paramsInJson)
+        .build());
+    return this;
   }
 
-  public String getParamsInJson() {
-    return paramsInJson;
+  io.milvus.grpc.SearchParam grpc() {
+    return builder.build();
   }
 
-  /** Builder for <code>SearchParam</code> */
-  public static class Builder {
-    // Required parameters
-    private final String collectionName;
-
-    // Optional parameters - initialized to default values
-    private List<List<Float>> floatVectors = new ArrayList<>();
-    private List<ByteBuffer> binaryVectors = new ArrayList<>();
-    private List<String> partitionTags = new ArrayList<>();
-    private long topK = 1024;
-    private String paramsInJson;
-
-    /** @param collectionName collection to search from */
-    public Builder(@Nonnull String collectionName) {
-      this.collectionName = collectionName;
-    }
-
-    /**
-     * Default to an empty <code>ArrayList</code>. You can search either float or binary vectors,
-     * not both.
-     *
-     * @param floatVectors a <code>List</code> of float vectors to be queries. Each inner <code>List
-     *     </code> represents a float vector.
-     * @return <code>Builder</code>
-     */
-    public SearchParam.Builder withFloatVectors(@Nonnull List<List<Float>> floatVectors) {
-      this.floatVectors = floatVectors;
-      return this;
-    }
-
-    /**
-     * Default to an empty <code>ArrayList</code>. You can search either float or binary vectors,
-     * not both.
-     *
-     * @param binaryVectors a <code>List</code> of binary vectors to be queried. Each <code>
-     *     ByteBuffer</code> object represents a binary vector, with every 8 bits constituting a
-     *     byte.
-     * @return <code>Builder</code>
-     * @see ByteBuffer
-     */
-    public SearchParam.Builder withBinaryVectors(@Nonnull List<ByteBuffer> binaryVectors) {
-      this.binaryVectors = binaryVectors;
-      return this;
-    }
+  private Optional<JSONObject> locateVectorQuery(Object obj) {
+    return obj instanceof JSONObject ? locateVectorQuery((JSONObject) obj)
+        : obj instanceof JSONArray ? locateVectorQuery((JSONArray) obj)
+        : Optional.empty();
+  }
 
-    /**
-     * Optional. Search vectors with corresponding <code>partitionTags</code>. Default to an empty
-     * <code>List</code>
-     *
-     * @param partitionTags a <code>List</code> of partition tags
-     * @return <code>Builder</code>
-     */
-    public Builder withPartitionTags(@Nonnull List<String> partitionTags) {
-      this.partitionTags = partitionTags;
-      return this;
-    }
+  private Optional<JSONObject> locateVectorQuery(JSONArray array) {
+    return StreamSupport.stream(array.spliterator(), false)
+        .map(this::locateVectorQuery)
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .findFirst();
+  }
 
-    /**
-     * Optional. Limits search result to <code>topK</code>. Default to 1024.
-     *
-     * @param topK a topK number
-     * @return <code>Builder</code>
-     */
-    public Builder withTopK(long topK) {
-      this.topK = topK;
-      return this;
+  private Optional<JSONObject> locateVectorQuery(JSONObject obj) {
+    if (obj.opt(VECTOR_QUERY_KEY) instanceof JSONObject) {
+      return Optional.of(obj);
     }
+    return obj.keySet().stream()
+        .map(key -> locateVectorQuery(obj.get(key)))
+        .filter(Optional::isPresent)
+        .map(Optional::get)
+        .findFirst();
+  }
 
-    /**
-     * Optional. Default to empty <code>String</code>. Search parameters are different for different
-     * index types. Refer to <a
-     * href="https://milvus.io/docs/milvus_operation.md">https://milvus.io/docs/milvus_operation.md</a>
-     * for more information.
-     *
-     * <pre>
-     *   FLAT/IVFLAT/SQ8/IVFPQ: {"nprobe": 32}
-     *   nprobe range:[1,999999]
-     *
-     *   NSG: {"search_length": 100}
-     *   search_length range:[10, 300]
-     *
-     *   HNSW: {"ef": 64}
-     *   ef range:[topk, 4096]
-     *
-     *   ANNOY: {search_k", 0.05 * totalDataCount}
-     *   search_k range: none
-     * </pre>
-     *
-     * @param paramsInJson extra parameters in JSON format
-     * @return <code>Builder</code>
-     */
-    public SearchParam.Builder withParamsInJson(@Nonnull String paramsInJson) {
-      this.paramsInJson = paramsInJson;
-      return this;
-    }
+  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()))
+        .build();
+  }
 
-    public SearchParam build() {
-      return new SearchParam(this);
-    }
+  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()))
+        .build();
   }
 }

+ 0 - 137
src/main/java/io/milvus/client/SearchResponse.java

@@ -1,137 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package io.milvus.client;
-
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.LongStream;
-
-/**
- * Contains the returned <code>response</code> and <code>queryResultsList</code> for <code>search
- * </code>
- */
-public class SearchResponse {
-
-  private Response response;
-  private int numQueries;
-  private long topK;
-  private List<List<Long>> resultIdsList;
-  private List<List<Float>> resultDistancesList;
-
-  public int getNumQueries() {
-    return numQueries;
-  }
-
-  void setNumQueries(int numQueries) {
-    this.numQueries = numQueries;
-  }
-
-  public long getTopK() {
-    return topK;
-  }
-
-  void setTopK(long topK) {
-    this.topK = topK;
-  }
-
-  /**
-   * @return a <code>List</code> of <code>QueryResult</code>s. Each inner <code>List</code> contains
-   *     the query result of a vector.
-   */
-  public List<List<QueryResult>> getQueryResultsList() {
-    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()))
-        .collect(Collectors.toList());
-  }
-
-  /**
-   * @return a <code>List</code> of result ids. Each inner <code>List</code> contains the result ids
-   *     of a vector.
-   */
-  public List<List<Long>> getResultIdsList() {
-    return resultIdsList;
-  }
-
-  void setResultIdsList(List<List<Long>> resultIdsList) {
-    this.resultIdsList = resultIdsList;
-  }
-
-  /**
-   * @return @return a <code>List</code> of result distances. Each inner <code>List</code> contains
-   *     the result distances of a vector.
-   */
-  public List<List<Float>> getResultDistancesList() {
-    return resultDistancesList;
-  }
-
-  void setResultDistancesList(List<List<Float>> resultDistancesList) {
-    this.resultDistancesList = resultDistancesList;
-  }
-
-  public Response getResponse() {
-    return response;
-  }
-
-  void setResponse(Response response) {
-    this.response = response;
-  }
-
-  /** @return <code>true</code> if the response status equals SUCCESS */
-  public boolean ok() {
-    return response.ok();
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "SearchResponse {%s, returned results for %d queries}", response.toString(), numQueries);
-  }
-
-  /**
-   * Represents a single result of a vector query. Contains the result <code>vectorId</code> and its
-   * <code>distance</code> to the vector being queried
-   */
-  public static class QueryResult {
-    private final long vectorId;
-    private final float distance;
-
-    QueryResult(long vectorId, float distance) {
-      this.vectorId = vectorId;
-      this.distance = distance;
-    }
-
-    public long getVectorId() {
-      return vectorId;
-    }
-
-    public float getDistance() {
-      return distance;
-    }
-  }
-}

+ 72 - 0
src/main/java/io/milvus/client/SearchResult.java

@@ -0,0 +1,72 @@
+package io.milvus.client;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+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;
+
+  public SearchResult(int numQueries,
+                      long topK,
+                      List<List<Long>> resultIdsList,
+                      List<List<Float>> resultDistancesList,
+                      List<List<Map<String, Object>>> fieldsMap) {
+    this.numQueries = numQueries;
+    this.topK = topK;
+    this.resultIdsList = resultIdsList;
+    this.resultDistancesList = resultDistancesList;
+    this.fieldsMap = fieldsMap;
+  }
+
+  public int getNumQueries() {
+    return numQueries;
+  }
+
+  public long getTopK() {
+    return topK;
+  }
+
+  public List<List<Long>> getResultIdsList() {
+    return resultIdsList;
+  }
+
+  public List<List<Float>> getResultDistancesList() {
+    return resultDistancesList;
+  }
+
+  public List<List<Map<String, Object>>> getFieldsMap() {
+    return fieldsMap;
+  }
+
+  public List<List<QueryResult>> getQueryResultsList() {
+    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()))
+        .collect(Collectors.toList());
+  }
+
+  public static class QueryResult {
+    private final long entityId;
+    private final float distance;
+
+    QueryResult(long entityId, float distance) {
+      this.entityId = entityId;
+      this.distance = distance;
+    }
+
+    public long getEntityId() {
+      return entityId;
+    }
+
+    public float getDistance() {
+      return distance;
+    }
+  }
+}

+ 16 - 0
src/main/java/io/milvus/client/exception/ClientSideMilvusException.java

@@ -0,0 +1,16 @@
+package io.milvus.client.exception;
+
+public class ClientSideMilvusException extends MilvusException {
+
+  public ClientSideMilvusException(String target) {
+    super(target, false, null, null);
+  }
+
+  public ClientSideMilvusException(String target, Throwable cause) {
+    super(target, false, null, cause);
+  }
+
+  public ClientSideMilvusException(String target, String message) {
+    super(target, false, message, null);
+  }
+}

+ 0 - 16
src/main/java/io/milvus/client/exception/InitializationException.java

@@ -1,16 +0,0 @@
-package io.milvus.client.exception;
-
-public class InitializationException extends MilvusException {
-  private String host;
-  private Throwable cause;
-
-  public InitializationException(String host, Throwable cause) {
-    super(false, cause);
-    this.host = host;
-  }
-
-  public InitializationException(String host, String message) {
-    super(false, message);
-    this.host = host;
-  }
-}

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

@@ -0,0 +1,15 @@
+package io.milvus.client.exception;
+
+public class InvalidDsl extends ClientSideMilvusException {
+  private String dsl;
+
+  public InvalidDsl(String dsl, String message) {
+    super(null, message);
+    this.dsl = dsl;
+  }
+
+  @Override
+  protected String getErrorMessage() {
+    return super.getErrorMessage() + ": " + dsl;
+  }
+}

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

@@ -1,20 +1,26 @@
 package io.milvus.client.exception;
 
-public class MilvusException extends RuntimeException {
+public abstract class MilvusException extends RuntimeException {
+  private String target;
   private boolean fillInStackTrace;
 
-  MilvusException(boolean fillInStackTrace) {
-    this.fillInStackTrace = fillInStackTrace;
+  MilvusException(String target, boolean fillInStackTrace) {
+    this(target, fillInStackTrace, null, null);
   }
 
-  MilvusException(boolean fillInStackTrace, Throwable cause) {
-    super(cause);
+  MilvusException(String target, boolean fillInStackTrace, String message, Throwable cause) {
+    super(message, cause);
+    this.target = target;
     this.fillInStackTrace = fillInStackTrace;
   }
 
-  MilvusException(boolean fillInStackTrace, String message) {
-    super(message);
-    this.fillInStackTrace = fillInStackTrace;
+  @Override
+  public final String getMessage() {
+    return String.format("%s: %s", target, getErrorMessage());
+  }
+
+  protected String getErrorMessage() {
+    return super.getMessage();
   }
 
   @Override

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

@@ -0,0 +1,33 @@
+package io.milvus.client.exception;
+
+import io.milvus.grpc.ErrorCode;
+import io.milvus.grpc.Status;
+
+public class ServerSideMilvusException extends MilvusException {
+  private ErrorCode errorCode;
+  private String reason;
+
+  public ServerSideMilvusException(String target, Status status) {
+    super(target, false);
+    this.errorCode = status.getErrorCode();
+    this.reason = status.getReason();
+  }
+
+  public ErrorCode getErrorCode() {
+    return errorCode;
+  }
+
+  public String getReason() {
+    return reason;
+  }
+
+  @Override
+  public synchronized Throwable fillInStackTrace() {
+    return this;
+  }
+
+  @Override
+  public String getErrorMessage() {
+    return String.format("ServerSideMilvusException{errorCode=%s, reason=%s}", errorCode, reason);
+  }
+}

+ 7 - 0
src/main/java/io/milvus/client/exception/UnsupportedDataType.java

@@ -0,0 +1,7 @@
+package io.milvus.client.exception;
+
+public class UnsupportedDataType extends ClientSideMilvusException {
+  public UnsupportedDataType(String message) {
+    super(null, message);
+  }
+}

+ 7 - 9
src/main/java/io/milvus/client/exception/UnsupportedServerVersion.java

@@ -2,21 +2,19 @@ package io.milvus.client.exception;
 
 import io.milvus.client.MilvusClient;
 
-public class UnsupportedServerVersion extends MilvusException {
-  private String host;
+public class UnsupportedServerVersion extends ClientSideMilvusException {
   private String expect;
   private String actual;
 
-  public UnsupportedServerVersion(String host, String expect, String actual) {
-    super(false);
-    this.host = host;
+  public UnsupportedServerVersion(String target, String expect, String actual) {
+    super(target);
     this.expect = expect;
     this.actual = actual;
   }
 
   @Override
-  public String getMessage() {
-    return String.format("%s: Milvus client %s is expected to work with Milvus server %s, but the version of the connected server is %s",
-        host, MilvusClient.clientVersion, expect, actual);
+  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",
+        MilvusClient.clientVersion, expect, actual);
   }
-}
+}

+ 300 - 84
src/main/proto/milvus.proto

@@ -8,6 +8,26 @@ option java_outer_classname = "MilvusProto";
 
 package milvus.grpc;
 
+/**
+ * @brief Field data type
+ */
+enum DataType {
+    NONE = 0;
+    BOOL = 1;
+    INT8 = 2;
+    INT16 = 3;
+    INT32 = 4;
+    INT64 = 5;
+
+    FLOAT = 10;
+    DOUBLE = 11;
+
+    STRING = 20;
+
+    VECTOR_BINARY = 100;
+    VECTOR_FLOAT = 101;
+}
+
 /**
  * @brief general usage
  */
@@ -32,20 +52,39 @@ message CollectionNameList {
 }
 
 /**
- * @brief Collection schema
- * metric_type: 1-L2, 2-IP
+ * @brief Field name
  */
-message CollectionSchema {
+message FieldName {
+    string collection_name = 1;
+    string field_name = 2;
+}
+
+/**
+ * @brief Collection mapping
+ * @extra_params: key-value pair for extra parameters of the collection
+ *    typically usage:
+ *        extra_params["params"] = {segment_row_limit: 1000000, auto_id: true}
+ *    Note:
+ *        the segment_row_limit specify segment row count limit for merging
+ *        the auto_id = true means entity id is auto-generated by milvus
+ */
+message Mapping {
     Status status = 1;
     string collection_name = 2;
-    int64 dimension = 3;
-    int64 index_file_size = 4;
-    int32 metric_type = 5;
-    repeated KeyValuePair extra_params = 6;
+    repeated FieldParam fields = 3;
+    repeated KeyValuePair extra_params = 4;
 }
 
 /**
- * @brief Params of partition
+ * @brief Collection mapping list
+ */
+message MappingList {
+    Status status = 1;
+    repeated Mapping mapping_list = 2;
+}
+
+/**
+ * @brief Parameters of partition
  */
 message PartitionParam {
     string collection_name = 1;
@@ -61,70 +100,106 @@ message PartitionList {
 }
 
 /**
- * @brief Record inserted
+ * @brief Vector row record
  */
-message RowRecord {
+message VectorRowRecord {
     repeated float float_data = 1;             //float vector data
     bytes binary_data = 2;                      //binary vector data
 }
 
 /**
- * @brief Params to be inserted
+ * @brief Attribute record
+ */
+message AttrRecord {
+    repeated int32 int32_value = 1;
+    repeated int64 int64_value = 2;
+    repeated float float_value = 3;
+    repeated double double_value = 4;
+}
+
+/**
+ * @brief Vector records
+ */
+message VectorRecord {
+    repeated VectorRowRecord records = 1;
+}
+
+/**
+ * @brief Field values
+ */
+message FieldValue {
+    string field_name = 1;
+    DataType type = 2;
+    AttrRecord attr_record = 3;
+    VectorRecord vector_record = 4;
+}
+
+/**
+ * @brief Parameters for insert action
  */
 message InsertParam {
     string collection_name = 1;
-    repeated RowRecord row_record_array = 2;
-    repeated int64 row_id_array = 3;            //optional
+    repeated FieldValue fields = 2;
+    repeated int64 entity_id_array = 3;            //optional
     string partition_tag = 4;
     repeated KeyValuePair extra_params = 5;
 }
 
 /**
- * @brief Vector ids
+ * @brief Entity ids
  */
-message VectorIds {
+message EntityIds {
     Status status = 1;
-    repeated int64 vector_id_array = 2;
+    repeated int64 entity_id_array = 2;
 }
 
 /**
- * @brief Params for searching vector
+ * @brief Search vector parameters
+ */
+message VectorParam {
+    string json = 1;
+    VectorRecord row_record = 2;
+}
+
+/**
+ * @brief Parameters for search action
  */
 message SearchParam {
     string collection_name = 1;
     repeated string partition_tag_array = 2;
-    repeated RowRecord query_record_array = 3;
-    int64 topk = 4;
+    repeated VectorParam vector_param = 3;
+    string dsl = 4;
     repeated KeyValuePair extra_params = 5;
 }
 
 /**
- * @brief Params for searching vector in files
+ * @brief Parameters for searching in segments
  */
-message SearchInFilesParam {
+message SearchInSegmentParam {
     repeated string file_id_array = 1;
     SearchParam search_param = 2;
 }
 
 /**
- * @brief Params for searching vector by ID
+ * @brief Entities
  */
-message SearchByIDParam {
-    string collection_name = 1;
-    repeated string partition_tag_array = 2;
-    repeated int64 id_array = 3;
-    int64 topk = 4;
-    repeated KeyValuePair extra_params = 5;
+message Entities {
+    Status status = 1;
+    repeated int64 ids = 2;
+    repeated bool valid_row = 3;
+    repeated FieldValue fields = 4;
 }
 
 /**
- * @brief Query result params
+ * @brief Query result
  */
-message TopKQueryResult {
+message QueryResult {
     Status status = 1;
-    int64 row_num = 2;
-    repeated int64 ids = 3;
-    repeated float distances = 4;
+    Entities entities = 2;
+    int64 row_num = 3;
+    repeated float scores = 4;
+    repeated float distances = 5;
+    repeated KeyValuePair extra_params = 6;
 }
 
 /**
@@ -152,7 +227,7 @@ message CollectionRowCount {
 }
 
 /**
- * @brief Give server Command
+ * @brief Server command parameters
  */
 message Command {
     string cmd = 1;
@@ -160,24 +235,44 @@ message Command {
 
 /**
  * @brief Index params
- * @index_type: 0-invalid, 1-idmap, 2-ivflat, 3-ivfsq8, 4-nsgmix
+ * @collection_name: target collection
+ * @field_name: target field
+ * @index_name: a name for index provided by user, unique within this field
+ * @extra_params: index parameters in json format
+ *    for vector field:
+ *        extra_params["index_type"] = one of the values: IDMAP, IVFLAT, IVFSQ8, NSGMIX, IVFSQ8H,
+ *                                                        PQ, HNSW, HNSW_SQ8NM, ANNOY
+ *        extra_params["metric_type"] = one of the values: L2, IP, HAMMING, JACCARD, TANIMOTO
+ *                                                         SUBSTRUCTURE, SUPERSTRUCTURE
+ *        extra_params["params"] = extra parameters for index, for example ivflat: {nlist: 2048}
+ *    for structured field:
+ *        extra_params["index_type"] = one of the values: SORTED
  */
 message IndexParam {
     Status status = 1;
     string collection_name = 2;
-    int32 index_type = 3;
-    repeated KeyValuePair extra_params = 4;
+    string field_name = 3;
+    string index_name = 4;
+    repeated KeyValuePair extra_params = 5;
 }
 
 /**
- * @brief Flush params
+ * @brief Parameters for flush action
  */
 message FlushParam {
     repeated string collection_name_array = 1;
 }
 
 /**
- * @brief Flush params
+ * @brief Parameters for flush action
+ */
+message CompactParam {
+    string collection_name = 1;
+    double threshold = 2;
+}
+
+/**
+ * @brief Parameters for deleting entities by id
  */
 message DeleteByIDParam {
     string collection_name = 1;
@@ -185,7 +280,36 @@ message DeleteByIDParam {
 }
 
 /**
- * @brief collection information
+ * @brief Return collection stats
+ * @json_info: collection stats in json format, typically, the format is like:
+ *     {
+ *        row_count: xxx,
+ *        data_size: xxx,
+ *        partitions: [
+ *          {
+ *            tag: xxx,
+ *            id: xxx,
+ *            row_count: xxx,
+ *            data_size: xxx,
+ *            segments: [
+ *              {
+ *                 id: xxx,
+ *                 row_count: xxx,
+ *                 data_size: xxx,
+ *                 files: [
+ *                    {
+ *                       field: xxx,
+ *                       name: xxx,
+ *                       index_type: xxx,
+ *                       path: xxx,
+ *                       data_size: xxx,
+ *                    }
+ *                 ]
+ *              }
+ *            ]
+ *          }
+ *        ]
+ *     }
  */
 message CollectionInfo {
     Status status = 1;
@@ -193,38 +317,135 @@ message CollectionInfo {
 }
 
 /**
- * @brief vectors identity
+ * @brief Parameters for returning entities id of a segment
  */
-message VectorsIdentity {
+message GetEntityIDsParam {
+    string collection_name = 1;
+    int64 segment_id = 2;
+}
+
+/**
+ * @brief Entities identity
+ */
+message EntityIdentity {
     string collection_name = 1;
     repeated int64 id_array = 2;
+    repeated string field_names = 3;
 }
 
+/********************************************SearchPB interface***************************************************/
 /**
- * @brief vector data
+ * @brief Vector field parameters
  */
-message VectorsData {
-    Status status = 1;
-    repeated RowRecord vectors_data = 2;
+message VectorFieldParam {
+    int64 dimension = 1;
+}
+
+/**
+ * @brief Field type
+ */
+message FieldType {
+    oneof value {
+        DataType data_type = 1;
+        VectorFieldParam vector_param = 2;
+    }
+}
+
+/**
+ * @brief Field parameters
+ */
+message FieldParam {
+    uint64 id = 1;
+    string name = 2;
+    DataType type = 3;
+    repeated KeyValuePair index_params = 4;
+    repeated KeyValuePair extra_params = 5;
 }
 
 /**
- * @brief get vector ids from a segment parameters
+ * @brief Vector field record
  */
-message GetVectorIDsParam {
+message VectorFieldRecord {
+    repeated VectorRowRecord value = 1;
+}
+
+///////////////////////////////////////////////////////////////////
+
+message TermQuery {
+    string field_name = 1;
+    repeated int64 int_value = 2;
+    repeated double double_value = 3;
+    int64 value_num = 4;
+    float boost = 5;
+    repeated KeyValuePair extra_params = 6;
+}
+
+enum CompareOperator {
+    LT = 0;
+    LTE = 1;
+    EQ = 2;
+    GT = 3;
+    GTE = 4;
+    NE = 5;
+}
+
+message CompareExpr {
+    CompareOperator operator = 1;
+    string operand = 2;
+}
+
+message RangeQuery {
+    string field_name = 1;
+    repeated CompareExpr operand = 2;
+    float boost = 3;
+    repeated KeyValuePair extra_params = 4;
+}
+
+message VectorQuery {
+    string field_name = 1;
+    float query_boost = 2;
+    repeated VectorRowRecord records = 3;
+    int64 topk = 4;
+    repeated KeyValuePair extra_params = 5;
+}
+
+enum Occur {
+    INVALID = 0;
+    MUST = 1;
+    SHOULD = 2;
+    MUST_NOT = 3;
+}
+
+message BooleanQuery {
+    Occur occur = 1;
+    repeated GeneralQuery general_query = 2;
+}
+
+message GeneralQuery {
+    oneof query {
+        BooleanQuery boolean_query = 1;
+        TermQuery term_query = 2;
+        RangeQuery range_query = 3;
+        VectorQuery vector_query = 4;
+    }
+}
+
+message SearchParamPB {
     string collection_name = 1;
-    string segment_name = 2;
+    repeated string partition_tag_array = 2;
+    GeneralQuery general_query = 3;
+    repeated KeyValuePair extra_params = 4;
 }
 
 service MilvusService {
     /**
      * @brief This method is used to create collection
      *
-     * @param CollectionSchema, use to provide collection information to be created.
+     * @param Mapping, use to provide collection information to be created.
      *
      * @return Status
      */
-    rpc CreateCollection(CollectionSchema) returns (Status){}
+    rpc CreateCollection(Mapping) returns (Status){}
 
     /**
      * @brief This method is used to test collection existence.
@@ -242,7 +463,7 @@ service MilvusService {
      *
      * @return CollectionSchema
      */
-    rpc DescribeCollection(CollectionName) returns (CollectionSchema) {}
+    rpc DescribeCollection(CollectionName) returns (Mapping) {}
 
     /**
      * @brief This method is used to get collection schema.
@@ -276,7 +497,7 @@ service MilvusService {
      *
      * @param CollectionName, collection name is going to be deleted.
      *
-     * @return CollectionNameList
+     * @return Status
      */
     rpc DropCollection(CollectionName) returns (Status) {}
 
@@ -292,20 +513,20 @@ service MilvusService {
     /**
      * @brief This method is used to describe index
      *
-     * @param CollectionName, target collection name.
+     * @param IndexParam, target index.
      *
      * @return IndexParam
      */
-    rpc DescribeIndex(CollectionName) returns (IndexParam) {}
+    rpc DescribeIndex(IndexParam) returns (IndexParam) {}
 
     /**
      * @brief This method is used to drop index
      *
-     * @param CollectionName, target collection name.
+     * @param IndexParam, target field. if the IndexParam.field_name is empty, will drop all index of the collection
      *
      * @return Status
      */
-    rpc DropIndex(CollectionName) returns (Status) {}
+    rpc DropIndex(IndexParam) returns (Status) {}
 
     /**
      * @brief This method is used to create partition
@@ -348,54 +569,45 @@ service MilvusService {
      *
      * @param InsertParam, insert parameters.
      *
-     * @return VectorIds
+     * @return EntityIds
      */
-    rpc Insert(InsertParam) returns (VectorIds) {}
+    rpc Insert(InsertParam) returns (EntityIds) {}
 
     /**
-     * @brief This method is used to get vectors data by id array.
+     * @brief This method is used to get entities data by id array.
      *
-     * @param VectorsIdentity, target vector id array.
+     * @param EntitiesIdentity, target entity id array.
      *
-     * @return VectorsData
+     * @return Entities
      */
-    rpc GetVectorsByID(VectorsIdentity) returns (VectorsData) {}
+    rpc GetEntityByID(EntityIdentity) returns (Entities) {}
 
     /**
-     * @brief This method is used to get vector ids from a segment
+     * @brief This method is used to get entity ids from a segment
      *
-     * @param GetVectorIDsParam, target collection and segment
+     * @param GetEntityIDsParam, target collection and segment
      *
-     * @return VectorIds
+     * @return EntityIds
      */
-    rpc GetVectorIDs(GetVectorIDsParam) returns (VectorIds) {}
+    rpc GetEntityIDs(GetEntityIDsParam) returns (EntityIds) {}
 
     /**
      * @brief This method is used to query vector in collection.
      *
      * @param SearchParam, search parameters.
      *
-     * @return TopKQueryResult
-     */
-    rpc Search(SearchParam) returns (TopKQueryResult) {}
-
-    /**
-     * @brief This method is used to query vector by id.
-     *
-     * @param SearchByIDParam, search parameters.
-     *
-     * @return TopKQueryResult
+     * @return QueryResult
      */
-    rpc SearchByID(SearchByIDParam) returns (TopKQueryResult) {}
+    rpc Search(SearchParam) returns (QueryResult) {}
 
     /**
-     * @brief This method is used to query vector in specified files.
+     * @brief This method is used to query vector in specified segment.
      *
-     * @param SearchInFilesParam, search in files paremeters.
+     * @param SearchInSegmentParam, target segments to search.
      *
-     * @return TopKQueryResult
+     * @return QueryResult
      */
-    rpc SearchInFiles(SearchInFilesParam) returns (TopKQueryResult) {}
+    rpc SearchInSegment(SearchInSegmentParam) returns (QueryResult) {}
 
     /**
      * @brief This method is used to give the server status.
@@ -436,9 +648,13 @@ service MilvusService {
     /**
      * @brief This method is used to compact collection
      *
-     * @param CollectionName, target collection name.
+     * @param CompactParam, compact parameters
      *
      * @return Status
      */
-    rpc Compact(CollectionName) returns (Status) {}
+    rpc Compact(CompactParam) returns (Status) {}
+
+    /********************************New Interface********************************************/
+
+    rpc SearchPB(SearchParamPB) returns (QueryResult) {}
 }

File diff suppressed because it is too large
+ 490 - 423
src/test/java/io/milvus/client/MilvusGrpcClientTest.java


+ 1 - 3
src/test/java/io/milvus/client/StaticNameResolverProvider.java

@@ -13,11 +13,9 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 public class StaticNameResolverProvider extends NameResolverProvider {
-  private String name;
   private List<SocketAddress> addresses;
 
-  public StaticNameResolverProvider(String name, SocketAddress... addresses) {
-    this.name = name;
+  public StaticNameResolverProvider(SocketAddress... addresses) {
     this.addresses = Arrays.asList(addresses);
   }
 

+ 0 - 0
src/main/resources/log4j2.xml → src/test/resources/log4j2-test.xml


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