Browse Source

Merge pull request #66 from milvus-io/0.5.3

Merge from 0.5.3
Jin Hai 5 years ago
parent
commit
5643364a93

+ 20 - 1
CHANGELOG.md

@@ -1,6 +1,25 @@
 # Changelog     
 # Changelog     
 
 
-## milvus-sdk-java 0.2.2 (2018-11-4)
+## milvus-sdk-java 0.3.0 (2019-11-13)
+
+### Bug
+---
+- \#64 - Search failed with exception if search result is empty
+
+### Improvement
+---
+- \#56 - Add keepalive and idleTimeout settings
+- \#57 - add ok() in other types of Response
+- \#62 - Change GRPC proto (and related code) to increase search result's transmission speed
+- \#63 - Make some functions and constructors package-private if necessary
+
+### Feature
+---
+
+### Task
+---
+
+## milvus-sdk-java 0.2.2 (2019-11-4)
 
 
 ### Bug
 ### Bug
 ---
 ---

+ 4 - 2
README.md

@@ -15,6 +15,8 @@ Milvus version compatibility:
 |:-----:|:-----:|
 |:-----:|:-----:|
 | 0.5.0 | 0.2.2 | 
 | 0.5.0 | 0.2.2 | 
 | 0.5.1 | 0.2.2 | 
 | 0.5.1 | 0.2.2 | 
+| 0.5.2 | 0.2.2 | 
+| 0.5.3 | 0.3.0 | 
 
 
 ### Dependency 
 ### Dependency 
 
 
@@ -23,13 +25,13 @@ Apache Maven
 <dependency>
 <dependency>
     <groupId>io.milvus</groupId>
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java</artifactId>
     <artifactId>milvus-sdk-java</artifactId>
-    <version>0.2.2</version>
+    <version>0.3.0</version>
 </dependency>
 </dependency>
 ```
 ```
 
 
 Gradle/Grails 
 Gradle/Grails 
 
 
-`compile 'io.milvus:milvus-sdk-java:0.2.2'`
+`compile 'io.milvus:milvus-sdk-java:0.3.0'`
 
 
 ### Examples
 ### Examples
 
 

+ 17 - 2
examples/pom.xml

@@ -25,7 +25,7 @@
 
 
     <groupId>io.milvus</groupId>
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java-examples</artifactId>
     <artifactId>milvus-sdk-java-examples</artifactId>
-    <version>0.2.2</version>
+    <version>0.3.0</version>
     <build>
     <build>
         <plugins>
         <plugins>
             <plugin>
             <plugin>
@@ -44,11 +44,26 @@
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     </properties>
 
 
+    <!--  For development use  -->
+    <!--  Include this if you want to try the latest SNAPSHOT version  -->
+    <repositories>
+        <repository>
+            <id>oss.sonatype.org-snapshot</id>
+            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
+            <releases>
+                <enabled>false</enabled>
+            </releases>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+        </repository>
+    </repositories>
+
     <dependencies>
     <dependencies>
         <dependency>
         <dependency>
             <groupId>io.milvus</groupId>
             <groupId>io.milvus</groupId>
             <artifactId>milvus-sdk-java</artifactId>
             <artifactId>milvus-sdk-java</artifactId>
-            <version>0.2.2</version>
+            <version>0.3.0</version>
         </dependency>
         </dependency>
     </dependencies>
     </dependencies>
 
 

+ 1 - 1
examples/src/main/java/MilvusClientExample.java

