Quellcode durchsuchen

Merge pull request #1 from youny626/branch-0.5.0

First implementation
Jin Hai vor 5 Jahren
Ursprung
Commit
367d12991b
30 geänderte Dateien mit 2601 neuen und 0 gelöschten Zeilen
  1. 6 0
      .gitignore
  2. 2 0
      milvus-sdk-java.iml
  3. 128 0
      pom.xml
  4. 50 0
      src/main/java/io/milvus/client/CommandParam.java
  5. 49 0
      src/main/java/io/milvus/client/ConnectParam.java
  6. 36 0
      src/main/java/io/milvus/client/DateRange.java
  7. 59 0
      src/main/java/io/milvus/client/DeleteByRangeParam.java
  8. 28 0
      src/main/java/io/milvus/client/DescribeIndexResponse.java
  9. 28 0
      src/main/java/io/milvus/client/DescribeTableResponse.java
  10. 26 0
      src/main/java/io/milvus/client/GetTableRowCountResponse.java
  11. 26 0
      src/main/java/io/milvus/client/HasTableResponse.java
  12. 41 0
      src/main/java/io/milvus/client/Index.java
  13. 61 0
      src/main/java/io/milvus/client/IndexParam.java
  14. 32 0
      src/main/java/io/milvus/client/IndexType.java
  15. 64 0
      src/main/java/io/milvus/client/InsertParam.java
  16. 30 0
      src/main/java/io/milvus/client/InsertResponse.java
  17. 28 0
      src/main/java/io/milvus/client/MetricType.java
  18. 49 0
      src/main/java/io/milvus/client/MilvusClient.java
  19. 755 0
      src/main/java/io/milvus/client/MilvusGrpcClient.java
  20. 93 0
      src/main/java/io/milvus/client/Response.java
  21. 51 0
      src/main/java/io/milvus/client/SearchInFilesParam.java
  22. 89 0
      src/main/java/io/milvus/client/SearchParam.java
  23. 50 0
      src/main/java/io/milvus/client/SearchResponse.java
  24. 28 0
      src/main/java/io/milvus/client/ShowTablesResponse.java
  25. 50 0
      src/main/java/io/milvus/client/TableParam.java
  26. 68 0
      src/main/java/io/milvus/client/TableSchema.java
  27. 50 0
      src/main/java/io/milvus/client/TableSchemaParam.java
  28. 327 0
      src/main/proto/milvus.proto
  29. 40 0
      src/main/proto/status.proto
  30. 257 0
      src/test/java/io/milvus/client/MilvusGrpcClientTest.java

+ 6 - 0
.gitignore

@@ -21,3 +21,9 @@
 
 # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
 hs_err_pid*
+
+.idea/
+target/
+index-files/
+io/
+javadoc/

+ 2 - 0
milvus-sdk-java.iml

@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4" />

+ 128 - 0
pom.xml

