浏览代码

Support Int8Vector(feature of v2.6) (#1408)

Signed-off-by: yhmo <yihua.mo@zilliz.com>
groot 1 月之前
父节点
当前提交
65abbab8a9

+ 180 - 0
examples/src/main/java/io/milvus/v2/Int8VectorExample.java

@@ -0,0 +1,180 @@
+package io.milvus.v2;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.milvus.v1.CommonUtils;
+import io.milvus.v2.client.ConnectConfig;
+import io.milvus.v2.client.MilvusClientV2;
+import io.milvus.v2.common.ConsistencyLevel;
+import io.milvus.v2.common.DataType;
+import io.milvus.v2.common.IndexParam;
+import io.milvus.v2.service.collection.request.AddFieldReq;
+import io.milvus.v2.service.collection.request.CreateCollectionReq;
+import io.milvus.v2.service.collection.request.DropCollectionReq;
+import io.milvus.v2.service.vector.request.InsertReq;
+import io.milvus.v2.service.vector.request.QueryReq;
+import io.milvus.v2.service.vector.request.SearchReq;
+import io.milvus.v2.service.vector.request.data.BinaryVec;
+import io.milvus.v2.service.vector.request.data.Int8Vec;
+import io.milvus.v2.service.vector.response.QueryResp;
+import io.milvus.v2.service.vector.response.SearchResp;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+public class Int8VectorExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_int8_vector_v2";
+    private static final String ID_FIELD = "id";
+    private static final String VECTOR_FIELD = "vector";
+
+    private static final Integer VECTOR_DIM = 128;
+
+    private static List<ByteBuffer> generateInt8Vectors(int count) {
+        Random RANDOM = new Random();
+        List<ByteBuffer> vectors = new ArrayList<>();
+        for (int i = 0; i < count; i++) {
+            ByteBuffer vector = ByteBuffer.allocate(VECTOR_DIM);
+            for (int k = 0; k < VECTOR_DIM; ++k) {
+                vector.put((byte) (RANDOM.nextInt(256) - 128));
+            }
+            vectors.add(vector);
+        }
+
+        return vectors;
+    }
+
+
+    public static void main(String[] args) {
+        ConnectConfig config = ConnectConfig.builder()
+                .uri("http://localhost:19530")
+                .build();
+        MilvusClientV2 client = new MilvusClientV2(config);
+
+        // Drop collection if exists
+        client.dropCollection(DropCollectionReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .build());
+
+        // Create collection
+        CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
+                .build();
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(ID_FIELD)
+                .dataType(DataType.Int64)
+                .isPrimaryKey(Boolean.TRUE)
+                .build());
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(VECTOR_FIELD)
+                .dataType(DataType.Int8Vector)
+                .dimension(VECTOR_DIM)
+                .build());
+
+        List<IndexParam> indexes = new ArrayList<>();
+        Map<String,Object> extraParams = new HashMap<>();
+        extraParams.put("M", 64);
+        extraParams.put("efConstruction", 200);
+        indexes.add(IndexParam.builder()
+                .fieldName(VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.HNSW)
+                .metricType(IndexParam.MetricType.L2)
+                .extraParams(extraParams)
+                .build());
+
+        CreateCollectionReq requestCreate = CreateCollectionReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .collectionSchema(collectionSchema)
+                .indexParams(indexes)
+                .consistencyLevel(ConsistencyLevel.BOUNDED)
+                .build();
+        client.createCollection(requestCreate);
+        System.out.println("Collection created");
+
+        // Insert entities by rows
+        int rowCount = 10000;
+        List<ByteBuffer> vectors = generateInt8Vectors(rowCount);
+        List<JsonObject> rows = new ArrayList<>();
+        Gson gson = new Gson();
+        for (long i = 0L; i < rowCount; ++i) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, i);
+            ByteBuffer vector = vectors.get((int)i);
+            row.add(VECTOR_FIELD, gson.toJsonTree(vector.array()));
+            rows.add(row);
+        }
+
+        client.insert(InsertReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .data(rows)
+                .build());
+
+        // Get row count, set ConsistencyLevel.STRONG to sync the data to query node so that data is visible
+        QueryResp countR = client.query(QueryReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .filter("")
+                .outputFields(Collections.singletonList("count(*)"))
+                .consistencyLevel(ConsistencyLevel.STRONG)
+                .build());
+        System.out.printf("%d rows persisted\n", (long)countR.getQueryResults().get(0).getEntity().get("count(*)"));
+
+        // Pick some vectors from the inserted vectors to search
+        // Ensure the returned top1 item's ID should be equal to target vector's ID
+        for (int i = 0; i < 10; i++) {
+            Random ran = new Random();
+            int k = ran.nextInt(rowCount);
+            ByteBuffer targetVector = vectors.get(k);
+            SearchResp searchResp = client.search(SearchReq.builder()
+                    .collectionName(COLLECTION_NAME)
+                    .data(Collections.singletonList(new Int8Vec(targetVector)))
+                    .annsField(VECTOR_FIELD)
+                    .outputFields(Collections.singletonList(VECTOR_FIELD))
+                    .topK(3)
+                    .build());
+
+            // The search() allows multiple target vectors to search in a batch.
+            // Here we only input one vector to search, get the result of No.0 vector to check
+            List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
+            List<SearchResp.SearchResult> results = searchResults.get(0);
+            System.out.printf("\nThe result of No.%d vector %s:\n", k, Arrays.toString(targetVector.array()));
+            for (SearchResp.SearchResult result : results) {
+                System.out.printf("ID: %d, Score: %f, Vector: ", (long)result.getId(), result.getScore());
+                ByteBuffer vector = (ByteBuffer) result.getEntity().get(VECTOR_FIELD);
+                System.out.print(Arrays.toString(vector.array()));
+                System.out.println();
+            }
+
+            SearchResp.SearchResult firstResult = results.get(0);
+            if ((long)firstResult.getId() != k) {
+                throw new RuntimeException(String.format("The top1 ID %d is not equal to target vector's ID %d",
+                        (long)firstResult.getId(), k));
+            }
+        }
+        System.out.println("Search result is correct");
+
+        // Retrieve some data
+        int n = 99;
+        QueryResp queryResp = client.query(QueryReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .filter(String.format("id == %d", n))
+                .outputFields(Collections.singletonList(VECTOR_FIELD))
+                .build());
+
+        List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
+        if (queryResults.isEmpty()) {
+            throw new RuntimeException("The query result is empty");
+        } else {
+            ByteBuffer vector = (ByteBuffer) queryResults.get(0).getEntity().get(VECTOR_FIELD);
+            if (vector.compareTo(vectors.get(n)) != 0) {
+                throw new RuntimeException("The query result is incorrect");
+            }
+        }
+        System.out.println("Query result is correct");
+
+
+        // Drop the collection if you don't need the collection anymore
+        client.dropCollection(DropCollectionReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .build());
+
+        client.close();
+    }
+}