@@ -137,7 +137,7 @@ public class MilvusClientExample {
         new SearchParam.Builder(tableName, vectorsToSearch).withTopK(topK).build();
         new SearchParam.Builder(tableName, vectorsToSearch).withTopK(topK).build();
     SearchResponse searchResponse = client.search(searchParam);
     SearchResponse searchResponse = client.search(searchParam);
     System.out.println(searchResponse);
     System.out.println(searchResponse);
-    if (searchResponse.getResponse().ok()) {
+    if (searchResponse.ok()) {
       List<List<SearchResponse.QueryResult>> queryResultsList =
       List<List<SearchResponse.QueryResult>> queryResultsList =
           searchResponse.getQueryResultsList();
           searchResponse.getQueryResultsList();
       final double epsilon = 0.001;
       final double epsilon = 0.001;

+ 6 - 1
pom.xml

@@ -25,7 +25,7 @@
 
 
     <groupId>io.milvus</groupId>
     <groupId>io.milvus</groupId>
     <artifactId>milvus-sdk-java</artifactId>
     <artifactId>milvus-sdk-java</artifactId>
-    <version>0.2.2</version>
+    <version>0.3.0</version>
     <packaging>jar</packaging>
     <packaging>jar</packaging>
 
 
     <name>io.milvus:milvus-sdk-java</name>
     <name>io.milvus:milvus-sdk-java</name>
@@ -134,6 +134,11 @@
             <artifactId>commons-text</artifactId>
             <artifactId>commons-text</artifactId>
             <version>1.6</version>
             <version>1.6</version>
         </dependency>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
+            <version>4.4</version>
+        </dependency>
     </dependencies>
     </dependencies>
 
 
     <profiles>
     <profiles>

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

@@ -20,17 +20,26 @@
 package io.milvus.client;
 package io.milvus.client;
 
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nonnull;
+import java.util.concurrent.TimeUnit;
 
 
 /** Contains parameters for connecting to Milvus server */
 /** Contains parameters for connecting to Milvus server */
 public class ConnectParam {
 public class ConnectParam {
   private final String host;
   private final String host;
   private final String port;
   private final String port;
-  private final long timeout;
+  private final long connectTimeoutNanos;
+  private final long keepAliveTimeNanos;
+  private final long keepAliveTimeoutNanos;
+  private final boolean keepAliveWithoutCalls;
+  private final long idleTimeoutNanos;
 
 
   private ConnectParam(@Nonnull Builder builder) {
   private ConnectParam(@Nonnull Builder builder) {
     this.host = builder.host;
     this.host = builder.host;
     this.port = builder.port;
     this.port = builder.port;
-    this.timeout = builder.timeout;
+    this.connectTimeoutNanos = builder.connectTimeoutNanos;
+    this.keepAliveTimeNanos = builder.keepAliveTimeNanos;
+    this.keepAliveTimeoutNanos = builder.keepAliveTimeoutNanos;
+    this.keepAliveWithoutCalls = builder.keepAliveWithoutCalls;
+    this.idleTimeoutNanos = builder.idleTimeoutNanos;
   }
   }
 
 
   public String getHost() {
   public String getHost() {
@@ -41,24 +50,61 @@ public class ConnectParam {
     return port;
     return port;
   }
   }
 
 
-  public long getTimeout() {
-    return timeout;
+  public long getConnectTimeout(@Nonnull TimeUnit timeUnit) {
+    return timeUnit.convert(connectTimeoutNanos, TimeUnit.NANOSECONDS);
+  }
+
+  public long getKeepAliveTime(@Nonnull TimeUnit timeUnit) {
+    return timeUnit.convert(keepAliveTimeNanos, TimeUnit.NANOSECONDS);
+  }
+
+  public long getKeepAliveTimeout(@Nonnull TimeUnit timeUnit) {
+    return timeUnit.convert(keepAliveTimeoutNanos, TimeUnit.NANOSECONDS);
+  }
+
+  public boolean isKeepAliveWithoutCalls() {
+    return keepAliveWithoutCalls;
+  }
+
+  public long getIdleTimeout(@Nonnull TimeUnit timeUnit) {
+    return timeUnit.convert(idleTimeoutNanos, TimeUnit.NANOSECONDS);
   }
   }
 
 
   @Override
   @Override
   public String toString() {
   public String toString() {
-    return "ConnectParam{" + "host='" + host + '\'' + ", port='" + port + '\'' + '}';
+    return "ConnectParam {"
+        + "host='"
+        + host
+        + '\''
+        + ", port='"
+        + port
+        + '\''
+        + ", connectTimeoutNanos="
+        + connectTimeoutNanos
+        + ", keepAliveTimeNanos="
+        + keepAliveTimeNanos
+        + ", keepAliveTimeoutNanos="
+        + keepAliveTimeoutNanos
+        + ", keepAliveWithoutCalls="
+        + keepAliveWithoutCalls
+        + ", idleTimeoutNanos="
+        + idleTimeoutNanos
+        + '}';
   }
   }
 
 
   /** Builder for <code>ConnectParam</code> */
   /** Builder for <code>ConnectParam</code> */
   public static class Builder {
   public static class Builder {
     // Optional parameters - initialized to default values
     // Optional parameters - initialized to default values
-    private String host = "127.0.0.1";
+    private String host = "localhost";
     private String port = "19530";
     private String port = "19530";
-    private long timeout = 10000; // ms
+    private long connectTimeoutNanos = TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS);
+    private long keepAliveTimeNanos = Long.MAX_VALUE; // Disabling keepalive
+    private long keepAliveTimeoutNanos = TimeUnit.NANOSECONDS.convert(20, TimeUnit.SECONDS);
+    private boolean keepAliveWithoutCalls = false;
+    private long idleTimeoutNanos = TimeUnit.NANOSECONDS.convert(24, TimeUnit.HOURS);
 
 
     /**
     /**
-     * Optional. Default to "127.0.0.1".
+     * Optional. Defaults to "localhost".
      *
      *
      * @param host server host
      * @param host server host
      * @return <code>Builder</code>
      * @return <code>Builder</code>
@@ -69,24 +115,108 @@ public class ConnectParam {
     }
     }
 
 
     /**
     /**
-     * Optional. Default to "19530".
+     * Optional. Defaults to "19530".
      *
      *
      * @param port server port
      * @param port server port
      * @return <code>Builder</code>
      * @return <code>Builder</code>
      */
      */
-    public Builder withPort(@Nonnull String port) {
+    public Builder withPort(@Nonnull String port) throws IllegalArgumentException {
+      int portInt = Integer.parseInt(port);
+      if (portInt < 0 || portInt > 0xFFFF) {
+        throw new IllegalArgumentException("Port is out of range!");
+      }
       this.port = port;
       this.port = port;
       return this;
       return this;
     }
     }
 
 
     /**
     /**
-     * Optional. Default to 10000 ms
+     * Optional. Defaults to 10 seconds.
+     *
+     * @param connectTimeout Timeout for client to establish a connection to server
+     * @return <code>Builder</code>
+     * @throws IllegalArgumentException
+     */
+    public Builder withConnectTimeout(long connectTimeout, @Nonnull TimeUnit timeUnit)
+        throws IllegalArgumentException {
+      if (connectTimeout <= 0L) {
+        throw new IllegalArgumentException("Connect timeout must be positive!");
+      }
+      connectTimeoutNanos = timeUnit.toNanos(connectTimeout);
+      return this;
+    }
+
+    /**
+     * Optional. Sets the time without read activity before sending a keepalive ping. An
+     * unreasonably small value might be increased, and Long.MAX_VALUE nano seconds or an
+     * unreasonably large value will disable keepalive. Defaults to infinite.
+     *
+     * @see <a
+     *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveTime-long-java.util.concurrent.TimeUnit-">
+     *     GRPC keepAliveTime Javadoc</a>
+     * @return <code>Builder</code>
+     * @throws IllegalArgumentException
+     */
+    public Builder withKeepAliveTime(long keepAliveTime, @Nonnull TimeUnit timeUnit)
+        throws IllegalArgumentException {
+      if (keepAliveTime <= 0L) {
+        throw new IllegalArgumentException("Keepalive time must be positive!");
+      }
+      keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
+      return this;
+    }
+
+    /**
+     * Optional. Sets the time waiting for read activity after sending a keepalive ping. If the time
+     * 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.
+     *
+     * @see <a
+     *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveTimeout-long-java.util.concurrent.TimeUnit-">
+     *     GRPC keepAliveTimeout Javadoc</a>
+     * @return <code>Builder</code>
+     * @throws IllegalArgumentException
+     */
+    public Builder withKeepAliveTimeout(long keepAliveTimeout, @Nonnull TimeUnit timeUnit)
+        throws IllegalArgumentException {
+      if (keepAliveTimeout <= 0L) {
+        throw new IllegalArgumentException("Keepalive timeout must be positive!");
+      }
+      keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
+      return this;
+    }
+
+    /**
+     * Optional. Sets whether keepalive will be performed when there are no outstanding RPC on a
+     * connection. Defaults to false.
+     *
+     * @see <a
+     *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#keepAliveWithoutCalls-boolean-">
+     *     GRPC keepAliveWithoutCalls Javadoc</a>
+     * @return <code>Builder</code>
+     */
+    public Builder keepAliveWithoutCalls(boolean enable) {
+      keepAliveWithoutCalls = enable;
+      return this;
+    }
+
+    /**
+     * Optional. Set the duration without ongoing RPCs before going to idle mode. A new RPC would
+     * take the channel out of idle mode. Defaults to 24 hour.
      *
      *
-     * @param timeout Timeout in ms for client to establish a connection to server
+     * @see <a
+     *     href="https://grpc.github.io/grpc-java/javadoc/io/grpc/ManagedChannelBuilder.html#idleTimeout-long-java.util.concurrent.TimeUnit-">
+     *     GRPC idleTimeout Javadoc</a>
      * @return <code>Builder</code>
      * @return <code>Builder</code>
+     * @throws IllegalArgumentException
      */
      */
-    public Builder withTimeout(long timeout) {
-      this.timeout = timeout;
+    public Builder withIdleTimeout(long idleTimeout, TimeUnit timeUnit)
+        throws IllegalArgumentException {
+      if (idleTimeout <= 0L) {
+        throw new IllegalArgumentException("Idle timeout must be positive!");
+      }
+      idleTimeoutNanos = timeUnit.toNanos(idleTimeout);
       return this;
       return this;
     }
     }
 
 

+ 5 - 1
src/main/java/io/milvus/client/DescribeIndexResponse.java

@@ -29,7 +29,7 @@ public class DescribeIndexResponse {
   private final Response response;
   private final Response response;
   private final Index index;
   private final Index index;
 
 
-  public DescribeIndexResponse(Response response, @Nullable Index index) {
+  DescribeIndexResponse(Response response, @Nullable Index index) {
     this.response = response;
     this.response = response;
     this.index = index;
     this.index = index;
   }
   }
@@ -47,6 +47,10 @@ public class DescribeIndexResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(

+ 5 - 1
src/main/java/io/milvus/client/DescribeTableResponse.java

@@ -30,7 +30,7 @@ public class DescribeTableResponse {
   private final Response response;
   private final Response response;
   private final TableSchema tableSchema;
   private final TableSchema tableSchema;
 
 
-  public DescribeTableResponse(Response response, @Nullable TableSchema tableSchema) {
+  DescribeTableResponse(Response response, @Nullable TableSchema tableSchema) {
     this.response = response;
     this.response = response;
     this.tableSchema = tableSchema;
     this.tableSchema = tableSchema;
   }
   }
@@ -48,6 +48,10 @@ public class DescribeTableResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(

+ 5 - 1
src/main/java/io/milvus/client/GetTableRowCountResponse.java

@@ -27,7 +27,7 @@ public class GetTableRowCountResponse {
   private final Response response;
   private final Response response;
   private final long tableRowCount;
   private final long tableRowCount;
 
 
-  public GetTableRowCountResponse(Response response, long tableRowCount) {
+  GetTableRowCountResponse(Response response, long tableRowCount) {
     this.response = response;
     this.response = response;
     this.tableRowCount = tableRowCount;
     this.tableRowCount = tableRowCount;
   }
   }
@@ -40,6 +40,10 @@ public class GetTableRowCountResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(

+ 5 - 1
src/main/java/io/milvus/client/HasTableResponse.java

@@ -26,7 +26,7 @@ public class HasTableResponse {
   private final Response response;
   private final Response response;
   private final boolean hasTable;
   private final boolean hasTable;
 
 
-  public HasTableResponse(Response response, boolean hasTable) {
+  HasTableResponse(Response response, boolean hasTable) {
     this.response = response;
     this.response = response;
     this.hasTable = hasTable;
     this.hasTable = hasTable;
   }
   }
@@ -39,6 +39,10 @@ public class HasTableResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format("HasTableResponse {%s, has table = %s}", response.toString(), hasTable);
     return String.format("HasTableResponse {%s, has table = %s}", response.toString(), hasTable);

+ 5 - 1
src/main/java/io/milvus/client/InsertResponse.java

@@ -28,7 +28,7 @@ public class InsertResponse {
   private final Response response;
   private final Response response;
   private final List<Long> vectorIds;
   private final List<Long> vectorIds;
 
 
-  public InsertResponse(Response response, List<Long> vectorIds) {
+  InsertResponse(Response response, List<Long> vectorIds) {
     this.response = response;
     this.response = response;
     this.vectorIds = vectorIds;
     this.vectorIds = vectorIds;
   }
   }
@@ -41,6 +41,10 @@ public class InsertResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(

+ 8 - 3
src/main/java/io/milvus/client/MilvusClient.java

@@ -22,7 +22,7 @@ package io.milvus.client;
 /** The Milvus Client Interface */
 /** The Milvus Client Interface */
 public interface MilvusClient {
 public interface MilvusClient {
 
 
-  String clientVersion = "0.2.2";
+  String clientVersion = "0.3.0";
 
 
   /** @return the current Milvus client version */
   /** @return the current Milvus client version */
   default String getClientVersion() {
   default String getClientVersion() {
@@ -39,7 +39,11 @@ public interface MilvusClient {
    * ConnectParam connectParam = new ConnectParam.Builder()
    * ConnectParam connectParam = new ConnectParam.Builder()
    *                                             .withHost("localhost")
    *                                             .withHost("localhost")
    *                                             .withPort("19530")
    *                                             .withPort("19530")
-   *                                             .withTimeout(10000)
+   *                                             .withConnectTimeout(10, TimeUnit.SECONDS)
+   *                                             .withKeepAliveTime(Long.MAX_VALUE, TimeUnit.NANOSECONDS)
+   *                                             .withKeepAliveTimeout(20, TimeUnit.SECONDS)
+   *                                             .keepAliveWithoutCalls(false)
+   *                                             .withIdleTimeout(24, TimeUnit.HOURS)
    *                                             .build();
    *                                             .build();
    * </code>
    * </code>
    * </pre>
    * </pre>
@@ -52,7 +56,8 @@ public interface MilvusClient {
    */
    */
   Response connect(ConnectParam connectParam) throws ConnectFailedException;
   Response connect(ConnectParam connectParam) throws ConnectFailedException;
 
 
-  /** @return <code>true</code> if the client is connected to Milvus server */
+  /** @return <code>true</code> if the client is connected to Milvus server.
+   * The channel's connectivity state is READY.*/
   boolean isConnected();
   boolean isConnected();
 
 
   /**
   /**

+ 92 - 66
src/main/java/io/milvus/client/MilvusGrpcClient.java

@@ -23,6 +23,7 @@ import io.grpc.ConnectivityState;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannel;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.ManagedChannelBuilder;
 import io.grpc.StatusRuntimeException;
 import io.grpc.StatusRuntimeException;
+import org.apache.commons.collections4.ListUtils;
 
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nonnull;
 import java.text.SimpleDateFormat;
 import java.text.SimpleDateFormat;
@@ -55,22 +56,23 @@ public class MilvusGrpcClient implements MilvusClient {
     }
     }
 
 
     try {
     try {
-      int port = Integer.parseInt(connectParam.getPort());
-      if (port < 0 || port > 0xFFFF) {
-        logSevere("Connect failed! Port {0} out of range", connectParam.getPort());
-        throw new ConnectFailedException("Port " + port + " out of range");
-      }
 
 
       channel =
       channel =
-          ManagedChannelBuilder.forAddress(connectParam.getHost(), port)
+          ManagedChannelBuilder.forAddress(
+                  connectParam.getHost(), Integer.parseInt(connectParam.getPort()))
               .usePlaintext()
               .usePlaintext()
               .maxInboundMessageSize(Integer.MAX_VALUE)
               .maxInboundMessageSize(Integer.MAX_VALUE)
+              .keepAliveTime(
+                  connectParam.getKeepAliveTime(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
+              .keepAliveTimeout(
+                  connectParam.getKeepAliveTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
+              .keepAliveWithoutCalls(connectParam.isKeepAliveWithoutCalls())
+              .idleTimeout(connectParam.getIdleTimeout(TimeUnit.NANOSECONDS), TimeUnit.NANOSECONDS)
               .build();
               .build();
 
 
-      ConnectivityState connectivityState;
-      connectivityState = channel.getState(true);
+      channel.getState(true);
 
 
-      long timeout = connectParam.getTimeout();
+      long timeout = connectParam.getConnectTimeout(TimeUnit.MILLISECONDS);
       logInfo("Trying to connect...Timeout in {0} ms", timeout);
       logInfo("Trying to connect...Timeout in {0} ms", timeout);
 
 
       final long checkFrequency = 100; // ms
       final long checkFrequency = 100; // ms
@@ -92,7 +94,9 @@ public class MilvusGrpcClient implements MilvusClient {
       throw new ConnectFailedException("Exception occurred: " + e.toString());
       throw new ConnectFailedException("Exception occurred: " + e.toString());
     }
     }
 
 
-    logInfo("Connected successfully!\n{0}", connectParam.toString());
+    logInfo(
+        "Connection established successfully to host={0}, port={1}",
+        connectParam.getHost(), connectParam.getPort());
     return new Response(Response.Status.SUCCESS);
     return new Response(Response.Status.SUCCESS);
   }
   }
 
 
@@ -107,7 +111,7 @@ public class MilvusGrpcClient implements MilvusClient {
 
 
   @Override
   @Override
   public Response disconnect() throws InterruptedException {
   public Response disconnect() throws InterruptedException {
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     } else {
     } else {
@@ -129,7 +133,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public Response createTable(@Nonnull TableSchema tableSchema) {
   public Response createTable(@Nonnull TableSchema tableSchema) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -168,7 +172,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public HasTableResponse hasTable(@Nonnull String tableName) {
   public HasTableResponse hasTable(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new HasTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), false);
       return new HasTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), false);
     }
     }
@@ -200,7 +204,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public Response dropTable(@Nonnull String tableName) {
   public Response dropTable(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -229,7 +233,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public Response createIndex(@Nonnull CreateIndexParam createIndexParam) {
   public Response createIndex(@Nonnull CreateIndexParam createIndexParam) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -268,7 +272,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public InsertResponse insert(@Nonnull InsertParam insertParam) {
   public InsertResponse insert(@Nonnull InsertParam insertParam) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new InsertResponse(
       return new InsertResponse(
           new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
           new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
@@ -317,10 +321,11 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public SearchResponse search(@Nonnull SearchParam searchParam) {
   public SearchResponse search(@Nonnull SearchParam searchParam) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
-      return new SearchResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+      SearchResponse searchResponse = new SearchResponse();
+      searchResponse.setResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED));
+      return searchResponse;
     }
     }
 
 
     List<io.milvus.grpc.RowRecord> queryRowRecordList = getQueryRowRecordList(searchParam);
     List<io.milvus.grpc.RowRecord> queryRowRecordList = getQueryRowRecordList(searchParam);
@@ -336,39 +341,43 @@ public class MilvusGrpcClient implements MilvusClient {
             .setNprobe(searchParam.getNProbe())
             .setNprobe(searchParam.getNProbe())
             .build();
             .build();
 
 
-    io.milvus.grpc.TopKQueryResultList response;
+    io.milvus.grpc.TopKQueryResult response;
 
 
     try {
     try {
       response = blockingStub.search(request);
       response = blockingStub.search(request);
 
 
       if (response.getStatus().getErrorCode() == io.milvus.grpc.ErrorCode.SUCCESS) {
       if (response.getStatus().getErrorCode() == io.milvus.grpc.ErrorCode.SUCCESS) {
-        List<List<SearchResponse.QueryResult>> queryResultsList = getQueryResultsList(response);
+        SearchResponse searchResponse = buildSearchResponse(response);
+        searchResponse.setResponse(new Response(Response.Status.SUCCESS));
         logInfo(
         logInfo(
             "Search completed successfully! Returned results for {0} queries",
             "Search completed successfully! Returned results for {0} queries",
-            queryResultsList.size());
-        return new SearchResponse(new Response(Response.Status.SUCCESS), queryResultsList);
+            searchResponse.getNumQueries());
+        return searchResponse;
       } else {
       } else {
         logSevere("Search failed:\n{0}", response.toString());
         logSevere("Search failed:\n{0}", response.toString());
-        return new SearchResponse(
+        SearchResponse searchResponse = new SearchResponse();
+        searchResponse.setResponse(
             new Response(
             new Response(
                 Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
                 Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            new ArrayList<>());
+                response.getStatus().getReason()));
+        return searchResponse;
       }
       }
     } catch (StatusRuntimeException e) {
     } catch (StatusRuntimeException e) {
       logSevere("search RPC failed:\n{0}", e.getStatus().toString());
       logSevere("search RPC failed:\n{0}", e.getStatus().toString());
-      return new SearchResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+      SearchResponse searchResponse = new SearchResponse();
+      searchResponse.setResponse(new Response(Response.Status.RPC_ERROR, e.toString()));
+      return searchResponse;
     }
     }
   }
   }
 
 
   @Override
   @Override
   public SearchResponse searchInFiles(@Nonnull SearchInFilesParam searchInFilesParam) {
   public SearchResponse searchInFiles(@Nonnull SearchInFilesParam searchInFilesParam) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
-      return new SearchResponse(
-          new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+      SearchResponse searchResponse = new SearchResponse();
+      searchResponse.setResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED));
+      return searchResponse;
     }
     }
 
 
     SearchParam searchParam = searchInFilesParam.getSearchParam();
     SearchParam searchParam = searchInFilesParam.getSearchParam();
@@ -392,37 +401,40 @@ public class MilvusGrpcClient implements MilvusClient {
             .setSearchParam(searchParamToSet)
             .setSearchParam(searchParamToSet)
             .build();
             .build();
 
 
-    io.milvus.grpc.TopKQueryResultList response;
+    io.milvus.grpc.TopKQueryResult response;
 
 
     try {
     try {
       response = blockingStub.searchInFiles(request);
       response = blockingStub.searchInFiles(request);
 
 
       if (response.getStatus().getErrorCode() == io.milvus.grpc.ErrorCode.SUCCESS) {
       if (response.getStatus().getErrorCode() == io.milvus.grpc.ErrorCode.SUCCESS) {
+        SearchResponse searchResponse = buildSearchResponse(response);
+        searchResponse.setResponse(new Response(Response.Status.SUCCESS));
         logInfo("Search in files {0} completed successfully!", searchInFilesParam.getFileIds());
         logInfo("Search in files {0} completed successfully!", searchInFilesParam.getFileIds());
-
-        List<List<SearchResponse.QueryResult>> queryResultsList = getQueryResultsList(response);
-        return new SearchResponse(new Response(Response.Status.SUCCESS), queryResultsList);
+        return searchResponse;
       } else {
       } else {
         logSevere(
         logSevere(
             "Search in files {0} failed:\n{1}",
             "Search in files {0} failed:\n{1}",
             searchInFilesParam.getFileIds(), response.toString());
             searchInFilesParam.getFileIds(), response.toString());
-        return new SearchResponse(
+
+        SearchResponse searchResponse = new SearchResponse();
+        searchResponse.setResponse(
             new Response(
             new Response(
                 Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
                 Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
-                response.getStatus().getReason()),
-            new ArrayList<>());
+                response.getStatus().getReason()));
+        return searchResponse;
       }
       }
     } catch (StatusRuntimeException e) {
     } catch (StatusRuntimeException e) {
       logSevere("searchInFiles RPC failed:\n{0}", e.getStatus().toString());
       logSevere("searchInFiles RPC failed:\n{0}", e.getStatus().toString());
-      return new SearchResponse(
-          new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+      SearchResponse searchResponse = new SearchResponse();
+      searchResponse.setResponse(new Response(Response.Status.RPC_ERROR, e.toString()));
+      return searchResponse;
     }
     }
   }
   }
 
 
   @Override
   @Override
   public DescribeTableResponse describeTable(@Nonnull String tableName) {
   public DescribeTableResponse describeTable(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new DescribeTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
       return new DescribeTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
     }
     }
@@ -459,7 +471,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public ShowTablesResponse showTables() {
   public ShowTablesResponse showTables() {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new ShowTablesResponse(
       return new ShowTablesResponse(
           new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
           new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
@@ -493,7 +505,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public GetTableRowCountResponse getTableRowCount(@Nonnull String tableName) {
   public GetTableRowCountResponse getTableRowCount(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new GetTableRowCountResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), 0);
       return new GetTableRowCountResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), 0);
     }
     }
@@ -535,7 +547,7 @@ public class MilvusGrpcClient implements MilvusClient {
 
 
   private Response command(@Nonnull String command) {
   private Response command(@Nonnull String command) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -564,7 +576,7 @@ public class MilvusGrpcClient implements MilvusClient {
   // TODO: make deleteByRange private for now
   // TODO: make deleteByRange private for now
   private Response deleteByRange(@Nonnull String tableName, @Nonnull DateRange dateRange) {
   private Response deleteByRange(@Nonnull String tableName, @Nonnull DateRange dateRange) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -599,7 +611,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public Response preloadTable(@Nonnull String tableName) {
   public Response preloadTable(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -628,7 +640,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public DescribeIndexResponse describeIndex(@Nonnull String tableName) {
   public DescribeIndexResponse describeIndex(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new DescribeIndexResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
       return new DescribeIndexResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
     }
     }
@@ -665,7 +677,7 @@ public class MilvusGrpcClient implements MilvusClient {
   @Override
   @Override
   public Response dropIndex(@Nonnull String tableName) {
   public Response dropIndex(@Nonnull String tableName) {
 
 
-    if (!isConnected()) {
+    if (!channelIsReadyOrIdle()) {
       logWarning("You are not connected to Milvus server");
       logWarning("You are not connected to Milvus server");
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
       return new Response(Response.Status.CLIENT_NOT_CONNECTED);
     }
     }
@@ -726,25 +738,39 @@ public class MilvusGrpcClient implements MilvusClient {
         .build();
         .build();
   }
   }
 
 
-  private List<List<SearchResponse.QueryResult>> getQueryResultsList(
-      io.milvus.grpc.TopKQueryResultList searchResponse) {
-    // TODO: refactor
-    List<List<SearchResponse.QueryResult>> queryResultsList = new ArrayList<>();
-    Optional<List<io.milvus.grpc.TopKQueryResult>> topKQueryResultList =
-        Optional.ofNullable(searchResponse.getTopkQueryResultList());
-    if (topKQueryResultList.isPresent()) {
-      for (io.milvus.grpc.TopKQueryResult topKQueryResult : topKQueryResultList.get()) {
-        List<SearchResponse.QueryResult> responseQueryResults = new ArrayList<>();
-        List<io.milvus.grpc.QueryResult> queryResults = topKQueryResult.getQueryResultArraysList();
-        for (io.milvus.grpc.QueryResult queryResult : queryResults) {
-          SearchResponse.QueryResult responseQueryResult =
-              new SearchResponse.QueryResult(queryResult.getId(), queryResult.getDistance());
-          responseQueryResults.add(responseQueryResult);
-        }
-        queryResultsList.add(responseQueryResults);
-      }
+  private SearchResponse buildSearchResponse(io.milvus.grpc.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<>();
+    List<List<Float>> resultDistancesList = new ArrayList<>();
+    if (topK > 0) {
+      resultIdsList = ListUtils.partition(topKQueryResult.getIdsList(), topK);
+      resultDistancesList = ListUtils.partition(topKQueryResult.getDistancesList(), topK);
     }
     }
-    return queryResultsList;
+
+    SearchResponse searchResponse = new SearchResponse();
+    searchResponse.setNumQueries(numQueries);
+    searchResponse.setTopK(topK);
+    searchResponse.setResultIdsList(resultIdsList);
+    searchResponse.setResultDistancesList(resultDistancesList);
+
+    return searchResponse;
+  }
+
+  private boolean channelIsReadyOrIdle() {
+    if (channel == null) {
+      return false;
+    }
+    ConnectivityState connectivityState = channel.getState(false);
+    return connectivityState == ConnectivityState.READY
+        || connectivityState
+            == ConnectivityState.IDLE; // Since a new RPC would take the channel out of idle mode
   }
   }
 
 
   ///////////////////// Log Functions//////////////////////
   ///////////////////// Log Functions//////////////////////

+ 54 - 29
src/main/java/io/milvus/client/SearchResponse.java

@@ -19,8 +19,10 @@
 
 
 package io.milvus.client;
 package io.milvus.client;
 
 
-import java.util.ArrayList;
 import java.util.List;
 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
  * Contains the returned <code>response</code> and <code>queryResultsList</code> for <code>search
@@ -28,12 +30,26 @@ import java.util.List;
  */
  */
 public class SearchResponse {
 public class SearchResponse {
 
 
-  private final Response response;
-  private final List<List<QueryResult>> queryResultsList;
+  private Response response;
+  private int numQueries;
+  private long topK;
+  private List<List<Long>> resultIdsList;
+  private List<List<Float>> resultDistancesList;
 
 
-  public SearchResponse(Response response, List<List<QueryResult>> queryResultsList) {
-    this.response = response;
-    this.queryResultsList = queryResultsList;
+  public int getNumQueries() {
+    return numQueries;
+  }
+
+  void setNumQueries(int numQueries) {
+    this.numQueries = numQueries;
+  }
+
+  public long getTopK() {
+    return topK;
+  }
+
+  void setTopK(long topK) {
+    this.topK = topK;
   }
   }
 
 
   /**
   /**
@@ -41,7 +57,17 @@ public class SearchResponse {
    *     the query result of a vector.
    *     the query result of a vector.
    */
    */
   public List<List<QueryResult>> getQueryResultsList() {
   public List<List<QueryResult>> getQueryResultsList() {
-    return queryResultsList;
+    return IntStream.range(0, numQueries)
+        .mapToObj(
+            i ->
+                LongStream.range(0, topK)
+                    .mapToObj(
+                        j ->
+                            new QueryResult(
+                                resultIdsList.get(i).get((int) j),
+                                resultDistancesList.get(i).get((int) j)))
+                    .collect(Collectors.toList()))
+        .collect(Collectors.toList());
   }
   }
 
 
   /**
   /**
@@ -49,42 +75,41 @@ public class SearchResponse {
    *     of a vector.
    *     of a vector.
    */
    */
   public List<List<Long>> getResultIdsList() {
   public List<List<Long>> getResultIdsList() {
-    List<List<Long>> resultIdsList = new ArrayList<>();
-    for (List<QueryResult> queryResults : queryResultsList) {
-      List<Long> resultIds = new ArrayList<>();
-      for (QueryResult queryResult : queryResults) {
-        resultIds.add(queryResult.vectorId);
-      }
-      resultIdsList.add(resultIds);
-    }
     return resultIdsList;
     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
    * @return @return a <code>List</code> of result distances. Each inner <code>List</code> contains
    *     the result distances of a vector.
    *     the result distances of a vector.
    */
    */
-  public List<List<Double>> getResultDistancesList() {
-    List<List<Double>> resultDistancesList = new ArrayList<>();
-    for (List<QueryResult> queryResults : queryResultsList) {
-      List<Double> resultDistances = new ArrayList<>();
-      for (QueryResult queryResult : queryResults) {
-        resultDistances.add(queryResult.distance);
-      }
-      resultDistancesList.add(resultDistances);
-    }
+  public List<List<Float>> getResultDistancesList() {
     return resultDistancesList;
     return resultDistancesList;
   }
   }
 
 
+  void setResultDistancesList(List<List<Float>> resultDistancesList) {
+    this.resultDistancesList = resultDistancesList;
+  }
+
   public Response getResponse() {
   public Response getResponse() {
     return response;
     return response;
   }
   }
 
 
+  void setResponse(Response response) {
+    this.response = response;
+  }
+
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(
-        "SearchResponse {%s, returned results for %d queries}",
-        response.toString(), this.queryResultsList.size());
+        "SearchResponse {%s, returned results for %d queries}", response.toString(), numQueries);
   }
   }
 
 
   /**
   /**
@@ -93,9 +118,9 @@ public class SearchResponse {
    */
    */
   public static class QueryResult {
   public static class QueryResult {
     private final long vectorId;
     private final long vectorId;
-    private final double distance;
+    private final float distance;
 
 
-    public QueryResult(long vectorId, double distance) {
+    QueryResult(long vectorId, float distance) {
       this.vectorId = vectorId;
       this.vectorId = vectorId;
       this.distance = distance;
       this.distance = distance;
     }
     }
@@ -104,7 +129,7 @@ public class SearchResponse {
       return vectorId;
       return vectorId;
     }
     }
 
 
-    public double getDistance() {
+    public float getDistance() {
       return distance;
       return distance;
     }
     }
   }
   }

+ 5 - 1
src/main/java/io/milvus/client/ShowTablesResponse.java

@@ -29,7 +29,7 @@ public class ShowTablesResponse {
   private final Response response;
   private final Response response;
   private final List<String> tableNames;
   private final List<String> tableNames;
 
 
-  public ShowTablesResponse(Response response, List<String> tableNames) {
+  ShowTablesResponse(Response response, List<String> tableNames) {
     this.response = response;
     this.response = response;
     this.tableNames = tableNames;
     this.tableNames = tableNames;
   }
   }
@@ -42,6 +42,10 @@ public class ShowTablesResponse {
     return response;
     return response;
   }
   }
 
 
+  public boolean ok() {
+    return response.ok();
+  }
+
   @Override
   @Override
   public String toString() {
   public String toString() {
     return String.format(
     return String.format(

+ 272 - 31
src/main/proto/milvus.proto

@@ -9,319 +9,560 @@ option java_outer_classname = "MilvusProto";
 package milvus.grpc;
 package milvus.grpc;
 
 
 /**
 /**
+
  * @brief Table Name
  * @brief Table Name
+
  */
  */
+
 message TableName {
 message TableName {
+
     string table_name = 1;
     string table_name = 1;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Table Name List
  * @brief Table Name List
+
  */
  */
+
 message TableNameList {
 message TableNameList {
+
     Status status = 1;
     Status status = 1;
+
     repeated string table_names = 2;
     repeated string table_names = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Table Schema
  * @brief Table Schema
+
  */
  */
+
 message TableSchema {
 message TableSchema {
+
     Status status = 1;
     Status status = 1;
+
     string table_name = 2;
     string table_name = 2;
+
     int64 dimension = 3;
     int64 dimension = 3;
+
     int64 index_file_size = 4;
     int64 index_file_size = 4;
+
     int32 metric_type = 5;
     int32 metric_type = 5;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Range Schema
  * @brief Range Schema
+
  */
  */
+
 message Range {
 message Range {
+
     string start_value = 1;
     string start_value = 1;
+
     string end_value = 2;
     string end_value = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Record inserted
  * @brief Record inserted
+
  */
  */
+
 message RowRecord {
 message RowRecord {
-    repeated float vector_data = 1;             //binary vector data
+
+    repeated float vector_data = 1; //binary vector data
+
 }
 }
 
 
 /**
 /**
+
  * @brief params to be inserted
  * @brief params to be inserted
+
  */
  */
+
 message InsertParam {
 message InsertParam {
+
     string table_name = 1;
     string table_name = 1;
+
     repeated RowRecord row_record_array = 2;
     repeated RowRecord row_record_array = 2;
-    repeated int64 row_id_array = 3;            //optional
+
+    repeated int64 row_id_array = 3; //optional
+
 }
 }
 
 
 /**
 /**
+
  * @brief Vector ids
  * @brief Vector ids
+
  */
  */
+
 message VectorIds {
 message VectorIds {
+
     Status status = 1;
     Status status = 1;
+
     repeated int64 vector_id_array = 2;
     repeated int64 vector_id_array = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief params for searching vector
  * @brief params for searching vector
+
  */
  */
+
 message SearchParam {
 message SearchParam {
+
     string table_name = 1;
     string table_name = 1;
+
     repeated RowRecord query_record_array = 2;
     repeated RowRecord query_record_array = 2;
+
     repeated Range query_range_array = 3;
     repeated Range query_range_array = 3;
+
     int64 topk = 4;
     int64 topk = 4;
+
     int64 nprobe = 5;
     int64 nprobe = 5;
+
 }
 }
 
 
 /**
 /**
+
  * @brief params for searching vector in files
  * @brief params for searching vector in files
+
  */
  */
+
 message SearchInFilesParam {
 message SearchInFilesParam {
+
     repeated string file_id_array = 1;
     repeated string file_id_array = 1;
+
     SearchParam search_param = 2;
     SearchParam search_param = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Query result params
  * @brief Query result params
- */
-message QueryResult {
-    int64 id = 1;
-    double distance = 2;
-}
 
 
-/**
- * @brief TopK query result
  */
  */
+
 message TopKQueryResult {
 message TopKQueryResult {
-    repeated QueryResult query_result_arrays = 1;
-}
 
 
-/**
- * @brief List of topK query result
- */
-message TopKQueryResultList {
     Status status = 1;
     Status status = 1;
-    repeated TopKQueryResult topk_query_result = 2;
+
+    int64 row_num = 2;
+
+    repeated int64 ids = 3;
+
+    repeated float distances = 4;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Server String Reply
  * @brief Server String Reply
+
  */
  */
+
 message StringReply {
 message StringReply {
+
     Status status = 1;
     Status status = 1;
+
     string string_reply = 2;
     string string_reply = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Server bool Reply
  * @brief Server bool Reply
+
  */
  */
+
 message BoolReply {
 message BoolReply {
+
     Status status = 1;
     Status status = 1;
+
     bool bool_reply = 2;
     bool bool_reply = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Return table row count
  * @brief Return table row count
+
  */
  */
+
 message TableRowCount {
 message TableRowCount {
+
     Status status = 1;
     Status status = 1;
+
     int64 table_row_count = 2;
     int64 table_row_count = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Give Server Command
  * @brief Give Server Command
+
  */
  */
+
 message Command {
 message Command {
+
     string cmd = 1;
     string cmd = 1;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Index
  * @brief Index
+
  * @index_type: 0-invalid, 1-idmap, 2-ivflat, 3-ivfsq8, 4-nsgmix
  * @index_type: 0-invalid, 1-idmap, 2-ivflat, 3-ivfsq8, 4-nsgmix
+
  * @metric_type: 1-L2, 2-IP
  * @metric_type: 1-L2, 2-IP
+
  */
  */
+
 message Index {
 message Index {
+
     int32 index_type = 1;
     int32 index_type = 1;
+
     int32 nlist = 2;
     int32 nlist = 2;
+
 }
 }
 
 
 /**
 /**
+
  * @brief Index params
  * @brief Index params
+
  */
  */
+
 message IndexParam {
 message IndexParam {
+
     Status status = 1;
     Status status = 1;
+
     string table_name = 2;
     string table_name = 2;
+
     Index index = 3;
     Index index = 3;
+
 }
 }
 
 
 /**
 /**
+
  * @brief table name and range for DeleteByRange
  * @brief table name and range for DeleteByRange
+
  */
  */
+
 message DeleteByRangeParam {
 message DeleteByRangeParam {
+
     Range range = 1;
     Range range = 1;
+
     string table_name = 2;
     string table_name = 2;
+
 }
 }
 
 
 service MilvusService {
 service MilvusService {
+
     /**
     /**
+
      * @brief Create table method
      * @brief Create table method
+
      *
      *
+
      * This method is used to create table
      * This method is used to create table
+
      *
      *
+
      * @param param, use to provide table information to be created.
      * @param param, use to provide table information to be created.
+
      *
      *
+
      */
      */
-    rpc CreateTable(TableSchema) returns (Status){}
+
+    rpc CreateTable (TableSchema) returns (Status) {
+    }
 
 
     /**
     /**
+
      * @brief Test table existence method
      * @brief Test table existence method
+
      *
      *
+
      * This method is used to test table existence.
      * This method is used to test table existence.
+
      *
      *
+
      * @param table_name, table name is going to be tested.
      * @param table_name, table name is going to be tested.
+
      *
      *
+
      */
      */
-    rpc HasTable(TableName) returns (BoolReply) {}
+
+    rpc HasTable (TableName) returns (BoolReply) {
+    }
 
 
     /**
     /**
+
      * @brief Delete table method
      * @brief Delete table method
+
      *
      *
+
      * This method is used to delete table.
      * This method is used to delete table.
+
      *
      *
+
      * @param table_name, table name is going to be deleted.
      * @param table_name, table name is going to be deleted.
+
      *
      *
+
      */
      */
-    rpc DropTable(TableName) returns (Status) {}
+
+    rpc DropTable (TableName) returns (Status) {
+    }
 
 
     /**
     /**
+
      * @brief Build index by table method
      * @brief Build index by table method
+
      *
      *
+
      * This method is used to build index by table in sync mode.
      * This method is used to build index by table in sync mode.
+
      *
      *
+
      * @param table_name, table is going to be built index.
      * @param table_name, table is going to be built index.
+
      *
      *
+
      */
      */
-    rpc CreateIndex(IndexParam) returns (Status) {}
+
+    rpc CreateIndex (IndexParam) returns (Status) {
+    }
 
 
     /**
     /**
+
      * @brief Add vector array to table
      * @brief Add vector array to table
+
      *
      *
+
      * This method is used to add vector array to table.
      * This method is used to add vector array to table.
+
      *
      *
+
      * @param table_name, table_name is inserted.
      * @param table_name, table_name is inserted.
+
      * @param record_array, vector array is inserted.
      * @param record_array, vector array is inserted.
+
      *
      *
+
      * @return vector id array
      * @return vector id array
+
      */
      */
-    rpc Insert(InsertParam) returns (VectorIds) {}
+
+    rpc Insert (InsertParam) returns (VectorIds) {
+    }
 
 
     /**
     /**
+
      * @brief Query vector
      * @brief Query vector
+
      *
      *
+
      * This method is used to query vector in table.
      * This method is used to query vector in table.
+
      *
      *
+
      * @param table_name, table_name is queried.
      * @param table_name, table_name is queried.
+
      * @param query_record_array, all vector are going to be queried.
      * @param query_record_array, all vector are going to be queried.
+
      * @param query_range_array, optional ranges for conditional search. If not specified, search whole table
      * @param query_range_array, optional ranges for conditional search. If not specified, search whole table
+
      * @param topk, how many similarity vectors will be searched.
      * @param topk, how many similarity vectors will be searched.
+
      *
      *
+
      * @return query result array.
      * @return query result array.
+
      */
      */
-    rpc Search(SearchParam) returns (TopKQueryResultList) {}
+
+    rpc Search (SearchParam) returns (TopKQueryResult) {
+    }
 
 
     /**
     /**
+
      * @brief Internal use query interface
      * @brief Internal use query interface
+
      *
      *
+
      * This method is used to query vector in specified files.
      * This method is used to query vector in specified files.
+
      *
      *
+
      * @param file_id_array, specified files id array, queried.
      * @param file_id_array, specified files id array, queried.
+
      * @param query_record_array, all vector are going to be queried.
      * @param query_record_array, all vector are going to be queried.
+
      * @param query_range_array, optional ranges for conditional search. If not specified, search whole table
      * @param query_range_array, optional ranges for conditional search. If not specified, search whole table
+
      * @param topk, how many similarity vectors will be searched.
      * @param topk, how many similarity vectors will be searched.
+
      *
      *
+
      * @return query result array.
      * @return query result array.
+
      */
      */
-    rpc SearchInFiles(SearchInFilesParam) returns (TopKQueryResultList) {}
+
+    rpc SearchInFiles (SearchInFilesParam) returns (TopKQueryResult) {
+    }
 
 
     /**
     /**
+
      * @brief Get table schema
      * @brief Get table schema
+
      *
      *
+
      * This method is used to get table schema.
      * This method is used to get table schema.
+
      *
      *
+
      * @param table_name, target table name.
      * @param table_name, target table name.
+
      *
      *
+
      * @return table schema
      * @return table schema
+
      */
      */
-    rpc DescribeTable(TableName) returns (TableSchema) {}
+
+    rpc DescribeTable (TableName) returns (TableSchema) {
+    }
 
 
     /**
     /**
+
      * @brief Get table schema
      * @brief Get table schema
+
      *
      *
+
      * This method is used to get table schema.
      * This method is used to get table schema.
+
      *
      *
+
      * @param table_name, target table name.
      * @param table_name, target table name.
+
      *
      *
+
      * @return table schema
      * @return table schema
+
      */
      */
-    rpc CountTable(TableName) returns (TableRowCount) {}
+
+    rpc CountTable (TableName) returns (TableRowCount) {
+    }
 
 
     /**
     /**
+
      * @brief List all tables in database
      * @brief List all tables in database
+
      *
      *
+
      * This method is used to list all tables.
      * This method is used to list all tables.
+
      *
      *
+
      *
      *
+
      * @return table names.
      * @return table names.
+
      */
      */
-    rpc ShowTables(Command) returns (TableNameList) {}
+
+    rpc ShowTables (Command) returns (TableNameList) {
+    }
 
 
     /**
     /**
+
      * @brief Give the server status
      * @brief Give the server status
+
      *
      *
+
      * This method is used to give the server status.
      * This method is used to give the server status.
+
      *
      *
+
      * @return Server status.
      * @return Server status.
+
      */
      */
-    rpc Cmd(Command) returns (StringReply) {}
+
+    rpc Cmd (Command) returns (StringReply) {
+    }
 
 
     /**
     /**
+
     * @brief delete table by range
     * @brief delete table by range
+
     *
     *
+
     * This method is used to delete vector by range
     * This method is used to delete vector by range
+
     *
     *
+
     * @return rpc status.
     * @return rpc status.
+
     */
     */
-    rpc DeleteByRange(DeleteByRangeParam) returns (Status) {}
+
+    rpc DeleteByRange (DeleteByRangeParam) returns (Status) {
+    }
 
 
     /**
     /**
+
      * @brief preload table
      * @brief preload table
+
      *
      *
+
      * This method is used to preload table
      * This method is used to preload table
+
      *
      *
+
      * @return Status.
      * @return Status.
+
      */
      */
-    rpc PreloadTable(TableName) returns (Status) {}
+
+    rpc PreloadTable (TableName) returns (Status) {
+    }
 
 
     /**
     /**
+
      * @brief describe index
      * @brief describe index
+
      *
      *
+
      * This method is used to describe index
      * This method is used to describe index
+
      *
      *
+
      * @return Status.
      * @return Status.
+
      */
      */
-    rpc DescribeIndex(TableName) returns (IndexParam) {}
+
+    rpc DescribeIndex (TableName) returns (IndexParam) {
+    }
 
 
     /**
     /**
+
      * @brief drop index
      * @brief drop index
+
      *
      *
+
      * This method is used to drop index
      * This method is used to drop index
+
      *
      *
+
      * @return Status.
      * @return Status.
+
      */
      */
-    rpc DropIndex(TableName) returns (Status) {}
+
+    rpc DropIndex (TableName) returns (Status) {
+    }
 
 
 }
 }

+ 60 - 14
src/test/java/io/milvus/client/MilvusGrpcClientTest.java

@@ -89,11 +89,52 @@ class MilvusClientTest {
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
-  void connectInvalidPort() {
+  void idleTest() throws InterruptedException, ConnectFailedException {
     MilvusClient client = new MilvusGrpcClient();
     MilvusClient client = new MilvusGrpcClient();
     ConnectParam connectParam =
     ConnectParam connectParam =
-        new ConnectParam.Builder().withHost("localhost").withPort("66666").build();
-    assertThrows(ConnectFailedException.class, () -> client.connect(connectParam));
+        new ConnectParam.Builder()
+            .withHost("localhost")
+            .withIdleTimeout(1, TimeUnit.SECONDS)
+            .build();
+    client.connect(connectParam);
+    TimeUnit.SECONDS.sleep(2);
+    // Channel should be idle
+    assertFalse(client.isConnected());
+    // A new RPC would take the channel out of idle mode
+    assertTrue(client.showTables().ok());
+  }
+
+  @org.junit.jupiter.api.Test
+  void setInvalidConnectParam() {
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> {
+          ConnectParam connectParam = new ConnectParam.Builder().withPort("66666").build();
+        });
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> {
+          ConnectParam connectParam =
+              new ConnectParam.Builder().withConnectTimeout(-1, TimeUnit.MILLISECONDS).build();
+        });
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> {
+          ConnectParam connectParam =
+              new ConnectParam.Builder().withKeepAliveTime(-1, TimeUnit.MILLISECONDS).build();
+        });
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> {
+          ConnectParam connectParam =
+              new ConnectParam.Builder().withKeepAliveTimeout(-1, TimeUnit.MILLISECONDS).build();
+        });
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> {
+          ConnectParam connectParam =
+              new ConnectParam.Builder().withIdleTimeout(-1, TimeUnit.MILLISECONDS).build();
+        });
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -120,7 +161,7 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void hasTable() {
   void hasTable() {
     HasTableResponse hasTableResponse = client.hasTable(randomTableName);
     HasTableResponse hasTableResponse = client.hasTable(randomTableName);
-    assertTrue(hasTableResponse.getResponse().ok());
+    assertTrue(hasTableResponse.ok());
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -145,7 +186,7 @@ class MilvusClientTest {
     List<List<Float>> vectors = generateVectors(size, dimension);
     List<List<Float>> vectors = generateVectors(size, dimension);
     InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
     InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
     InsertResponse insertResponse = client.insert(insertParam);
     InsertResponse insertResponse = client.insert(insertParam);
-    assertTrue(insertResponse.getResponse().ok());
+    assertTrue(insertResponse.ok());
     assertEquals(size, insertResponse.getVectorIds().size());
     assertEquals(size, insertResponse.getVectorIds().size());
   }
   }
 
 
@@ -155,7 +196,7 @@ class MilvusClientTest {
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     vectors = vectors.stream().map(MilvusClientTest::normalizeVector).collect(Collectors.toList());
     InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
     InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
     InsertResponse insertResponse = client.insert(insertParam);
     InsertResponse insertResponse = client.insert(insertParam);
-    assertTrue(insertResponse.getResponse().ok());
+    assertTrue(insertResponse.ok());
     List<Long> vectorIds = insertResponse.getVectorIds();
     List<Long> vectorIds = insertResponse.getVectorIds();
     assertEquals(size, vectorIds.size());
     assertEquals(size, vectorIds.size());
 
 
@@ -174,7 +215,7 @@ class MilvusClientTest {
     c.add(Calendar.DAY_OF_MONTH, 1);
     c.add(Calendar.DAY_OF_MONTH, 1);
     Date tomorrow = c.getTime();
     Date tomorrow = c.getTime();
     queryRanges.add(new DateRange(yesterday, tomorrow));
     queryRanges.add(new DateRange(yesterday, tomorrow));
-    final long topK = 1000;
+    final long topK = 10;
     SearchParam searchParam =
     SearchParam searchParam =
         new SearchParam.Builder(randomTableName, vectorsToSearch)
         new SearchParam.Builder(randomTableName, vectorsToSearch)
             .withTopK(topK)
             .withTopK(topK)
@@ -182,15 +223,20 @@ class MilvusClientTest {
             .withDateRanges(queryRanges)
             .withDateRanges(queryRanges)
             .build();
             .build();
     SearchResponse searchResponse = client.search(searchParam);
     SearchResponse searchResponse = client.search(searchParam);
-    assertTrue(searchResponse.getResponse().ok());
-    System.out.println(searchResponse);
+    assertTrue(searchResponse.ok());
+    List<List<Long>> resultIdsList = searchResponse.getResultIdsList();
+    assertEquals(searchSize, resultIdsList.size());
+    List<List<Float>> resultDistancesList = searchResponse.getResultDistancesList();
+    assertEquals(searchSize, resultDistancesList.size());
     List<List<SearchResponse.QueryResult>> queryResultsList = searchResponse.getQueryResultsList();
     List<List<SearchResponse.QueryResult>> queryResultsList = searchResponse.getQueryResultsList();
     assertEquals(searchSize, queryResultsList.size());
     assertEquals(searchSize, queryResultsList.size());
     final double epsilon = 0.001;
     final double epsilon = 0.001;
     for (int i = 0; i < searchSize; i++) {
     for (int i = 0; i < searchSize; i++) {
       SearchResponse.QueryResult firstQueryResult = queryResultsList.get(i).get(0);
       SearchResponse.QueryResult firstQueryResult = queryResultsList.get(i).get(0);
       assertEquals(vectorIds.get(i), firstQueryResult.getVectorId());
       assertEquals(vectorIds.get(i), firstQueryResult.getVectorId());
+      assertEquals(vectorIds.get(i), resultIdsList.get(i).get(0));
       assertTrue(Math.abs(1 - firstQueryResult.getDistance()) < epsilon);
       assertTrue(Math.abs(1 - firstQueryResult.getDistance()) < epsilon);
+      assertTrue(Math.abs(1 - resultDistancesList.get(i).get(0)) < epsilon);
     }
     }
   }
   }
 
 
@@ -201,19 +247,19 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void describeTable() {
   void describeTable() {
     DescribeTableResponse describeTableResponse = client.describeTable(randomTableName);
     DescribeTableResponse describeTableResponse = client.describeTable(randomTableName);
-    assertTrue(describeTableResponse.getResponse().ok());
+    assertTrue(describeTableResponse.ok());
     assertTrue(describeTableResponse.getTableSchema().isPresent());
     assertTrue(describeTableResponse.getTableSchema().isPresent());
 
 
     String nonExistingTableName = generator.generate(10);
     String nonExistingTableName = generator.generate(10);
     describeTableResponse = client.describeTable(nonExistingTableName);
     describeTableResponse = client.describeTable(nonExistingTableName);
-    assertFalse(describeTableResponse.getResponse().ok());
+    assertFalse(describeTableResponse.ok());
     assertFalse(describeTableResponse.getTableSchema().isPresent());
     assertFalse(describeTableResponse.getTableSchema().isPresent());
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void showTables() {
   void showTables() {
     ShowTablesResponse showTablesResponse = client.showTables();
     ShowTablesResponse showTablesResponse = client.showTables();
-    assertTrue(showTablesResponse.getResponse().ok());
+    assertTrue(showTablesResponse.ok());
   }
   }
 
 
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
@@ -234,7 +280,7 @@ class MilvusClientTest {
     TimeUnit.SECONDS.sleep(1);
     TimeUnit.SECONDS.sleep(1);
 
 
     GetTableRowCountResponse getTableRowCountResponse = client.getTableRowCount(randomTableName);
     GetTableRowCountResponse getTableRowCountResponse = client.getTableRowCount(randomTableName);
-    assertTrue(getTableRowCountResponse.getResponse().ok());
+    assertTrue(getTableRowCountResponse.ok());
     assertEquals(size, getTableRowCountResponse.getTableRowCount());
     assertEquals(size, getTableRowCountResponse.getTableRowCount());
   }
   }
 
 
@@ -247,7 +293,7 @@ class MilvusClientTest {
   @org.junit.jupiter.api.Test
   @org.junit.jupiter.api.Test
   void describeIndex() {
   void describeIndex() {
     DescribeIndexResponse describeIndexResponse = client.describeIndex(randomTableName);
     DescribeIndexResponse describeIndexResponse = client.describeIndex(randomTableName);
-    assertTrue(describeIndexResponse.getResponse().ok());
+    assertTrue(describeIndexResponse.ok());
     assertTrue(describeIndexResponse.getIndex().isPresent());
     assertTrue(describeIndexResponse.getIndex().isPresent());
   }
   }