@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>io.milvus</groupId>
+    <artifactId>milvus-sdk-java</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <name>milvus-sdk-java</name>
+    <description>Java SDK for Milvus distributed high-performance vector search engine. </description>
+    <url>https://github.com/milvus-io/milvus-sdk-java</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <grpc.version>1.24.0</grpc.version><!-- CURRENT_GRPC_VERSION -->
+        <protobuf.version>3.9.0</protobuf.version>
+        <protoc.version>3.9.0</protoc.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.grpc</groupId>
+                <artifactId>grpc-bom</artifactId>
+                <version>${grpc.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-netty-shaded</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-protobuf</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.grpc</groupId>
+            <artifactId>grpc-stub</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.2</version>
+            <scope>provided</scope> <!-- not needed at runtime -->
+        </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>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <version>RELEASE</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-text -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>1.6</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <extensions>
+            <extension>
+                <groupId>kr.motd.maven</groupId>
+                <artifactId>os-maven-plugin</artifactId>
+                <version>1.6.2</version>
+            </extension>
+        </extensions>
+        <plugins>
+            <plugin>
+                <groupId>org.xolstice.maven.plugins</groupId>
+                <artifactId>protobuf-maven-plugin</artifactId>
+                <version>0.6.1</version>
+                <configuration>
+                    <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
+                    <pluginId>grpc-java</pluginId>
+                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>compile</goal>
+                            <goal>compile-custom</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>1.4.1</version>
+                <executions>
+                    <execution>
+                        <id>enforce</id>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireUpperBoundDeps/>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 50 - 0
src/main/java/io/milvus/client/CommandParam.java

@@ -0,0 +1,50 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class CommandParam {
+    private final String command;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final String command;
+
+        // Optional parameters - initialized to default values
+        private long timeout = 10;
+
+        public Builder(String command) {
+            this.command = command;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public CommandParam build() {
+            return new CommandParam(this);
+        }
+    }
+
+    private CommandParam(@Nonnull Builder builder) {
+        this.command = builder.command;
+        this.timeout = builder.timeout;
+    }
+
+    public String getCommand() {
+        return command;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public String toString() {
+        return "CommandParam {" +
+                "command='" + command + '\'' +
+                ", timeout=" + timeout +
+                '}';
+    }
+}

+ 49 - 0
src/main/java/io/milvus/client/ConnectParam.java

@@ -0,0 +1,49 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class ConnectParam {
+    private final String host;
+    private final String port;
+
+    public static class Builder {
+        // Optional parameters - initialized to default values
+        private String host = "127.0.0.1";
+        private String port = "19530";
+
+        public Builder withHost(String host) {
+            this.host = host;
+            return this;
+        }
+
+        public Builder withPort(String port) {
+            this.port = port;
+            return this;
+        }
+
+        public ConnectParam build() {
+            return new ConnectParam(this);
+        }
+    }
+
+    private ConnectParam(@Nonnull Builder builder) {
+        this.host = builder.host;
+        this.port = builder.port;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public String getPort() {
+        return port;
+    }
+
+    @Override
+    public String toString() {
+        return "ConnectParam {" +
+                "host='" + host + '\'' +
+                ", port='" + port + '\'' +
+                '}';
+    }
+}

+ 36 - 0
src/main/java/io/milvus/client/DateRange.java

@@ -0,0 +1,36 @@
+package io.milvus.client;
+
+import java.util.Date;
+
+public class DateRange {
+    private Date startDate;
+    private Date endDate;
+
+    public DateRange(Date startDate, Date endDate) {
+        this.startDate = startDate;
+        this.endDate = endDate;
+    }
+
+    public void setStartDate(Date startDate) {
+        this.startDate = startDate;
+    }
+
+    public void setEndDate(Date endDate) {
+        this.endDate = endDate;
+    }
+
+    public Date getStartDate() {
+        return startDate;
+    }
+
+    public Date getEndDate() {
+        return endDate;
+    }
+
+    @Override
+    public String toString() {
+        return "{startDate=" + startDate +
+                ", endDate=" + endDate +
+                '}';
+    }
+}

+ 59 - 0
src/main/java/io/milvus/client/DeleteByRangeParam.java

@@ -0,0 +1,59 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class DeleteByRangeParam {
+    private final DateRange dateRange;
+    private final String tableName;
+    private final long timeout;
+
+    public static final class Builder {
+        // Required parameters
+        private final DateRange dateRange;
+        private final String tableName;
+
+        // Optional parameters - initialized to default values
+        private long timeout = 10;
+
+        public Builder(DateRange dateRange, String tableName) {
+            this.dateRange = dateRange;
+            this.tableName = tableName;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public DeleteByRangeParam build() {
+            return new DeleteByRangeParam(this);
+        }
+    }
+
+    private DeleteByRangeParam(@Nonnull Builder builder) {
+        this.dateRange = builder.dateRange;
+        this.tableName = builder.tableName;
+        this.timeout = builder.timeout;
+    }
+
+    public DateRange getDateRange() {
+        return dateRange;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public String toString() {
+        return "DeleteByRangeParam {" +
+                "dateRange = " + dateRange.toString() +
+                ", tableName = '" + tableName + '\'' +
+                ", timeout = " + timeout +
+                '}';
+    }
+}

+ 28 - 0
src/main/java/io/milvus/client/DescribeIndexResponse.java

@@ -0,0 +1,28 @@
+package io.milvus.client;
+
+import javax.annotation.Nullable;
+import java.util.Optional;
+
+public class DescribeIndexResponse {
+    private final Response response;
+    private final IndexParam indexParam;
+
+    public DescribeIndexResponse(Response response, @Nullable IndexParam indexParam) {
+        this.response = response;
+        this.indexParam = indexParam;
+    }
+
+    public Optional<IndexParam> getIndexParam() {
+        return Optional.ofNullable(indexParam);
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("DescribeIndexResponse {%s, %s}", response.toString(),
+                              indexParam == null ? "Index param = None" : indexParam.toString());
+    }
+}

+ 28 - 0
src/main/java/io/milvus/client/DescribeTableResponse.java

@@ -0,0 +1,28 @@
+package io.milvus.client;
+
+import javax.annotation.Nullable;
+import java.util.Optional;
+
+public class DescribeTableResponse {
+    private final Response response;
+    private final TableSchema tableSchema;
+
+    public DescribeTableResponse(Response response, @Nullable TableSchema tableSchema) {
+        this.response = response;
+        this.tableSchema = tableSchema;
+    }
+
+    public Optional<TableSchema> getTableSchema() {
+        return Optional.ofNullable(tableSchema);
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("DescribeTableResponse {%s, %s}", response.toString(),
+                              tableSchema == null ? "Table schema = None" : tableSchema.toString());
+    }
+}

+ 26 - 0
src/main/java/io/milvus/client/GetTableRowCountResponse.java

@@ -0,0 +1,26 @@
+package io.milvus.client;
+
+public class GetTableRowCountResponse {
+    private final Response response;
+    private final long tableRowCount;
+
+    public GetTableRowCountResponse(Response response, long tableRowCount) {
+        this.response = response;
+        this.tableRowCount = tableRowCount;
+    }
+
+    public long getTableRowCount() {
+        return tableRowCount;
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("CountTableResponse {%s, table row count = %d}",
+                              response.toString(),
+                              tableRowCount);
+    }
+}

+ 26 - 0
src/main/java/io/milvus/client/HasTableResponse.java

@@ -0,0 +1,26 @@
+package io.milvus.client;
+
+public class HasTableResponse {
+    private final Response response;
+    private final boolean hasTable;
+
+    public HasTableResponse(Response response, boolean hasTable) {
+        this.response = response;
+        this.hasTable = hasTable;
+    }
+
+    public boolean hasTable() {
+        return hasTable;
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("HasTableResponse {%s, has table = %s}",
+                              response.toString(),
+                              hasTable);
+    }
+}

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

@@ -0,0 +1,41 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class Index {
+    private final IndexType indexType;
+    private final int nList;
+
+    public static class Builder {
+        // Optional parameters - initialized to default values
+        private IndexType indexType = IndexType.FLAT;
+        private int nList = 16384;
+
+        public Builder withIndexType(IndexType indexType) {
+            this.indexType = indexType;
+            return this;
+        }
+
+        public Builder withNList(int nList) {
+            this.nList = nList;
+            return this;
+        }
+
+        public Index build() {
+            return new Index(this);
+        }
+    }
+
+    private Index(@Nonnull Builder builder) {
+        this.indexType = builder.indexType;
+        this.nList = builder.nList;
+    }
+
+    public IndexType getIndexType() {
+        return indexType;
+    }
+
+    public int getNList() {
+        return nList;
+    }
+}

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

@@ -0,0 +1,61 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class IndexParam {
+
+    private final String tableName;
+    private final Index index;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final String tableName;
+
+        // Optional parameters - initialized to default values
+        private Index index;
+        private long timeout = 10;
+
+        public Builder(String tableName) {
+            this.tableName = tableName;
+        }
+
+        public Builder withIndex(Index index) {
+            this.index = index;
+            return this;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public IndexParam build() {
+            return new IndexParam(this);
+        }
+    }
+
+    private IndexParam(@Nonnull Builder builder) {
+        this.tableName = builder.tableName;
+        this.index = builder.index;
+        this.timeout = builder.timeout;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public Index getIndex() {
+        return index;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("IndexParam = {tableName = %s, index = {indexType = %s, nList = %d}, timeout = %d}",
+                                tableName, index.getIndexType().name(), index.getNList(), timeout);
+    }
+}

+ 32 - 0
src/main/java/io/milvus/client/IndexType.java

@@ -0,0 +1,32 @@
+package io.milvus.client;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public enum IndexType {
+
+    INVALID(0),
+    FLAT(1),
+    IVFLAT(2),
+    IVF_SQ8(3),
+    MIX_NSG(4),
+
+    UNKNOWN(-1);
+
+    private final int val;
+
+    IndexType(int val) {
+        this.val = val;
+    }
+
+    public int getVal() {
+        return val;
+    }
+
+    public static IndexType valueOf(int val) {
+        Optional<IndexType> search = Arrays.stream(values())
+                                           .filter(indexType -> indexType.val == val)
+                                           .findFirst();
+        return search.orElse(UNKNOWN);
+    }
+}

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

@@ -0,0 +1,64 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+
+public class InsertParam {
+    private final String tableName;
+    private final List<List<Float>> vectors;
+    private final List<Long> vectorIds;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final String tableName;
+        private final List<List<Float>> vectors;
+
+        // Optional parameters - initialized to default values
+        private List<Long> vectorIds = new ArrayList<>();
+        private long timeout = 10;
+
+        public Builder(String tableName, List<List<Float>> vectors) {
+            this.tableName = tableName;
+            this.vectors = vectors;
+        }
+
+        public Builder withVectorIds(List<Long> vectorIds) {
+            this.vectorIds = vectorIds;
+            return this;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public InsertParam build() {
+            return new InsertParam(this);
+        }
+    }
+
+    private InsertParam(@Nonnull Builder builder) {
+        this.tableName = builder.tableName;
+        this.vectors = builder.vectors;
+        this.vectorIds = builder.vectorIds;
+        this.timeout = builder.timeout;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public List<List<Float>> getVectors() {
+        return vectors;
+    }
+
+    public List<Long> getVectorIds() {
+        return vectorIds;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+}

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

@@ -0,0 +1,30 @@
+package io.milvus.client;
+
+import java.util.List;
+
+public class InsertResponse {
+    private final Response response;
+    private final List<Long> vectorIds;
+
+    public InsertResponse(Response response, List<Long> vectorIds) {
+        this.response = response;
+        this.vectorIds = vectorIds;
+    }
+
+    public List<Long> getVectorIds() {
+        return vectorIds;
+    }
+
+    //TODO: iterator
+
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("InsertResponse {%s, returned %d vector ids}",
+                              response.toString(), this.vectorIds.size());
+    }
+}

+ 28 - 0
src/main/java/io/milvus/client/MetricType.java

@@ -0,0 +1,28 @@
+package io.milvus.client;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public enum MetricType {
+    L2(1),
+    IP(2),
+
+    UNKNOWN(-1);
+
+    private final int val;
+
+    MetricType(int val) {
+        this.val = val;
+    }
+
+    public int getVal() {
+        return val;
+    }
+
+    public static MetricType valueOf(int val) {
+        Optional<MetricType> search = Arrays.stream(values())
+                                     .filter(metricType -> metricType.val == val)
+                                     .findFirst();
+        return search.orElse(UNKNOWN);
+    }
+}

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

@@ -0,0 +1,49 @@
+package io.milvus.client;
+
+public interface MilvusClient {
+
+    String clientVersion = "0.1.0";
+    default String clientVersion() {
+        return clientVersion;
+    }
+
+    Response connect(ConnectParam connectParam);
+
+    boolean connected();
+
+    Response disconnect() throws InterruptedException;
+
+    Response createTable(TableSchemaParam tableSchemaParam);
+
+    HasTableResponse hasTable(TableParam tableParam);
+
+    Response dropTable(TableParam tableParam);
+
+    Response createIndex(IndexParam indexParam);
+
+    InsertResponse insert(InsertParam insertParam);
+
+    SearchResponse search(SearchParam searchParam);
+
+    SearchResponse searchInFiles(SearchInFilesParam searchInFilesParam);
+
+    DescribeTableResponse describeTable(TableParam tableParam);
+
+    ShowTablesResponse showTables();
+
+    GetTableRowCountResponse getTableRowCount(TableParam tableParam);
+
+    Response serverStatus();
+
+    Response serverVersion();
+
+//    Response command(CommandParam commandParam);
+
+    Response deleteByRange(DeleteByRangeParam deleteByRangeParam);
+
+    Response preloadTable(TableParam tableParam);
+
+    DescribeIndexResponse describeIndex(TableParam tableParam);
+
+    Response dropIndex(TableParam tableParam);
+}

+ 755 - 0
src/main/java/io/milvus/client/MilvusGrpcClient.java

@@ -0,0 +1,755 @@
+package io.milvus.client;
+
+import io.grpc.*;
+import io.milvus.client.grpc.Command;
+
+import javax.annotation.Nonnull;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class MilvusGrpcClient implements MilvusClient {
+
+    private static final Logger logger = Logger.getLogger(MilvusGrpcClient.class.getName());
+
+    private ManagedChannel channel = null;
+    private io.milvus.client.grpc.MilvusServiceGrpc.MilvusServiceBlockingStub blockingStub;
+
+//    private MilvusGrpcClient(String host, int port) {
+//        this(ManagedChannelBuilder.forAddress(host, port).usePlaintext());
+//    }
+//
+//    private MilvusGrpcClient(ManagedChannelBuilder<?> channelBuilder) {
+//        channel = channelBuilder.build();
+//        blockingStub = io.milvus.client.grpc.MilvusServiceGrpc.newBlockingStub(channel);
+//    }
+
+    ///////////////////////Client Calls///////////////////////
+
+    @Override
+    public Response connect(ConnectParam connectParam) {
+        if (channel != null) {
+            logWarning("You have already connected!");
+            return new Response(Response.Status.CONNECT_FAILED, "You have already connected!");
+        }
+
+        try {
+            channel = ManagedChannelBuilder
+                    .forAddress(connectParam.getHost(), Integer.parseInt(connectParam.getPort()))
+                    .usePlaintext()
+                    .build();
+
+            ConnectivityState connectivityState = channel.getState(true);
+            connectivityState = channel.getState(false);
+
+            blockingStub = io.milvus.client.grpc.MilvusServiceGrpc.newBlockingStub(channel);
+
+        } catch (Exception e) {
+            logSevere("Connect failed!", e.toString());
+            return new Response(Response.Status.CONNECT_FAILED);
+        }
+
+        logInfo("Connected successfully!\n{0}", connectParam.toString());
+        return new Response(Response.Status.SUCCESS);
+    }
+
+    @Override
+    public boolean connected() {
+        return channel != null && !channel.isShutdown() && !channel.isTerminated();
+    }
+
+    @Override
+    public Response disconnect() throws InterruptedException {
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+        else {
+            if (channel.shutdown().awaitTermination(60, TimeUnit.SECONDS)) {
+                logInfo("Channel terminated");
+            } else {
+                logSevere("Encountered error when terminating channel");
+                return new Response(Response.Status.RPC_ERROR);
+            }
+        }
+        return new Response(Response.Status.SUCCESS);
+    }
+
+    @Override
+    public Response createTable(@Nonnull TableSchemaParam tableSchemaParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        TableSchema tableSchema = tableSchemaParam.getTableSchema();
+        io.milvus.client.grpc.TableSchema request = io.milvus.client.grpc.TableSchema
+                                                    .newBuilder()
+                                                    .setTableName(tableSchema.getTableName())
+                                                    .setDimension(tableSchema.getDimension())
+                                                    .setIndexFileSize(tableSchema.getIndexFileSize())
+                                                    .setMetricType(tableSchema.getMetricType().getVal())
+                                                    .build();
+
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableSchemaParam.getTimeout(), TimeUnit.SECONDS)
+                       .createTable(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Created table successfully!\n{0}", tableSchema.toString());
+                return new Response(Response.Status.SUCCESS);
+            } else if (response.getReason().contentEquals("Table already exists")) {
+                logWarning("Table `{0}` already exists", tableSchema.getTableName());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            } else {
+                logSevere("Create table failed\n{0}\n{1}", tableSchema.toString(), response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("createTable RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    @Override
+    public HasTableResponse hasTable(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new HasTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), false);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                                                  .newBuilder()
+                                                  .setTableName(tableName)
+                                                  .build();
+        io.milvus.client.grpc.BoolReply response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .hasTable(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("hasTable `{0}` = {1}", tableName, response.getBoolReply());
+                return new HasTableResponse(new Response(Response.Status.SUCCESS), response.getBoolReply());
+            } else {
+                logSevere("hasTable `{0}` failed:\n{1}", tableName, response.toString());
+                return new HasTableResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                         response.getStatus().getReason()),
+                                    false);
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("hasTable RPC failed:\n{0}", e.getStatus().toString());
+            return new HasTableResponse(new Response(Response.Status.RPC_ERROR, e.toString()), false);
+        }
+    }
+
+    @Override
+    public Response dropTable(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                                                  .newBuilder()
+                                                  .setTableName(tableName)
+                                                  .build();
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .dropTable(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Dropped table `{0}` successfully!", tableName);
+                return new Response(Response.Status.SUCCESS);
+            } else {
+                logSevere("Drop table `{0}` failed:\n{1}", tableName, response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("dropTable RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    @Override
+    public Response createIndex(@Nonnull IndexParam indexParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        io.milvus.client.grpc.Index index = io.milvus.client.grpc.Index
+                                            .newBuilder()
+                                            .setIndexType(indexParam.getIndex().getIndexType().getVal())
+                                            .setNlist(indexParam.getIndex().getNList())
+                                            .build();
+        io.milvus.client.grpc.IndexParam request = io.milvus.client.grpc.IndexParam
+                                           .newBuilder()
+                                           .setTableName(indexParam.getTableName())
+                                           .setIndex(index)
+                                           .build();
+
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(indexParam.getTimeout(), TimeUnit.SECONDS)
+                       .createIndex(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Created index successfully!\n{0}", indexParam.toString());
+                return new Response(Response.Status.SUCCESS);
+            } else {
+                logSevere("Create index failed\n{0}\n{1}", indexParam.toString(), response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("createIndex RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    @Override
+    public InsertResponse insert(@Nonnull InsertParam insertParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new InsertResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+        }
+
+        List<io.milvus.client.grpc.RowRecord> rowRecordList = new ArrayList<>();
+        for (List<Float> vectors : insertParam.getVectors()) {
+            io.milvus.client.grpc.RowRecord rowRecord = io.milvus.client.grpc.RowRecord
+                                                        .newBuilder()
+                                                        .addAllVectorData(vectors)
+                                                        .build();
+            rowRecordList.add(rowRecord);
+        }
+
+        io.milvus.client.grpc.InsertParam request = io.milvus.client.grpc.InsertParam
+                                                    .newBuilder()
+                                                    .setTableName(insertParam.getTableName())
+                                                    .addAllRowRecordArray(rowRecordList)
+                                                    .addAllRowIdArray(insertParam.getVectorIds())
+                                                    .build();
+        io.milvus.client.grpc.VectorIds response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(insertParam.getTimeout(), TimeUnit.SECONDS)
+                       .insert(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                Optional<List<Long>> resultVectorIds = Optional.ofNullable(response.getVectorIdArrayList());
+                logInfo("Inserted {0} vectors to table `{1} successfully!", resultVectorIds.map(List::size).orElse(0), insertParam.getTableName());
+                return new InsertResponse(new Response(Response.Status.SUCCESS), resultVectorIds.orElse(new ArrayList<>()));
+            } else {
+                logSevere("Insert vectors failed:\n{0}", response.toString());
+                return new InsertResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                       response.getStatus().getReason()),
+                                          new ArrayList<>());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("insert RPC failed:\n{0}", e.getStatus().toString());
+            return new InsertResponse(new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+        }
+    }
+
+    @Override
+    public SearchResponse search(@Nonnull SearchParam searchParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new SearchResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+        }
+
+        List<io.milvus.client.grpc.RowRecord> queryRowRecordList = getQueryRowRecordList(searchParam);
+
+        List<io.milvus.client.grpc.Range> queryRangeList = getQueryRangeList(searchParam);
+
+        io.milvus.client.grpc.SearchParam request = io.milvus.client.grpc.SearchParam
+                                                    .newBuilder()
+                                                    .setTableName(searchParam.getTableName())
+                                                    .addAllQueryRecordArray(queryRowRecordList)
+                                                    .addAllQueryRangeArray(queryRangeList)
+                                                    .setTopk(searchParam.getTopK())
+                                                    .setNprobe(searchParam.getNProbe())
+                                                    .build();
+
+        io.milvus.client.grpc.TopKQueryResultList response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(searchParam.getTimeout(), TimeUnit.SECONDS)
+                       .search(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                List<List<SearchResponse.QueryResult>> queryResultsList = getQueryResultsList(response);
+                logInfo("Search completed successfully! Returned results for {0} queries", queryResultsList.size());
+                return new SearchResponse(new Response(Response.Status.SUCCESS), queryResultsList);
+            } else {
+                logSevere("Search failed:\n{0}", response.toString());
+                return new SearchResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                       response.getStatus().getReason()),
+                                          new ArrayList<>());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("search RPC failed:\n{0}", e.getStatus().toString());
+            return new SearchResponse(new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+        }
+    }
+
+    @Override
+    public SearchResponse searchInFiles(@Nonnull SearchInFilesParam searchInFilesParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new SearchResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+        }
+
+        SearchParam searchParam = searchInFilesParam.getSearchParam();
+
+        List<io.milvus.client.grpc.RowRecord> queryRowRecordList = getQueryRowRecordList(searchParam);
+
+        List<io.milvus.client.grpc.Range> queryRangeList = getQueryRangeList(searchParam);
+
+        io.milvus.client.grpc.SearchParam searchParamToSet = io.milvus.client.grpc.SearchParam
+                                                             .newBuilder()
+                                                             .setTableName(searchParam.getTableName())
+                                                             .addAllQueryRecordArray(queryRowRecordList)
+                                                             .addAllQueryRangeArray(queryRangeList)
+                                                             .setTopk(searchParam.getTopK())
+                                                             .setNprobe(searchParam.getNProbe())
+                                                             .build();
+
+        io.milvus.client.grpc.SearchInFilesParam request = io.milvus.client.grpc.SearchInFilesParam
+                                                           .newBuilder()
+                                                           .addAllFileIdArray(searchInFilesParam.getFileIds())
+                                                           .setSearchParam(searchParamToSet)
+                                                           .build();
+
+        io.milvus.client.grpc.TopKQueryResultList response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(searchInFilesParam.getTimeout(), TimeUnit.SECONDS)
+                       .searchInFiles(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                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);
+            } else {
+                logSevere("Search in files {0} failed:\n{1}", searchInFilesParam.getFileIds(), response.toString());
+                return new SearchResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                       response.getStatus().getReason()),
+                                          new ArrayList<>());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("searchInFiles RPC failed:\n{0}", e.getStatus().toString());
+            return new SearchResponse(new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+        }
+    }
+
+    @Override
+    public DescribeTableResponse describeTable(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new DescribeTableResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                                                  .newBuilder()
+                                                  .setTableName(tableName)
+                                                  .build();
+        io.milvus.client.grpc.TableSchema response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .describeTable(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                TableSchema tableSchema = new TableSchema.Builder(response.getTableName(), response.getDimension())
+                        .withIndexFileSize(response.getIndexFileSize())
+                        .withMetricType(MetricType.valueOf(response.getMetricType()))
+                        .build();
+                logInfo("Describe Table `{0}` returned:\n{1}", tableName, tableSchema);
+                return new DescribeTableResponse(new Response(Response.Status.SUCCESS), tableSchema);
+            } else {
+                logSevere("Describe Table `{0}` failed:\n{1}", tableName, response.toString());
+                return new DescribeTableResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                              response.getStatus().getReason()),
+                                      null);
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("describeTable RPC failed:\n{0}", e.getStatus().toString());
+            return new DescribeTableResponse(new Response(Response.Status.RPC_ERROR, e.toString()), null);
+        }
+    }
+
+    @Override
+    public ShowTablesResponse showTables() {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new ShowTablesResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), new ArrayList<>());
+        }
+
+        io.milvus.client.grpc.Command request = io.milvus.client.grpc.Command
+                                                .newBuilder()
+                                                .setCmd("")
+                                                .build();
+        io.milvus.client.grpc.TableNameList response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(10, TimeUnit.SECONDS)
+                       .showTables(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                List<String> tableNames = response.getTableNamesList();
+                logInfo("Current tables: {0}", tableNames.toString());
+                return new ShowTablesResponse(new Response(Response.Status.SUCCESS), tableNames);
+            } else {
+                logSevere("Show tables failed:\n{0}", response.toString());
+                return new ShowTablesResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                           response.getStatus().getReason()),
+                                              new ArrayList<>());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("showTables RPC failed:\n{0}", e.getStatus().toString());
+            return new ShowTablesResponse(new Response(Response.Status.RPC_ERROR, e.toString()), new ArrayList<>());
+        }
+    }
+
+    @Override
+    public GetTableRowCountResponse getTableRowCount(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new GetTableRowCountResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), 0);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                                                  .newBuilder()
+                                                  .setTableName(tableName)
+                                                  .build();
+        io.milvus.client.grpc.TableRowCount response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .countTable(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                long tableRowCount = response.getTableRowCount();
+                logInfo("Table `{0}` has {1} rows", tableName, tableRowCount);
+                return new GetTableRowCountResponse(new Response(Response.Status.SUCCESS), tableRowCount);
+            } else {
+                logSevere("Get table `{0}` row count failed:\n{1}", tableName, response.toString());
+                return new GetTableRowCountResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                                 response.getStatus().getReason()),
+                                        0);
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("countTable RPC failed:\n{0}", e.getStatus().toString());
+            return new GetTableRowCountResponse(new Response(Response.Status.RPC_ERROR, e.toString()), 0);
+        }
+    }
+
+    @Override
+    public Response serverStatus() {
+        CommandParam commandParam = new CommandParam.Builder("OK").build();
+        return command(commandParam);
+    }
+
+    @Override
+    public Response serverVersion() {
+        CommandParam commandParam = new CommandParam.Builder("version").build();
+        return command(commandParam);
+    }
+
+    private Response command(@Nonnull CommandParam commandParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        String command = commandParam.getCommand();
+        io.milvus.client.grpc.Command request = io.milvus.client.grpc.Command
+                                                .newBuilder()
+                                                .setCmd(command)
+                                                .build();
+        io.milvus.client.grpc.StringReply response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(commandParam.getTimeout(), TimeUnit.SECONDS)
+                       .cmd(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Command `{0}`: {1}", command, response.getStringReply());
+                return new Response(Response.Status.SUCCESS, response.getStringReply());
+            } else {
+                logSevere("Command `{0}` failed:\n{1}", command, response.toString());
+                return new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                    response.getStatus().getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("Command RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    public Response deleteByRange(@Nonnull DeleteByRangeParam deleteByRangeParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        io.milvus.client.grpc.DeleteByRangeParam request = io.milvus.client.grpc.DeleteByRangeParam
+                                                           .newBuilder()
+                                                           .setRange(getRange(deleteByRangeParam.getDateRange()))
+                                                           .setTableName(deleteByRangeParam.getTableName())
+                                                           .build();
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(deleteByRangeParam.getTimeout(), TimeUnit.SECONDS)
+                       .deleteByRange(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Deleted vectors from table `{0}` in range {1} successfully!",
+                             deleteByRangeParam.getTableName(), deleteByRangeParam.getDateRange().toString());
+                return new Response(Response.Status.SUCCESS);
+            } else {
+                logSevere("Deleted vectors from table `{0}` in range {1} failed:\n{1}", response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("deleteByRange RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    @Override
+    public Response preloadTable(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                .newBuilder()
+                .setTableName(tableName)
+                .build();
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .preloadTable(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Preloaded table `{0}` successfully!", tableName);
+                return new Response(Response.Status.SUCCESS);
+            } else {
+                logSevere("Preload table `{0}` failed:\n{1}", tableName, response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("preloadTable RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    @Override
+    public DescribeIndexResponse describeIndex(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new DescribeIndexResponse(new Response(Response.Status.CLIENT_NOT_CONNECTED), null);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                                                  .newBuilder()
+                                                  .setTableName(tableName)
+                                                  .build();
+        io.milvus.client.grpc.IndexParam response;
+
+        try {
+            response = blockingStub
+                       .withDeadlineAfter(tableParam.getTimeout(), TimeUnit.SECONDS)
+                       .describeIndex(request);
+
+            if (response.getStatus().getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                Index index = new Index.Builder()
+                        .withIndexType(IndexType.valueOf(response.getIndex().getIndexType()))
+                        .withNList(response.getIndex().getNlist())
+                        .build();
+                IndexParam indexParam = new IndexParam.Builder(response.getTableName())
+                        .withIndex(index)
+                        .build();
+                logInfo("Describe index for table `{0}` returned:\n{1}", tableName, indexParam);
+                return new DescribeIndexResponse(new Response(Response.Status.SUCCESS), indexParam);
+            } else {
+                logSevere("Describe index for table `{0}` failed:\n{1}", tableName, response.toString());
+                return new DescribeIndexResponse(new Response(Response.Status.valueOf(response.getStatus().getErrorCodeValue()),
+                                                              response.getStatus().getReason()),
+                                       null);
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("describeIndex RPC failed:\n{0}", e.getStatus().toString());
+            return new DescribeIndexResponse(new Response(Response.Status.RPC_ERROR, e.toString()), null);
+        }
+    }
+
+    @Override
+    public Response dropIndex(@Nonnull TableParam tableParam) {
+
+        if (!connected()) {
+            logWarning("You are not connected to Milvus server");
+            return new Response(Response.Status.CLIENT_NOT_CONNECTED);
+        }
+
+        String tableName = tableParam.getTableName();
+        io.milvus.client.grpc.TableName request = io.milvus.client.grpc.TableName
+                .newBuilder()
+                .setTableName(tableName)
+                .build();
+        io.milvus.client.grpc.Status response;
+
+        try {
+            response = blockingStub.dropIndex(request);
+
+            if (response.getErrorCode() == io.milvus.client.grpc.ErrorCode.SUCCESS) {
+                logInfo("Dropped index for table `{0}` successfully!", tableName);
+                return new Response(Response.Status.SUCCESS);
+            } else {
+                logSevere("Drop index for table `{0}` failed:\n{1}", tableName, response.toString());
+                return new Response(Response.Status.valueOf(response.getErrorCodeValue()), response.getReason());
+            }
+        } catch (StatusRuntimeException e) {
+            logSevere("dropIndex RPC failed:\n{0}", e.getStatus().toString());
+            return new Response(Response.Status.RPC_ERROR, e.toString());
+        }
+    }
+
+    /////////////////////Util Functions/////////////////////
+    private List<io.milvus.client.grpc.RowRecord> getQueryRowRecordList(@Nonnull SearchParam searchParam) {
+        List<io.milvus.client.grpc.RowRecord> queryRowRecordList = new ArrayList<>();
+        for (List<Float> vectors : searchParam.getQueryVectors()) {
+            io.milvus.client.grpc.RowRecord rowRecord = io.milvus.client.grpc.RowRecord
+                    .newBuilder()
+                    .addAllVectorData(vectors)
+                    .build();
+            queryRowRecordList.add(rowRecord);
+        }
+        return queryRowRecordList;
+    }
+
+    private List<io.milvus.client.grpc.Range> getQueryRangeList(@Nonnull SearchParam searchParam) {
+        List<io.milvus.client.grpc.Range> queryRangeList = new ArrayList<>();
+        String datePattern = "yyyy-MM-dd";
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
+        for (DateRange queryRange : searchParam.getQueryRanges()) {
+            io.milvus.client.grpc.Range dateRange = io.milvus.client.grpc.Range
+                    .newBuilder()
+                    .setStartValue(simpleDateFormat.format(queryRange.getStartDate()))
+                    .setEndValue(simpleDateFormat.format(queryRange.getEndDate()))
+                    .build();
+            queryRangeList.add(dateRange);
+        }
+        return queryRangeList;
+    }
+
+    private io.milvus.client.grpc.Range getRange(@Nonnull DateRange dateRange) {
+        String datePattern = "yyyy-MM-dd";
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
+        return io.milvus.client.grpc.Range
+                .newBuilder()
+                .setStartValue(simpleDateFormat.format(dateRange.getStartDate()))
+                .setEndValue(simpleDateFormat.format(dateRange.getEndDate()))
+                .build();
+    }
+
+    private List<List<SearchResponse.QueryResult>> getQueryResultsList
+            (io.milvus.client.grpc.TopKQueryResultList searchResponse) {
+        //TODO: refactor
+        List<List<SearchResponse.QueryResult>> queryResultsList = new ArrayList<>();
+        Optional<List<io.milvus.client.grpc.TopKQueryResult>> topKQueryResultList
+                = Optional.ofNullable(searchResponse.getTopkQueryResultList());
+        if (topKQueryResultList.isPresent()) {
+            for (io.milvus.client.grpc.TopKQueryResult topKQueryResult : topKQueryResultList.get()) {
+                List<SearchResponse.QueryResult> responseQueryResults = new ArrayList<>();
+                List<io.milvus.client.grpc.QueryResult> queryResults = topKQueryResult.getQueryResultArraysList();
+                for (io.milvus.client.grpc.QueryResult queryResult : queryResults) {
+                    SearchResponse.QueryResult responseQueryResult
+                            = new SearchResponse.QueryResult(queryResult.getId(), queryResult.getDistance());
+                    responseQueryResults.add(responseQueryResult);
+                }
+                queryResultsList.add(responseQueryResults);
+            }
+        }
+        return queryResultsList;
+    }
+
+    /////////////////////Log Functions//////////////////////
+
+    private static final String ANSI_RESET = "\u001B[0m";
+    private static final String ANSI_BLACK = "\u001B[30m";
+    private static final String ANSI_RED = "\u001B[31m";
+    private static final String ANSI_GREEN = "\u001B[32m";
+    private static final String ANSI_YELLOW = "\u001B[33m";
+    private static final String ANSI_BLUE = "\u001B[34m";
+    private static final String ANSI_PURPLE = "\u001B[35m";
+    private static final String ANSI_CYAN = "\u001B[36m";
+    private static final String ANSI_WHITE = "\u001B[37m";
+
+    private static final String ANSI_BRIGHT_BLACK  = "\u001B[90m";
+    private static final String ANSI_BRIGHT_RED    = "\u001B[91m";
+    private static final String ANSI_BRIGHT_GREEN  = "\u001B[92m";
+    private static final String ANSI_BRIGHT_YELLOW = "\u001B[93m";
+    private static final String ANSI_BRIGHT_BLUE   = "\u001B[94m";
+    private static final String ANSI_BRIGHT_PURPLE = "\u001B[95m";
+    private static final String ANSI_BRIGHT_CYAN   = "\u001B[96m";
+    private static final String ANSI_BRIGHT_WHITE  = "\u001B[97m";
+
+    private void logInfo(String msg, Object... params) {
+        logger.log(Level.INFO, ANSI_YELLOW + msg + ANSI_RESET, params);
+    }
+
+    private void logWarning(String msg, Object... params) {
+        logger.log(Level.WARNING, ANSI_PURPLE + msg + ANSI_RESET, params);
+    }
+
+    private void logSevere(String msg, Object... params) {
+        logger.log(Level.SEVERE, ANSI_BRIGHT_PURPLE + msg + ANSI_RESET, params);
+    }
+}

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

@@ -0,0 +1,93 @@
+package io.milvus.client;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public class Response {
+
+    public enum Status {
+        //Server side error
+        SUCCESS(0),
+        UNEXPECTED_ERROR(1),
+        CONNECT_FAILED(2),
+        PERMISSION_DENIED(3),
+        TABLE_NOT_EXISTS(4),
+        ILLEGAL_ARGUMENT(5),
+        ILLEGAL_RANGE(6),
+        ILLEGAL_DIMENSION(7),
+        ILLEGAL_INDEX_TYPE(8),
+        ILLEGAL_TABLE_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);
+
+        private final int code;
+
+        Status(int code) {
+            this.code = code;
+        }
+
+        public int getCode() {
+            return code;
+        }
+
+        //TODO: Potential performance issue?
+        public static Status valueOf(int val) {
+            Optional<Status> search = Arrays.stream(values())
+                                            .filter(status -> status.code == val)
+                                            .findFirst();
+            return search.orElse(UNKNOWN);
+        }
+    };
+
+    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;
+    }
+
+    public boolean ok() {
+        return status == Status.SUCCESS;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("Response {code = %s, message = %s}", status.name(), this.message);
+    }
+}

+ 51 - 0
src/main/java/io/milvus/client/SearchInFilesParam.java

@@ -0,0 +1,51 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+public class SearchInFilesParam {
+    private final List<String> fileIds;
+    private final SearchParam searchParam;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final List<String> fileIds;
+        private final SearchParam searchParam;
+
+        // Optional parameters - initialized to default values
+        private long timeout = 10;
+
+        public Builder(List<String> fileIds, SearchParam searchParam) {
+            this.fileIds = fileIds;
+            this.searchParam = searchParam;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public SearchInFilesParam build() {
+            return new SearchInFilesParam(this);
+        }
+    }
+
+    private SearchInFilesParam(@Nonnull Builder builder) {
+        this.fileIds = builder.fileIds;
+        this.searchParam = builder.searchParam;
+        this.timeout = builder.timeout;
+    }
+
+    public List<String> getFileIds() {
+        return fileIds;
+    }
+
+    public SearchParam getSearchParam() {
+        return searchParam;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+}

+ 89 - 0
src/main/java/io/milvus/client/SearchParam.java

@@ -0,0 +1,89 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SearchParam {
+
+    private final String tableName;
+    private final List<List<Float>> queryVectors;
+    private final List<DateRange> queryRanges;
+    private final long topK;
+    private final long nProbe;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final String tableName;
+        private final List<List<Float>> queryVectors;
+
+        // Optional parameters - initialized to default values
+        private List<DateRange> queryRanges = new ArrayList<>();
+        private long topK = 1;
+        private long nProbe = 10;
+        private long timeout = 10;
+
+        public Builder(String tableName, List<List<Float>> queryVectors) {
+            this.tableName = tableName;
+            this.queryVectors = queryVectors;
+        }
+
+        public Builder withDateRanges(List<DateRange> queryRanges) {
+            this.queryRanges = queryRanges;
+            return this;
+        }
+
+        public Builder withTopK(long topK) {
+            this.topK = topK;
+            return this;
+        }
+
+        public Builder withNProbe(long nProbe) {
+            this.nProbe = nProbe;
+            return this;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public SearchParam build() {
+            return new SearchParam(this);
+        }
+    }
+
+    private SearchParam(@Nonnull Builder builder) {
+        this.tableName = builder.tableName;
+        this.queryVectors = builder.queryVectors;
+        this.queryRanges = builder.queryRanges;
+        this.nProbe = builder.nProbe;
+        this.topK = builder.topK;
+        this.timeout = builder.timeout;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public List<List<Float>> getQueryVectors() {
+        return queryVectors;
+    }
+
+    public List<DateRange> getQueryRanges() {
+        return queryRanges;
+    }
+
+    public long getTopK() {
+        return topK;
+    }
+
+    public long getNProbe() {
+        return nProbe;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+}

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

@@ -0,0 +1,50 @@
+package io.milvus.client;
+
+import java.util.List;
+
+public class SearchResponse {
+
+    private final Response response;
+
+    public static class QueryResult {
+        private final long vectorId;
+        private final double distance;
+
+        public QueryResult(long vectorId, double distance) {
+            this.vectorId = vectorId;
+            this.distance = distance;
+        }
+
+        public long getVectorId() {
+            return vectorId;
+        }
+
+        public double getDistance() {
+            return distance;
+        }
+    }
+
+    private final List<List<QueryResult>> queryResultsList;
+
+    public SearchResponse(Response response, List<List<QueryResult>> queryResultsList) {
+        this.response = response;
+        this.queryResultsList = queryResultsList;
+    }
+
+    public List<List<QueryResult>> getQueryResultsList() {
+        return queryResultsList;
+    }
+
+    //TODO: iterator
+
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("SearchResponse {%s, returned results for %d queries}",
+                              response.toString(), this.queryResultsList.size());
+    }
+}

+ 28 - 0
src/main/java/io/milvus/client/ShowTablesResponse.java

@@ -0,0 +1,28 @@
+package io.milvus.client;
+
+import java.util.List;
+
+public class ShowTablesResponse {
+    private final Response response;
+    private final List<String> tableNames;
+
+    public ShowTablesResponse(Response response, List<String> tableNames) {
+        this.response = response;
+        this.tableNames = tableNames;
+    }
+
+    public List<String> getTableNames() {
+        return tableNames;
+    }
+
+    public Response getResponse() {
+        return response;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("ShowTablesResponse {%s, table names = %s}",
+                              response,
+                              tableNames.toString());
+    }
+}

+ 50 - 0
src/main/java/io/milvus/client/TableParam.java

@@ -0,0 +1,50 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class TableParam {
+    private final String tableName;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final String tableName;
+
+        // Optional parameters - initialized to default values
+        private long timeout = 10;
+
+        public Builder(String tableName) {
+            this.tableName = tableName;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public TableParam build() {
+            return new TableParam(this);
+        }
+    }
+
+    private TableParam(@Nonnull Builder builder) {
+        this.tableName = builder.tableName;
+        this.timeout = builder.timeout;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public String toString() {
+        return "TableParam {" +
+                "tableName = '" + tableName + '\'' +
+                ", timeout = " + timeout +
+                '}';
+    }
+}

+ 68 - 0
src/main/java/io/milvus/client/TableSchema.java

@@ -0,0 +1,68 @@
+package io.milvus.client;
+
+import javax.annotation.*;
+
+// Builder Pattern
+public class TableSchema {
+    private final String tableName;
+    private final long dimension;
+    private final long indexFileSize;
+    private final MetricType metricType;
+
+    public static class Builder {
+        // Required parameters
+        private final String tableName;
+        private final long dimension;
+
+        // Optional parameters - initialized to default values
+        private long indexFileSize = 1024;
+        private MetricType metricType = MetricType.L2;
+
+        public Builder(String tableName, long dimension) {
+            this.tableName = tableName;
+            this.dimension = dimension;
+        }
+
+        public Builder withIndexFileSize(long indexFileSize) {
+            this.indexFileSize = indexFileSize;
+            return this;
+        }
+        public Builder withMetricType(MetricType metricType) {
+            this.metricType = metricType;
+            return this;
+        }
+
+        public TableSchema build() {
+            return new TableSchema(this);
+        }
+    }
+
+    private TableSchema(@Nonnull Builder builder) {
+        tableName = builder.tableName;
+        dimension = builder.dimension;
+        indexFileSize = builder.indexFileSize;
+        metricType = builder.metricType;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public long getDimension() {
+        return dimension;
+    }
+
+    public long getIndexFileSize() {
+        return indexFileSize;
+    }
+
+    public MetricType getMetricType() {
+        return metricType;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("TableSchema = {tableName = %s, dimension = %d, indexFileSize = %d, metricType = %s}",
+                             tableName, dimension, indexFileSize, metricType.name());
+    }
+}

+ 50 - 0
src/main/java/io/milvus/client/TableSchemaParam.java

@@ -0,0 +1,50 @@
+package io.milvus.client;
+
+import javax.annotation.Nonnull;
+
+public class TableSchemaParam {
+    private final TableSchema tableSchema;
+    private final long timeout;
+
+    public static class Builder {
+        // Required parameters
+        private final TableSchema tableSchema;
+
+        // Optional parameters - initialized to default values
+        private long timeout = 10;
+
+        public Builder(TableSchema tableSchema) {
+            this.tableSchema = tableSchema;
+        }
+
+        public Builder withTimeout(long timeout) {
+            this.timeout = timeout;
+            return this;
+        }
+
+        public TableSchemaParam build() {
+            return new TableSchemaParam(this);
+        }
+    }
+
+    private TableSchemaParam(@Nonnull Builder builder) {
+        this.tableSchema = builder.tableSchema;
+        this.timeout = builder.timeout;
+    }
+
+    public TableSchema getTableSchema() {
+        return tableSchema;
+    }
+
+    public long getTimeout() {
+        return timeout;
+    }
+
+    @Override
+    public String toString() {
+        return "CreateTableParam {" +
+                tableSchema +
+                ", timeout = " + timeout +
+                '}';
+    }
+}

+ 327 - 0
src/main/proto/milvus.proto

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

+ 40 - 0
src/main/proto/status.proto

@@ -0,0 +1,40 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.milvus.client.grpc";
+option java_outer_classname = "MilvusStatusProto";
+
+package milvus.grpc;
+
+enum ErrorCode {
+    SUCCESS = 0;
+    UNEXPECTED_ERROR = 1;
+    CONNECT_FAILED = 2;
+    PERMISSION_DENIED = 3;
+    TABLE_NOT_EXISTS = 4;
+    ILLEGAL_ARGUMENT = 5;
+    ILLEGAL_RANGE = 6;
+    ILLEGAL_DIMENSION = 7;
+    ILLEGAL_INDEX_TYPE = 8;
+    ILLEGAL_TABLE_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;
+}
+
+message Status {
+    ErrorCode error_code = 1;
+    string reason = 2;
+}

+ 257 - 0
src/test/java/io/milvus/client/MilvusGrpcClientTest.java

@@ -0,0 +1,257 @@
+package io.milvus.client;
+
+import io.grpc.inprocess.InProcessChannelBuilder;
+import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.testing.GrpcCleanupRule;
+import io.grpc.util.MutableHandlerRegistry;
+import org.junit.Rule;
+import org.apache.commons.text.RandomStringGenerator;
+
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class MilvusGrpcClientTest {
+
+    @Rule
+//    public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
+//
+//    private final MutableHandlerRegistry serviceRegistry = new MutableHandlerRegistry();
+
+    private MilvusGrpcClient client;
+
+    private RandomStringGenerator generator;
+
+    private String randomTableName;
+    private long size;
+    private long dimension;
+    private TableParam tableParam;
+
+    @org.junit.jupiter.api.BeforeEach
+    void setUp() throws Exception {
+//TODO: dummy service
+
+//        // Generate a unique in-process server name.
+//        String serverName = InProcessServerBuilder.generateName();
+//        // Use a mutable service registry for later registering the service impl for each test case.
+//        grpcCleanup.register(InProcessServerBuilder.forName(serverName)
+//                .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start());
+//        client = new MilvusGrpcClient(InProcessChannelBuilder.forName(serverName).directExecutor());
+
+        client = new MilvusGrpcClient();
+        ConnectParam connectParam = new ConnectParam.Builder()
+                                        .withHost("192.168.1.149")
+                                        .withPort("19530")
+                                        .build();
+        client.connect(connectParam);
+
+        generator = new RandomStringGenerator.Builder()
+                                            .withinRange('a', 'z').build();
+        randomTableName = generator.generate(10);
+        size = 100;
+        dimension = 128;
+        tableParam = new TableParam.Builder(randomTableName).build();
+        TableSchema tableSchema = new TableSchema.Builder(randomTableName, dimension)
+                                                    .withIndexFileSize(1024)
+                                                    .withMetricType(MetricType.L2)
+                                                    .build();
+        TableSchemaParam tableSchemaParam = new TableSchemaParam.Builder(tableSchema).build();
+
+        assertTrue(client.createTable(tableSchemaParam).ok());
+    }
+
+    @org.junit.jupiter.api.AfterEach
+    void tearDown() throws InterruptedException {
+        assertTrue(client.dropTable(tableParam).ok());
+        client.disconnect();
+    }
+
+    @org.junit.jupiter.api.Test
+    void connected() {
+        assertTrue(client.connected());
+    }
+
+    @org.junit.jupiter.api.Test
+    void createTable() {
+        String invalidTableName = "╯°□°)╯";
+        TableSchema invalidTableSchema = new TableSchema.Builder(invalidTableName, dimension).build();
+        TableSchemaParam invalidTableSchemaParam = new TableSchemaParam.Builder(invalidTableSchema).withTimeout(20).build();
+        Response createTableResponse = client.createTable(invalidTableSchemaParam);
+        assertFalse(createTableResponse.ok());
+        assertEquals(Response.Status.ILLEGAL_TABLE_NAME, createTableResponse.getStatus());
+    }
+
+    @org.junit.jupiter.api.Test
+    void hasTable() {
+        HasTableResponse hasTableResponse = client.hasTable(tableParam);
+        assertTrue(hasTableResponse.getResponse().ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void dropTable() {
+        String nonExistingTableName = generator.generate(10);
+        TableParam tableParam = new TableParam.Builder(nonExistingTableName).build();
+        Response dropTableResponse = client.dropTable(tableParam);
+        assertFalse(dropTableResponse.ok());
+        assertEquals(Response.Status.TABLE_NOT_EXISTS, dropTableResponse.getStatus());
+    }
+
+    @org.junit.jupiter.api.Test
+    void createIndex() {
+        Index index = new Index.Builder()
+                                .withIndexType(IndexType.IVF_SQ8)
+                                .withNList(16384)
+                                .build();
+        IndexParam indexParam = new IndexParam.Builder(randomTableName)
+                                                .withIndex(index)
+                                                .build();
+        Response createIndexResponse = client.createIndex(indexParam);
+        assertTrue(createIndexResponse.ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void insert() {
+        Random random = new Random();
+        List<List<Float>> vectors = new ArrayList<>();
+        for (int i = 0; i < size; ++i) {
+            List<Float> vector = new ArrayList<>();
+            for (int j = 0; j < dimension; ++j) {
+                vector.add(random.nextFloat());
+            }
+            vectors.add(vector);
+        }
+        InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
+        InsertResponse insertResponse = client.insert(insertParam);
+        assertTrue(insertResponse.getResponse().ok());
+        assertEquals(size, insertResponse.getVectorIds().size());
+    }
+
+    @org.junit.jupiter.api.Test
+    void search() throws InterruptedException {
+        Random random = new Random();
+        List<List<Float>> vectors = new ArrayList<>();
+        List<List<Float>> vectorsToSearch = new ArrayList<>();
+        int searchSize = 5;
+        for (int i = 0; i < size; ++i) {
+            List<Float> vector = new ArrayList<>();
+            for (int j = 0; j < dimension; ++j) {
+                vector.add(random.nextFloat());
+            }
+            vectors.add(vector);
+            if (i < searchSize) {
+                vectorsToSearch.add(vector);
+            }
+        }
+        InsertParam insertParam = new InsertParam.Builder(randomTableName, vectors).build();
+        InsertResponse insertResponse = client.insert(insertParam);
+        assertTrue(insertResponse.getResponse().ok());
+        assertEquals(size, insertResponse.getVectorIds().size());
+
+        TimeUnit.SECONDS.sleep(1);
+
+        List<DateRange> queryRanges = new ArrayList<>();
+        Calendar rightNow = Calendar.getInstance();
+        Date startDate = new Calendar.Builder()
+                                     .setDate(rightNow.get(Calendar.YEAR), rightNow.get(Calendar.MONTH) , rightNow.get(Calendar.DAY_OF_MONTH) - 1)
+                                     .build()
+                                     .getTime();
+        Date endDate = new Calendar.Builder()
+                                   .setDate(rightNow.get(Calendar.YEAR), rightNow.get(Calendar.MONTH), rightNow.get(Calendar.DAY_OF_MONTH) + 1)
+                                   .build()
+                                   .getTime();
+        queryRanges.add(new DateRange(startDate, endDate));
+        System.out.println(queryRanges);
+        SearchParam searchParam = new SearchParam
+                                        .Builder(randomTableName, vectorsToSearch)
+                                        .withTopK(1)
+                                        .withNProbe(20)
+                                        .withDateRanges(queryRanges)
+                                        .build();
+        SearchResponse searchResponse = client.search(searchParam);
+        assertTrue(searchResponse.getResponse().ok());
+        System.out.println(searchResponse);
+        assertEquals(searchSize, searchResponse.getQueryResultsList().size());
+    }
+
+//    @org.junit.jupiter.api.Test
+//    void searchInFiles() {
+//    }
+
+    @org.junit.jupiter.api.Test
+    void describeTable() {
+        DescribeTableResponse describeTableResponse = client.describeTable(tableParam);
+        assertTrue(describeTableResponse.getResponse().ok());
+        assertTrue(describeTableResponse.getTableSchema().isPresent());
+
+        String nonExistingTableName = generator.generate(10);
+        TableParam tableParam = new TableParam.Builder(nonExistingTableName).build();
+        describeTableResponse = client.describeTable(tableParam);
+        assertFalse(describeTableResponse.getResponse().ok());
+        assertFalse(describeTableResponse.getTableSchema().isPresent());
+    }
+
+    @org.junit.jupiter.api.Test
+    void showTables() {
+        ShowTablesResponse showTablesResponse = client.showTables();
+        assertTrue(showTablesResponse.getResponse().ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void serverStatus() {
+        Response serverStatusResponse = client.serverStatus();
+        assertTrue(serverStatusResponse.ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void serverVersion() {
+        Response serverVersionResponse = client.serverVersion();
+        assertTrue(serverVersionResponse.ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void getTableRowCount() throws InterruptedException {
+        insert();
+        TimeUnit.SECONDS.sleep(1);
+
+        GetTableRowCountResponse getTableRowCountResponse = client.getTableRowCount(tableParam);
+        assertTrue(getTableRowCountResponse.getResponse().ok());
+        assertEquals(size, getTableRowCountResponse.getTableRowCount());
+    }
+
+    @org.junit.jupiter.api.Test
+    void deleteByRange() {
+        Calendar rightNow = Calendar.getInstance();
+        Date startDate = new Calendar.Builder()
+                .setDate(rightNow.get(Calendar.YEAR), rightNow.get(Calendar.MONTH) , rightNow.get(Calendar.DAY_OF_MONTH) - 1)
+                .build()
+                .getTime();
+        Date endDate = new Calendar.Builder()
+                .setDate(rightNow.get(Calendar.YEAR), rightNow.get(Calendar.MONTH), rightNow.get(Calendar.DAY_OF_MONTH) + 1)
+                .build()
+                .getTime();
+        DeleteByRangeParam deleteByRangeParam = new DeleteByRangeParam.Builder(
+                new DateRange(startDate, endDate), randomTableName).build();
+        Response deleteByRangeResponse = client.deleteByRange(deleteByRangeParam);
+        assertTrue(deleteByRangeResponse.ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void preloadTable() {
+        Response preloadTableResponse = client.preloadTable(tableParam);
+        assertTrue(preloadTableResponse.ok());
+    }
+
+    @org.junit.jupiter.api.Test
+    void describeIndex() {
+        DescribeIndexResponse describeIndexResponse = client.describeIndex(tableParam);
+        assertTrue(describeIndexResponse.getResponse().ok());
+        assertTrue(describeIndexResponse.getIndexParam().isPresent());
+    }
+
+    @org.junit.jupiter.api.Test
+    void dropIndex() {
+        Response dropIndexResponse = client.dropIndex(tableParam);
+        assertTrue(dropIndexResponse.ok());
+    }
+}