+ 17 - 7
sdk-core/src/main/java/io/milvus/param/ParamUtils.java

@@ -101,6 +101,8 @@ public class ParamUtils {
     private static int calculateBinVectorDim(DataType dataType, int byteCount) {
         if (dataType == DataType.BinaryVector) {
             return byteCount*8; // for BinaryVector, each byte is 8 dimensions
+        } else if (dataType == DataType.Int8Vector) {
+            return byteCount; // for Int8Vector, each byte is one dimension
         } else {
             if (byteCount%2 != 0) {
                 String msg = "Incorrect byte count for %s type field, byte count is %d, cannot be evenly divided by 2";
@@ -358,7 +360,8 @@ public class ParamUtils {
             }
             case BinaryVector:
             case Float16Vector:
-            case BFloat16Vector: {
+            case BFloat16Vector:
+            case Int8Vector: {
                 if (!(value.isJsonArray())) {
                     throw new ParamException(String.format(errMsgs.get(dataType), fieldSchema.getName()));
                 }
@@ -726,7 +729,7 @@ public class ParamUtils {
                 ByteString bs = ByteString.copyFrom(array);
                 byteStrings.add(bs);
             } else if (vector instanceof ByteBuffer) {
-                // for fp16/bf16 vector, each vector is a ByteBuffer with little endian
+                // for fp16/bf16/int8 vector, each vector is a ByteBuffer with little endian
                 // for binary vector, each vector is a ByteBuffer no matter which endian
                 // the endian of each ByteBuffer is already specified by the caller
                 plType = PlaceholderType.BinaryVector;
@@ -1138,17 +1141,21 @@ public class ParamUtils {
         }
     }
 
-    public static boolean isVectorDataType(DataType dataType) {
+    public static boolean isDenseVectorDataType(DataType dataType) {
         Set<DataType> vectorDataType = new HashSet<DataType>() {{
             add(DataType.FloatVector);
             add(DataType.BinaryVector);
             add(DataType.Float16Vector);
             add(DataType.BFloat16Vector);
-            add(DataType.SparseFloatVector);
+            add(DataType.Int8Vector);
         }};
         return vectorDataType.contains(dataType);
     }
 
+    public static boolean isVectorDataType(DataType dataType) {
+        return isDenseVectorDataType(dataType) || dataType == DataType.SparseFloatVector;
+    }
+
     public static FieldData genFieldData(FieldType fieldType, List<?> objects) {
         return genFieldData(fieldType, objects, Boolean.FALSE);
     }
@@ -1203,10 +1210,11 @@ public class ParamUtils {
             return VectorField.newBuilder().setDim(dim).setFloatVector(floatArray).build();
         } else if (dataType == DataType.BinaryVector ||
                 dataType == DataType.Float16Vector ||
-                dataType == DataType.BFloat16Vector) {
+                dataType == DataType.BFloat16Vector ||
+                dataType == DataType.Int8Vector) {
             ByteBuffer totalBuf = null;
             int dim = 0;
-            // for fp16/bf16 vector, each vector is a ByteBuffer with little endian
+            // for fp16/bf16/int8 vector, each vector is a ByteBuffer with little endian
             // for binary vector, each vector is a ByteBuffer no matter which endian
             // no need to set totalBuf endian since it is treated as byte array
             for (Object object : objects) {
@@ -1226,8 +1234,10 @@ public class ParamUtils {
                 return VectorField.newBuilder().setDim(dim).setBinaryVector(byteString).build();
             } else if (dataType == DataType.Float16Vector) {
                 return VectorField.newBuilder().setDim(dim).setFloat16Vector(byteString).build();
-            } else {
+            } else if (dataType == DataType.BFloat16Vector) {
                 return VectorField.newBuilder().setDim(dim).setBfloat16Vector(byteString).build();
+            } else {
+                return VectorField.newBuilder().setDim(dim).setInt8Vector(byteString).build();
             }
         } else if  (dataType == DataType.SparseFloatVector) {
             SparseFloatArray sparseArray = genSparseFloatArray(objects);

+ 36 - 24
sdk-core/src/main/java/io/milvus/response/FieldDataWrapper.java

@@ -76,6 +76,9 @@ public class FieldDataWrapper {
     }
 
     // this method returns bytes size of each vector according to vector type
+    // for binary vector, each dimension is one bit, each byte is 8 dim
+    // for int8 vector, each dimension is ony byte, each byte is one dim
+    // for float16 vector, each dimension 2 bytes
     private int checkDim(DataType dt, ByteString data, int dim) {
         if (dt == DataType.BinaryVector) {
             if ((data.size()*8) % dim != 0) {
@@ -91,11 +94,35 @@ public class FieldDataWrapper {
                 throw new IllegalResponseException(msg);
             }
             return dim*2;
+        } else if (dt == DataType.Int8Vector) {
+            if (data.size() % dim != 0) {
+                String msg = String.format("Returned int8 vector data array size %d doesn't match dimension %d",
+                        data.size(), dim);
+                throw new IllegalResponseException(msg);
+            }
+            return dim;
         }
 
         return 0;
     }
 
+    private ByteString getVectorBytes(FieldData fieldData, DataType dt) {
+        ByteString data;
+        if (dt == DataType.BinaryVector) {
+            data = fieldData.getVectors().getBinaryVector();
+        } else if (dt == DataType.Float16Vector) {
+            data = fieldData.getVectors().getFloat16Vector();
+        } else if (dt == DataType.BFloat16Vector) {
+            data = fieldData.getVectors().getBfloat16Vector();
+        } else if (dt == DataType.Int8Vector) {
+            data = fieldData.getVectors().getInt8Vector();
+        } else {
+            String msg = String.format("Unsupported data type %s returned by FieldData", dt.name());
+            throw new IllegalResponseException(msg);
+        }
+        return data;
+    }
+
     /**
      * Gets the row count of a field.
      * * Throws {@link IllegalResponseException} if the field type is illegal.
@@ -116,20 +143,12 @@ public class FieldDataWrapper {
 
                 return data.size()/dim;
             }
-            case BinaryVector: {
-                // for binary vector, each dimension is one bit, each byte is 8 dim
-                int dim = getDim();
-                ByteString data = fieldData.getVectors().getBinaryVector();
-                int bytePerVec = checkDim(dt, data, dim);
-
-                return data.size()/bytePerVec;
-            }
+            case BinaryVector:
             case Float16Vector:
-            case BFloat16Vector: {
-                // for float16 vector, each dimension 2 bytes
+            case BFloat16Vector:
+            case Int8Vector: {
                 int dim = getDim();
-                ByteString data = (dt == DataType.Float16Vector) ?
-                        fieldData.getVectors().getFloat16Vector() : fieldData.getVectors().getBfloat16Vector();
+                ByteString data = getVectorBytes(fieldData, dt);
                 int bytePerVec = checkDim(dt, data, dim);
 
                 return data.size()/bytePerVec;
@@ -211,25 +230,18 @@ public class FieldDataWrapper {
             }
             case BinaryVector:
             case Float16Vector:
-            case BFloat16Vector: {
+            case BFloat16Vector:
+            case Int8Vector: {
                 int dim = getDim();
-                ByteString data = null;
-                if (dt == DataType.BinaryVector) {
-                    data = fieldData.getVectors().getBinaryVector();
-                } else if (dt == DataType.Float16Vector) {
-                    data = fieldData.getVectors().getFloat16Vector();
-                } else {
-                    data = fieldData.getVectors().getBfloat16Vector();
-                }
-
+                ByteString data = getVectorBytes(fieldData, dt);
                 int bytePerVec = checkDim(dt, data, dim);
                 int count = data.size()/bytePerVec;
                 List<ByteBuffer> packData = new ArrayList<>();
                 for (int i = 0; i < count; ++i) {
                     ByteBuffer bf = ByteBuffer.allocate(bytePerVec);
                     // binary vector doesn't care endian since each byte is independent
-                    // fp16/bf16 vector is sensetive to endian because each dim occupies 2 bytes,
-                    // milvus server stores fp16/bf16 vector as little endian
+                    // fp16/bf16/int8 vector is sensitive to endian because each dim occupies 1~2 bytes,
+                    // milvus server stores fp16/bf16/int8 vector as little endian
                     bf.order(ByteOrder.LITTLE_ENDIAN);
                     bf.put(data.substring(i * bytePerVec, (i + 1) * bytePerVec).toByteArray());
                     packData.add(bf);

+ 2 - 1
sdk-core/src/main/java/io/milvus/v2/common/DataType.java

@@ -44,7 +44,8 @@ public enum DataType {
     FloatVector(101),
     Float16Vector(102),
     BFloat16Vector(103),
-    SparseFloatVector(104);
+    SparseFloatVector(104),
+    Int8Vector(105);
 
     private final int code;
     DataType(int code) {

+ 1 - 1
sdk-core/src/main/java/io/milvus/v2/service/collection/request/AddFieldReq.java

@@ -44,7 +44,7 @@ public class AddFieldReq {
     @Builder.Default
     private Boolean autoID = Boolean.FALSE;
     private Integer dimension;
-    private io.milvus.v2.common.DataType elementType;
+    private DataType elementType;
     private Integer maxCapacity;
     @Builder.Default
     private Boolean isNullable = Boolean.FALSE; // only for scalar fields(not include Array fields)

+ 2 - 2
sdk-core/src/main/java/io/milvus/v2/service/collection/request/CreateCollectionReq.java

@@ -20,6 +20,7 @@
 package io.milvus.v2.service.collection.request;
 
 import io.milvus.common.clientenum.FunctionType;
+import io.milvus.param.ParamUtils;
 import io.milvus.v2.common.ConsistencyLevel;
 import io.milvus.v2.common.DataType;
 import io.milvus.v2.common.IndexParam;
@@ -166,8 +167,7 @@ public class CreateCollectionReq {
                 fieldSchema.setMaxCapacity(addFieldReq.getMaxCapacity());
             } else if (addFieldReq.getDataType().equals(DataType.VarChar)) {
                 fieldSchema.setMaxLength(addFieldReq.getMaxLength());
-            } else if (addFieldReq.getDataType().equals(DataType.FloatVector) || addFieldReq.getDataType().equals(DataType.BinaryVector) ||
-                    addFieldReq.getDataType().equals(DataType.Float16Vector) || addFieldReq.getDataType().equals(DataType.BFloat16Vector)) {
+            } else if (ParamUtils.isDenseVectorDataType(io.milvus.grpc.DataType.valueOf(addFieldReq.getDataType().name()))) {
                 if (addFieldReq.getDimension() == null) {
                     throw new MilvusClientException(ErrorCode.INVALID_PARAMS, "Dimension is required for vector field");
                 }

+ 45 - 0
sdk-core/src/main/java/io/milvus/v2/service/vector/request/data/Int8Vec.java

@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.milvus.v2.service.vector.request.data;
+
+import io.milvus.grpc.PlaceholderType;
+
+import java.nio.ByteBuffer;
+
+public class Int8Vec implements BaseVector {
+    private final ByteBuffer data;
+
+    public Int8Vec(ByteBuffer data) {
+        this.data = data;
+    }
+    public Int8Vec(byte[] data) {
+        this.data = ByteBuffer.wrap(data);
+    }
+
+    @Override
+    public PlaceholderType getPlaceholderType() {
+        return PlaceholderType.Int8Vector;
+    }
+
+    @Override
+    public Object getData() {
+        return this.data;
+    }
+}

+ 1 - 1
sdk-core/src/main/milvus-proto

@@ -1 +1 @@
-Subproject commit f412f2e2745e208c97ac8b4d22055b9b466378a0
+Subproject commit 4080770055adb2e2b37369a7ab1e3c713481d0ea

+ 2 - 0
sdk-core/src/test/java/io/milvus/TestUtils.java

@@ -11,6 +11,8 @@ public class TestUtils {
     private int dimension = 256;
     private static final Random RANDOM = new Random();
 
+    public static final String MilvusDockerImageID = "milvusdb/milvus:v2.5.11";
+
     public TestUtils(int dimension) {
         this.dimension = dimension;
     }

+ 1 - 1
sdk-core/src/test/java/io/milvus/client/MilvusClientDockerTest.java

@@ -75,7 +75,7 @@ class MilvusClientDockerTest {
     private static final TestUtils utils = new TestUtils(DIMENSION);
 
     @Container
-    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:v2.5.11");
+    private static final MilvusContainer milvus = new MilvusContainer(TestUtils.MilvusDockerImageID);
 
     @BeforeAll
     public static void setUp() {

+ 138 - 1
sdk-core/src/test/java/io/milvus/v2/client/MilvusClientV2DockerTest.java

@@ -81,7 +81,7 @@ class MilvusClientV2DockerTest {
     private static final TestUtils utils = new TestUtils(DIMENSION);
 
     @Container
-    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:v2.5.11");
+    private static final MilvusContainer milvus = new MilvusContainer(TestUtils.MilvusDockerImageID);
 
     @BeforeAll
     public static void setUp() {
@@ -796,6 +796,143 @@ class MilvusClientV2DockerTest {
         client.dropCollection(DropCollectionReq.builder().collectionName(randomCollectionName).build());
     }
 
+//    @Test
+//    void testInt8Vectors() {
+//        String randomCollectionName = generator.generate(10);
+//        String vectorFieldName = "int8_vector";
+//        int dimension = 8;
+//        CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
+//                .build();
+//        collectionSchema.addField(AddFieldReq.builder()
+//                .fieldName("id")
+//                .dataType(DataType.Int64)
+//                .isPrimaryKey(Boolean.TRUE)
+//                .build());
+//        collectionSchema.addField(AddFieldReq.builder()
+//                .fieldName(vectorFieldName)
+//                .dataType(DataType.Int8Vector)
+//                .dimension(dimension)
+//                .build());
+//
+//        client.dropCollection(DropCollectionReq.builder()
+//                .collectionName(randomCollectionName)
+//                .build());
+//        CreateCollectionReq requestCreate = CreateCollectionReq.builder()
+//                .collectionName(randomCollectionName)
+//                .collectionSchema(collectionSchema)
+//                .build();
+//        client.createCollection(requestCreate);
+//
+//        // insert rows
+//        Gson gson = new Gson();
+//        Random RANDOM = new Random();
+//        long count = 10;
+//        List<ByteBuffer> vectors = new ArrayList<>();
+//        List<JsonObject> data = new ArrayList<>();
+//        for (int i = 0; i < count; i++) {
+//            JsonObject row = new JsonObject();
+//            row.addProperty("id", i);
+//
+//            ByteBuffer vector = ByteBuffer.allocate(dimension);
+//            for (int k = 0; k < dimension; ++k) {
+//                vector.put((byte) (RANDOM.nextInt(256) - 128));
+//            }
+//            vectors.add(vector);
+//            row.add(vectorFieldName, gson.toJsonTree(vector.array()));
+//            data.add(row);
+//        }
+//
+//        InsertResp insertResp = client.insert(InsertReq.builder()
+//                .collectionName(randomCollectionName)
+//                .data(data)
+//                .build());
+//        Assertions.assertEquals(count, insertResp.getInsertCnt());
+//
+//        // flush
+//        client.flush(FlushReq.builder()
+//                .collectionNames(Collections.singletonList(randomCollectionName))
+//                .build());
+//
+//        // create index
+//        Map<String,Object> extraParams = new HashMap<>();
+//        extraParams.put("M", 64);
+//        extraParams.put("efConstruction", 200);
+//        IndexParam indexParam = IndexParam.builder()
+//                .fieldName(vectorFieldName)
+//                .indexType(IndexParam.IndexType.HNSW)
+//                .metricType(IndexParam.MetricType.COSINE)
+//                .extraParams(extraParams)
+//                .build();
+//        client.createIndex(CreateIndexReq.builder()
+//                .collectionName(randomCollectionName)
+//                .indexParams(Collections.singletonList(indexParam))
+//                .build());
+//
+//        client.loadCollection(LoadCollectionReq.builder()
+//                .collectionName(randomCollectionName)
+//                .build());
+//
+//        // describe collection
+//        DescribeCollectionResp descResp = client.describeCollection(DescribeCollectionReq.builder()
+//                .collectionName(randomCollectionName)
+//                .build());
+//        Assertions.assertEquals(randomCollectionName, descResp.getCollectionName());
+//
+//        List<String> fieldNames = descResp.getFieldNames();
+//        Assertions.assertEquals(collectionSchema.getFieldSchemaList().size(), fieldNames.size());
+//        CreateCollectionReq.CollectionSchema schema = descResp.getCollectionSchema();
+//        for (String name : fieldNames) {
+//            CreateCollectionReq.FieldSchema f1 = collectionSchema.getField(name);
+//            CreateCollectionReq.FieldSchema f2 = schema.getField(name);
+//            Assertions.assertNotNull(f1);
+//            Assertions.assertNotNull(f2);
+//            Assertions.assertEquals(f1.getName(), f2.getName());
+//            Assertions.assertEquals(f1.getDataType(), f2.getDataType());
+//            Assertions.assertEquals(f1.getDimension(), f2.getDimension());
+//        }
+//
+//        // search in collection
+//        int topK = 3;
+//        List<BaseVector> targetVectors = Arrays.asList(new Int8Vec(vectors.get(5)), new Int8Vec(vectors.get(0)));
+//        SearchResp searchResp = client.search(SearchReq.builder()
+//                .collectionName(randomCollectionName)
+//                .annsField(vectorFieldName)
+//                .data(targetVectors)
+//                .topK(topK)
+//                .outputFields(Collections.singletonList("*"))
+//                .consistencyLevel(ConsistencyLevel.STRONG)
+//                .build());
+//        List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
+//        Assertions.assertEquals(targetVectors.size(), searchResults.size());
+//
+//        for (List<SearchResp.SearchResult> results : searchResults) {
+//            Assertions.assertEquals(topK, results.size());
+//            for (int i = 0; i < results.size(); i++) {
+//                SearchResp.SearchResult result = results.get(i);
+//                Map<String, Object> entity = result.getEntity();
+//                long id = (long) entity.get("id");
+//                ByteBuffer originVec = vectors.get((int) id);
+//                ByteBuffer getVec = (ByteBuffer) entity.get(vectorFieldName);
+//                Assertions.assertEquals(originVec, getVec);
+//            }
+//        }
+//
+//        // query
+//        QueryResp queryResp = client.query(QueryReq.builder()
+//                .collectionName(randomCollectionName)
+//                .filter("id == 5")
+//                .build());
+//        List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
+//        Assertions.assertEquals(1, queryResults.size());
+//        {
+//            QueryResp.QueryResult result = queryResults.get(0);
+//            Map<String, Object> entity = result.getEntity();
+//            ByteBuffer originVec = vectors.get(5);
+//            ByteBuffer getVec = (ByteBuffer)entity.get(vectorFieldName);
+//            Assertions.assertEquals(originVec, getVec);
+//        }
+//    }
+
     @Test
     void testHybridSearch() {
         String randomCollectionName = generator.generate(10);