浏览代码

Add examples for V2 (#1041)

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

+ 2 - 2
docker-compose.yml

@@ -32,7 +32,7 @@ services:
 
   standalone:
     container_name: milvus-javasdk-test-standalone
-    image: milvusdb/milvus:v2.4.6
+    image: milvusdb/milvus:v2.4.7
     command: ["milvus", "run", "standalone"]
     environment:
       ETCD_ENDPOINTS: etcd:2379
@@ -77,7 +77,7 @@ services:
 
   standaloneslave:
     container_name: milvus-javasdk-test-slave-standalone
-    image: milvusdb/milvus:v2.4.6
+    image: milvusdb/milvus:v2.4.7
     command: ["milvus", "run", "standalone"]
     environment:
       ETCD_ENDPOINTS: etcdslave:2379

+ 149 - 0
examples/main/java/io/milvus/v1/JsonFieldExample.java

@@ -0,0 +1,149 @@
+package io.milvus.v1;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.milvus.client.MilvusClient;
+import io.milvus.client.MilvusServiceClient;
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
+import io.milvus.grpc.DataType;
+import io.milvus.grpc.QueryResults;
+import io.milvus.param.*;
+import io.milvus.param.collection.*;
+import io.milvus.param.dml.InsertParam;
+import io.milvus.param.dml.QueryParam;
+import io.milvus.param.index.CreateIndexParam;
+import io.milvus.response.QueryResultsWrapper;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class JsonFieldExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_json_v1";
+    private static final String ID_FIELD = "id";
+    private static final String VECTOR_FIELD = "vector";
+    private static final String JSON_FIELD = "metadata";
+    private static final Integer VECTOR_DIM = 128;
+
+    private static void queryWithExpr(MilvusClient client, String expr) {
+        R<QueryResults> queryRet = client.query(QueryParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withExpr(expr)
+                .addOutField(ID_FIELD)
+                .addOutField(JSON_FIELD)
+                .build());
+        QueryResultsWrapper queryWrapper = new QueryResultsWrapper(queryRet.getData());
+        System.out.println("\nQuery with expression: " + expr);
+        List<QueryResultsWrapper.RowRecord> records = queryWrapper.getRowRecords();
+        for (QueryResultsWrapper.RowRecord record : records) {
+            System.out.println(record);
+        }
+        System.out.println("=============================================================");
+    }
+
+    public static void main(String[] args) {
+        // Connect to Milvus server. Replace the "localhost" and port with your Milvus server address.
+        MilvusServiceClient client = new MilvusServiceClient(ConnectParam.newBuilder()
+                .withHost("localhost")
+                .withPort(19530)
+                .build());
+
+        // Define fields
+        List<FieldType> fieldsSchema = Arrays.asList(
+                FieldType.newBuilder()
+                        .withName(ID_FIELD)
+                        .withDataType(DataType.Int64)
+                        .withPrimaryKey(true)
+                        .withAutoID(false)
+                        .build(),
+                FieldType.newBuilder()
+                        .withName(VECTOR_FIELD)
+                        .withDataType(DataType.FloatVector)
+                        .withDimension(VECTOR_DIM)
+                        .build(),
+                FieldType.newBuilder()
+                        .withName(JSON_FIELD)
+                        .withDataType(DataType.JSON)
+                        .build()
+        );
+
+        CollectionSchemaParam collectionSchemaParam = CollectionSchemaParam.newBuilder()
+                .withEnableDynamicField(false)
+                .withFieldTypes(fieldsSchema)
+                .build();
+
+        // Drop the collection if exists
+        client.dropCollection(DropCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .build());
+
+        // Create the collection with 3 fields
+        R<RpcStatus> ret = client.createCollection(CreateCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withSchema(collectionSchemaParam)
+                .build());
+        if (ret.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to create collection! Error: " + ret.getMessage());
+        }
+
+        // Specify an index type on the vector field.
+        ret = client.createIndex(CreateIndexParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withFieldName(VECTOR_FIELD)
+                .withIndexType(IndexType.FLAT)
+                .withMetricType(MetricType.L2)
+                .build());
+        if (ret.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException("Failed to create index on vector field! Error: " + ret.getMessage());
+        }
+
+        // Call loadCollection() to enable automatically loading data into memory for searching
+        client.loadCollection(LoadCollectionParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .build());
+
+        System.out.println("Collection created");
+
+        // insert rows
+        Gson gson = new Gson();
+        for (int i = 0; i < 100; i++) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, i);
+            row.add(VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloatVector(VECTOR_DIM)));
+
+            JsonObject metadata = new JsonObject();
+            metadata.addProperty("path", String.format("\\root/abc/path%d", i));
+            metadata.addProperty("size", i);
+            if (i%7 == 0) {
+                metadata.addProperty("special", true);
+            }
+            metadata.add("flags", gson.toJsonTree(Arrays.asList(i, i + 1, i + 2)));
+            row.add(JSON_FIELD, metadata);
+            System.out.println(metadata);
+
+            client.insert(InsertParam.newBuilder()
+                    .withCollectionName(COLLECTION_NAME)
+                    .withRows(Collections.singletonList(row))
+                    .build());
+        }
+
+        // get row count
+        R<QueryResults> queryRet = client.query(QueryParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withExpr("")
+                .addOutField("count(*)")
+                .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
+                .build());
+        QueryResultsWrapper queryWrapper = new QueryResultsWrapper(queryRet.getData());
+        long rowCount = (long)queryWrapper.getFieldWrapper("count(*)").getFieldData().get(0);
+        System.out.printf("%d rows persisted\n", rowCount);
+
+        // query by filtering JSON
+        queryWithExpr(client, "exists metadata[\"special\"]");
+        queryWithExpr(client, "metadata[\"size\"] < 5");
+        queryWithExpr(client, "metadata[\"size\"] in [4, 5, 6]");
+        queryWithExpr(client, "JSON_CONTAINS(metadata[\"flags\"], 9)");
+        queryWithExpr(client, "JSON_CONTAINS_ANY(metadata[\"flags\"], [8, 9, 10])");
+        queryWithExpr(client, "JSON_CONTAINS_ALL(metadata[\"flags\"], [8, 9, 10])");
+    }
+}

+ 12 - 7
examples/main/java/io/milvus/v1/SimpleExample.java

@@ -21,12 +21,15 @@ package io.milvus.v1;
 import com.google.gson.Gson;
 import com.google.gson.JsonObject;
 import io.milvus.client.MilvusServiceClient;
+import io.milvus.common.clientenum.ConsistencyLevelEnum;
 import io.milvus.grpc.*;
 import io.milvus.param.*;
 import io.milvus.param.collection.*;
 import io.milvus.param.dml.*;
 import io.milvus.param.index.*;
 import io.milvus.response.*;
+import io.milvus.v2.service.vector.response.QueryResp;
+
 import java.util.*;
 
 
@@ -124,14 +127,16 @@ public class SimpleExample {
             throw new RuntimeException("Failed to insert! Error: " + insertRet.getMessage());
         }
 
-        // Call flush to make sure the inserted records are consumed by Milvus server, so that the records
-        // be searchable immediately. Just a special action in this example.
-        // In practice, you don't need to call flush() frequently.
-        milvusClient.flush(FlushParam.newBuilder()
-                .addCollectionName(COLLECTION_NAME)
+        // get row count
+        R<QueryResults> queryRet = milvusClient.query(QueryParam.newBuilder()
+                .withCollectionName(COLLECTION_NAME)
+                .withExpr("")
+                .addOutField("count(*)")
+                .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
                 .build());
-
-        System.out.println("10 entities inserted");
+        QueryResultsWrapper wrapper = new QueryResultsWrapper(queryRet.getData());
+        long rowCount = (long)wrapper.getFieldWrapper("count(*)").getFieldData().get(0);
+        System.out.printf("%d rows persisted\n", rowCount);
 
         // Construct a vector to search top5 similar records, return the book title for us.
         // This vector is equal to the No.3 record, we suppose the No.3 record is the most similar.

+ 13 - 13
examples/main/java/io/milvus/v1/TLSExample.java

@@ -19,7 +19,7 @@
 package io.milvus.v1;
 
 import io.milvus.client.MilvusServiceClient;
-import io.milvus.grpc.CheckHealthResponse;
+import io.milvus.grpc.GetVersionResponse;
 import io.milvus.param.ConnectParam;
 import io.milvus.param.R;
 
@@ -52,7 +52,7 @@ import java.net.URL;
 public class TLSExample {
 
     private static void oneWayAuth() {
-        ClassLoader classLoader = BulkWriterExample.class.getClassLoader();
+        ClassLoader classLoader = TLSExample.class.getClassLoader();
         URL resourceUrl = classLoader.getResource("tls");
         String path = new File(resourceUrl.getFile()).getAbsolutePath();
         ConnectParam connectParam = ConnectParam.newBuilder()
@@ -61,18 +61,18 @@ public class TLSExample {
                 .withServerName("localhost")
                 .withServerPemPath(path + "/server.pem")
                 .build();
-        MilvusServiceClient milvusClient = new MilvusServiceClient(connectParam);
+        MilvusServiceClient client = new MilvusServiceClient(connectParam);
 
-        R<CheckHealthResponse> health = milvusClient.checkHealth();
-        if (health.getStatus() != R.Status.Success.getCode()) {
-            throw new RuntimeException(health.getMessage());
+        R<GetVersionResponse> version = client.getVersion();
+        if (version.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException(version.getMessage());
         } else {
-            System.out.println(health);
+            System.out.println("Server version: " + version.getData().getVersion());
         }
     }
 
     private static void twoWayAuth() {
-        ClassLoader classLoader = BulkWriterExample.class.getClassLoader();
+        ClassLoader classLoader = TLSExample.class.getClassLoader();
         URL resourceUrl = classLoader.getResource("tls");
         String path = new File(resourceUrl.getFile()).getAbsolutePath();
         ConnectParam connectParam = ConnectParam.newBuilder()
@@ -83,13 +83,13 @@ public class TLSExample {
                 .withClientKeyPath(path + "/client.key")
                 .withClientPemPath(path + "/client.pem")
                 .build();
-        MilvusServiceClient milvusClient = new MilvusServiceClient(connectParam);
+        MilvusServiceClient client = new MilvusServiceClient(connectParam);
 
-        R<CheckHealthResponse> health = milvusClient.checkHealth();
-        if (health.getStatus() != R.Status.Success.getCode()) {
-            throw new RuntimeException(health.getMessage());
+        R<GetVersionResponse> version = client.getVersion();
+        if (version.getStatus() != R.Status.Success.getCode()) {
+            throw new RuntimeException(version.getMessage());
         } else {
-            System.out.println(health);
+            System.out.println("Server version: " + version.getData().getVersion());
         }
     }
 

+ 172 - 0
examples/main/java/io/milvus/v2/BinaryVectorExample.java

@@ -0,0 +1,172 @@
+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.response.QueryResp;
+import io.milvus.v2.service.vector.response.SearchResp;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+public class BinaryVectorExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_binary_vector_v2";
+    private static final String ID_FIELD = "id";
+    private static final String VECTOR_FIELD = "vector";
+
+    private static final Integer VECTOR_DIM = 512;
+
+
+    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.BinaryVector)
+                .dimension(VECTOR_DIM)
+                .build());
+
+        List<IndexParam> indexes = new ArrayList<>();
+        Map<String,Object> extraParams = new HashMap<>();
+        extraParams.put("nlist",64);
+        indexes.add(IndexParam.builder()
+                .fieldName(VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.BIN_IVF_FLAT)
+                .metricType(IndexParam.MetricType.HAMMING)
+                .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<JsonObject> rows = new ArrayList<>();
+        Gson gson = new Gson();
+        List<ByteBuffer> vectors = new ArrayList<>();
+        for (long i = 0L; i < rowCount; ++i) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, i);
+            ByteBuffer vector = CommonUtils.generateBinaryVector(VECTOR_DIM);
+            vectors.add(vector);
+            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);
+            Map<String,Object> params = new HashMap<>();
+            params.put("nprobe",16);
+            SearchResp searchResp = client.search(SearchReq.builder()
+                    .collectionName(COLLECTION_NAME)
+                    .data(Collections.singletonList(new BinaryVec(targetVector)))
+                    .annsField(VECTOR_FIELD)
+                    .outputFields(Collections.singletonList(VECTOR_FIELD))
+                    .searchParams(params)
+                    .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("The result of No.%d target vector:\n", i);
+            for (SearchResp.SearchResult result : results) {
+                System.out.println(result.getEntity());
+                System.out.printf("ID: %d, Score: %f, Vector: ", result.getId(), result.getScore());
+                ByteBuffer vector = (ByteBuffer) result.getEntity().get(VECTOR_FIELD);
+                vector.rewind();
+                while (vector.hasRemaining()) {
+                    System.out.print(Integer.toBinaryString(vector.get()));
+                }
+                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",
+                        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();
+    }
+}

+ 4 - 1
examples/main/java/io/milvus/v2/ClientPoolExample.java

@@ -24,7 +24,7 @@ import java.util.List;
 import java.util.Random;
 
 public class ClientPoolExample {
-    public static String CollectionName = "java_sdk_example_pool_v1";
+    public static String CollectionName = "java_sdk_example_pool_v2";
     public static String VectorFieldName = "vector";
     public static int DIM = 128;
 
@@ -159,8 +159,11 @@ public class ClientPoolExample {
                 threadCount*repeatRequests, threadCount*repeatRequests, (end-start)*0.001);
         System.out.printf("Total %d idle clients and %d active clients%n",
                 pool.getTotalIdleClientNumber(), pool.getTotalActiveClientNumber());
+
         pool.clear(); // clear idle clients
         System.out.printf("After clear, total %d idle clients and %d active clients%n",
                 pool.getTotalIdleClientNumber(), pool.getTotalActiveClientNumber());
+
+        pool.close();
     }
 }

+ 15 - 13
examples/main/java/io/milvus/v2/Float16VectorExample.java

@@ -33,24 +33,24 @@ public class Float16VectorExample {
     private static final String BF16_VECTOR_FIELD = "bf16_vector";
     private static final Integer VECTOR_DIM = 128;
 
-    private static final MilvusClientV2 milvusClient;
+    private static final MilvusClientV2 client;
     static {
-        milvusClient = new MilvusClientV2(ConnectConfig.builder()
+        client = new MilvusClientV2(ConnectConfig.builder()
                 .uri("http://localhost:19530")
                 .build());
     }
 
     private static void createCollection() {
 
-        // drop the collection if you don't need the collection anymore
-        Boolean has = milvusClient.hasCollection(HasCollectionReq.builder()
+        // Drop the collection if you don't need the collection anymore
+        Boolean has = client.hasCollection(HasCollectionReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .build());
         if (has) {
             dropCollection();
         }
 
-        // build a collection with two vector fields
+        // Build a collection with two vector fields
         CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
                 .build();
         collectionSchema.addField(AddFieldReq.builder()
@@ -90,7 +90,7 @@ public class Float16VectorExample {
                 .indexParams(indexes)
                 .consistencyLevel(ConsistencyLevel.BOUNDED)
                 .build();
-        milvusClient.createCollection(requestCreate);
+        client.createCollection(requestCreate);
     }
 
     private static void prepareData(int count) {
@@ -106,7 +106,7 @@ public class Float16VectorExample {
             rows.add(row);
         }
 
-        InsertResp insertResp = milvusClient.insert(InsertReq.builder()
+        InsertResp insertResp = client.insert(InsertReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .data(rows)
                 .build());
@@ -115,7 +115,7 @@ public class Float16VectorExample {
 
     private static void searchVectors(List<Long> taargetIDs, List<BaseVector> targetVectors, String vectorFieldName) {
         int topK = 5;
-        SearchResp searchResp = milvusClient.search(SearchReq.builder()
+        SearchResp searchResp = client.search(SearchReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .data(targetVectors)
                 .annsField(vectorFieldName)
@@ -151,9 +151,9 @@ public class Float16VectorExample {
     }
 
     private static void search() {
-        // retrieve some rows for search
+        // Retrieve some rows for search
         List<Long> targetIDs = Arrays.asList(999L, 2024L);
-        QueryResp queryResp = milvusClient.query(QueryReq.builder()
+        QueryResp queryResp = client.query(QueryReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .filter(ID_FIELD + " in " + targetIDs)
                 .outputFields(Arrays.asList(FP16_VECTOR_FIELD, BF16_VECTOR_FIELD))
@@ -175,15 +175,15 @@ public class Float16VectorExample {
             targetBF16Vectors.add(new BFloat16Vec(bf16VectorBuf));
         }
 
-        // search float16 vector
+        // Search float16 vector
         searchVectors(targetIDs, targetFP16Vectors, FP16_VECTOR_FIELD);
 
-        // search bfloat16 vector
+        // Search bfloat16 vector
         searchVectors(targetIDs, targetBF16Vectors, BF16_VECTOR_FIELD);
     }
 
     private static void dropCollection() {
-        milvusClient.dropCollection(DropCollectionReq.builder()
+        client.dropCollection(DropCollectionReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .build());
         System.out.println("Collection dropped");
@@ -195,5 +195,7 @@ public class Float16VectorExample {
         prepareData(10000);
         search();
         dropCollection();
+
+        client.close();
     }
 }

+ 228 - 0
examples/main/java/io/milvus/v2/HybridSearchExample.java

@@ -0,0 +1,228 @@
+package io.milvus.v2;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import io.milvus.v2.common.DataType;
+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.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.AnnSearchReq;
+import io.milvus.v2.service.vector.request.HybridSearchReq;
+import io.milvus.v2.service.vector.request.InsertReq;
+import io.milvus.v2.service.vector.request.QueryReq;
+import io.milvus.v2.service.vector.request.data.BaseVector;
+import io.milvus.v2.service.vector.request.data.BinaryVec;
+import io.milvus.v2.service.vector.request.data.FloatVec;
+import io.milvus.v2.service.vector.request.data.SparseFloatVec;
+import io.milvus.v2.service.vector.request.ranker.RRFRanker;
+import io.milvus.v2.service.vector.response.QueryResp;
+import io.milvus.v2.service.vector.response.SearchResp;
+
+import java.util.*;
+
+public class HybridSearchExample {
+    private static final MilvusClientV2 client;
+
+    static {
+        ConnectConfig config = ConnectConfig.builder()
+                .uri("http://localhost:19530")
+                .build();
+        client = new MilvusClientV2(config);
+    }
+
+    private static final String COLLECTION_NAME = "java_sdk_example_hybrid_search_v2";
+    private static final String ID_FIELD = "ID";
+
+    private static final String FLOAT_VECTOR_FIELD = "float_vector";
+    private static final Integer FLOAT_VECTOR_DIM = 128;
+    private static final IndexParam.MetricType FLOAT_VECTOR_METRIC = IndexParam.MetricType.COSINE;
+
+    private static final String BINARY_VECTOR_FIELD = "binary_vector";
+    private static final Integer BINARY_VECTOR_DIM = 256;
+    private static final IndexParam.MetricType BINARY_VECTOR_METRIC = IndexParam.MetricType.JACCARD;
+
+    private static final String FLOAT16_VECTOR_FIELD = "float16_vector";
+    private static final Integer FLOAT16_VECTOR_DIM = 256;
+    private static final IndexParam.MetricType FLOAT16_VECTOR_METRIC = IndexParam.MetricType.L2;
+
+    private static final String SPARSE_VECTOR_FIELD = "sparse_vector";
+    private static final IndexParam.MetricType SPARSE_VECTOR_METRIC = IndexParam.MetricType.IP;
+
+    private void createCollection() {
+        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(FLOAT_VECTOR_FIELD)
+                .dataType(DataType.FloatVector)
+                .dimension(FLOAT_VECTOR_DIM)
+                .build());
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(BINARY_VECTOR_FIELD)
+                .dataType(DataType.BinaryVector)
+                .dimension(BINARY_VECTOR_DIM)
+                .build());
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(FLOAT16_VECTOR_FIELD)
+                .dataType(DataType.Float16Vector)
+                .dimension(FLOAT16_VECTOR_DIM)
+                .build());
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(SPARSE_VECTOR_FIELD)
+                .dataType(DataType.SparseFloatVector)
+                .build());
+
+        List<IndexParam> indexes = new ArrayList<>();
+        Map<String,Object> fvParams = new HashMap<>();
+        fvParams.put("nlist",128);
+        fvParams.put("m",16);
+        fvParams.put("nbits",8);
+        indexes.add(IndexParam.builder()
+                .fieldName(FLOAT_VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.IVF_PQ)
+                .extraParams(fvParams)
+                .metricType(FLOAT_VECTOR_METRIC)
+                .build());
+        indexes.add(IndexParam.builder()
+                .fieldName(BINARY_VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.BIN_FLAT)
+                .metricType(BINARY_VECTOR_METRIC)
+                .build());
+        Map<String,Object> fv16Params = new HashMap<>();
+        fv16Params.clear();
+        fv16Params.put("M",16);
+        fv16Params.put("efConstruction",64);
+        indexes.add(IndexParam.builder()
+                .fieldName(FLOAT16_VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.HNSW)
+                .extraParams(fv16Params)
+                .metricType(FLOAT16_VECTOR_METRIC)
+                .build());
+        indexes.add(IndexParam.builder()
+                .fieldName(SPARSE_VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
+                .metricType(SPARSE_VECTOR_METRIC)
+                .build());
+
+        CreateCollectionReq requestCreate = CreateCollectionReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .collectionSchema(collectionSchema)
+                .indexParams(indexes)
+                .consistencyLevel(ConsistencyLevel.BOUNDED)
+                .build();
+        client.createCollection(requestCreate);
+        System.out.println("Collection created");
+    }
+
+    private void insertData() {
+        long idCount = 0;
+        int rowCount = 10000;
+        // Insert entities by rows
+        List<JsonObject> rows = new ArrayList<>();
+        Gson gson = new Gson();
+        for (long i = 1L; i <= rowCount; ++i) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, idCount++);
+            row.add(FLOAT_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloatVector(FLOAT_VECTOR_DIM)));
+            row.add(BINARY_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateBinaryVector(BINARY_VECTOR_DIM).array()));
+            row.add(FLOAT16_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloat16Vector(FLOAT16_VECTOR_DIM, false).array()));
+            row.add(SPARSE_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateSparseVector()));
+            rows.add(row);
+        }
+
+        client.insert(InsertReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .data(rows)
+                .build());
+
+        System.out.printf("%d entities inserted by rows\n", rowCount);
+    }
+
+    private void hybridSearch() {
+        // 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(*)"));
+
+        // Search on multiple vector fields
+        int NQ = 2;
+        List<BaseVector> floatVectors = new ArrayList<>();
+        List<BaseVector> binaryVectors = new ArrayList<>();
+        List<BaseVector> sparseVectors = new ArrayList<>();
+        for (int i = 0; i < NQ; i++) {
+            floatVectors.add(new FloatVec(CommonUtils.generateFloatVector(FLOAT_VECTOR_DIM)));
+            binaryVectors.add(new BinaryVec(CommonUtils.generateBinaryVector(BINARY_VECTOR_DIM)));
+            sparseVectors.add(new SparseFloatVec(CommonUtils.generateSparseVector()));
+        }
+
+        List<AnnSearchReq> searchRequests = new ArrayList<>();
+        searchRequests.add(AnnSearchReq.builder()
+                .vectorFieldName("float_vector")
+                .vectors(floatVectors)
+                .params("{\"nprobe\": 10}")
+                .topK(10)
+                .build());
+        searchRequests.add(AnnSearchReq.builder()
+                .vectorFieldName("binary_vector")
+                .vectors(binaryVectors)
+                .topK(50)
+                .build());
+        searchRequests.add(AnnSearchReq.builder()
+                .vectorFieldName("sparse_vector")
+                .vectors(sparseVectors)
+                .topK(100)
+                .build());
+
+        HybridSearchReq hybridSearchReq = HybridSearchReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .searchRequests(searchRequests)
+                .ranker(new RRFRanker(2))
+                .topK(5)
+                .consistencyLevel(ConsistencyLevel.BOUNDED)
+                .build();
+        SearchResp searchResp = client.hybridSearch(hybridSearchReq);
+        List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
+        for (int i = 0; i < NQ; i++) {
+            System.out.printf("============= Search result of No.%d vector =============\n", i);
+            List<SearchResp.SearchResult> results = searchResults.get(i);
+            for (SearchResp.SearchResult result : results) {
+                System.out.printf("{id: %d, score: %f}%n", result.getId(), result.getScore());
+            }
+        }
+    }
+
+    private void dropCollection() {
+        client.dropCollection(DropCollectionReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .build());
+        System.out.println("Collection dropped");
+    }
+
+    public static void main(String[] args) {
+        io.milvus.v2.HybridSearchExample example = new io.milvus.v2.HybridSearchExample();
+        example.createCollection();
+        example.insertData();
+        example.hybridSearch();
+        example.dropCollection();
+
+        client.close();
+    }
+}

+ 9 - 7
examples/main/java/io/milvus/v2/IteratorExample.java

@@ -38,7 +38,7 @@ public class IteratorExample {
                 .build();
         MilvusClientV2 client = new MilvusClientV2(config);
 
-        // create collection
+        // Create collection
         CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
                 .build();
         collectionSchema.addField(AddFieldReq.builder()
@@ -64,12 +64,12 @@ public class IteratorExample {
                 .metricType(IndexParam.MetricType.L2)
                 .build());
 
-        // drop collection if exists
+        // Drop collection if exists
         client.dropCollection(DropCollectionReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .build());
 
-        // create collection
+        // Create collection
         CreateCollectionReq requestCreate = CreateCollectionReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .collectionSchema(collectionSchema)
@@ -77,7 +77,7 @@ public class IteratorExample {
                 .build();
         client.createCollection(requestCreate);
 
-        // insert rows
+        // Insert rows
         long count = 10000;
         List<JsonObject> rowsData = new ArrayList<>();
         Random ran = new Random();
@@ -95,7 +95,7 @@ public class IteratorExample {
                 .data(rowsData)
                 .build());
 
-        // check row count
+        // Check row count
         QueryResp queryResp = client.query(QueryReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .filter("")
@@ -105,7 +105,7 @@ public class IteratorExample {
         List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
         System.out.printf("Inserted row count: %d\n", queryResults.get(0).getEntity().get("count(*)"));
 
-        // search iterator
+        // Search iterator
         SearchIterator searchIterator = client.searchIterator(SearchIteratorReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .outputFields(Lists.newArrayList(AGE_FIELD))
@@ -135,7 +135,7 @@ public class IteratorExample {
         }
         System.out.printf("%d search results returned\n%n", counter);
 
-        // query iterator
+        // Query iterator
         QueryIterator queryIterator = client.queryIterator(QueryIteratorReq.builder()
                 .collectionName(COLLECTION_NAME)
                 .expr(String.format("%s < 300", ID_FIELD))
@@ -161,5 +161,7 @@ public class IteratorExample {
             }
         }
         System.out.printf("%d query results returned%n", counter);
+
+        client.close();
     }
 }

+ 128 - 0
examples/main/java/io/milvus/v2/JsonFieldExample.java

@@ -0,0 +1,128 @@
+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.response.QueryResp;
+
+import java.util.*;
+
+public class JsonFieldExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_json_v2";
+    private static final String ID_FIELD = "id";
+    private static final String VECTOR_FIELD = "vector";
+    private static final String JSON_FIELD = "metadata";
+    private static final Integer VECTOR_DIM = 128;
+
+    private static void queryWithExpr(MilvusClientV2 client, String expr) {
+        QueryResp queryRet = client.query(QueryReq.builder()
+                .collectionName(COLLECTION_NAME)
+                .filter(expr)
+                .outputFields(Arrays.asList(ID_FIELD, JSON_FIELD))
+                .build());
+        System.out.println("\nQuery with expression: " + expr);
+        List<QueryResp.QueryResult> records = queryRet.getQueryResults();
+        for (QueryResp.QueryResult record : records) {
+            System.out.println(record.getEntity());
+        }
+        System.out.println("=============================================================");
+    }
+
+    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.FloatVector)
+                .dimension(VECTOR_DIM)
+                .build());
+        collectionSchema.addField(AddFieldReq.builder()
+                .fieldName(JSON_FIELD)
+                .dataType(DataType.JSON)
+                .build());
+
+        List<IndexParam> indexes = new ArrayList<>();
+        indexes.add(IndexParam.builder()
+                .fieldName(VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.FLAT)
+                .metricType(IndexParam.MetricType.COSINE)
+                .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 rows
+        Gson gson = new Gson();
+        for (int i = 0; i < 100; i++) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, i);
+            row.add(VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloatVector(VECTOR_DIM)));
+
+            JsonObject metadata = new JsonObject();
+            metadata.addProperty("path", String.format("\\root/abc/path%d", i));
+            metadata.addProperty("size", i);
+            if (i%7 == 0) {
+                metadata.addProperty("special", true);
+            }
+            metadata.add("flags", gson.toJsonTree(Arrays.asList(i, i + 1, i + 2)));
+            row.add(JSON_FIELD, metadata);
+            System.out.println(metadata);
+
+            client.insert(InsertReq.builder()
+                    .collectionName(COLLECTION_NAME)
+                    .data(Collections.singletonList(row))
+                    .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(*)"));
+
+        // Query by filtering JSON
+        queryWithExpr(client, "exists metadata[\"special\"]");
+        queryWithExpr(client, "metadata[\"size\"] < 5");
+        queryWithExpr(client, "metadata[\"size\"] in [4, 5, 6]");
+        queryWithExpr(client, "JSON_CONTAINS(metadata[\"flags\"], 9)");
+        queryWithExpr(client, "JSON_CONTAINS_ANY(metadata[\"flags\"], [8, 9, 10])");
+        queryWithExpr(client, "JSON_CONTAINS_ALL(metadata[\"flags\"], [8, 9, 10])");
+
+        client.close();
+    }
+}

+ 8 - 6
examples/main/java/io/milvus/v2/SimpleExample.java

@@ -20,19 +20,19 @@ public class SimpleExample {
         MilvusClientV2 client = new MilvusClientV2(config);
 
         String collectionName = "java_sdk_example_simple_v2";
-        // drop collection if exists
+        // Drop collection if exists
         client.dropCollection(DropCollectionReq.builder()
                 .collectionName(collectionName)
                 .build());
 
-        // quickly create a collection with "id" field and "vector" field
+        // Quickly create a collection with "id" field and "vector" field
         client.createCollection(CreateCollectionReq.builder()
                 .collectionName(collectionName)
                 .dimension(4)
                 .build());
         System.out.printf("Collection '%s' created\n", collectionName);
 
-        // insert some data
+        // Insert some data
         List<JsonObject> rows = new ArrayList<>();
         Gson gson = new Gson();
         for (int i = 0; i < 100; i++) {
@@ -48,7 +48,7 @@ public class SimpleExample {
                 .build());
         System.out.printf("%d rows inserted\n", insertR.getInsertCnt());
 
-        // get row count
+        // 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(collectionName)
                 .filter("")
@@ -57,7 +57,7 @@ public class SimpleExample {
                 .build());
         System.out.printf("%d rows persisted\n", (long)countR.getQueryResults().get(0).getEntity().get("count(*)"));
 
-        // retrieve
+        // Retrieve
         List<Object> ids = Arrays.asList(1L, 50L);
         GetResp getR = client.get(GetReq.builder()
                 .collectionName(collectionName)
@@ -69,7 +69,7 @@ public class SimpleExample {
             System.out.println(result.getEntity());
         }
 
-        // search
+        // Search
         SearchResp searchR = client.search(SearchReq.builder()
                 .collectionName(collectionName)
                 .data(Collections.singletonList(new FloatVec(new float[]{1.0f, 1.0f, 1.0f, 1.0f})))
@@ -84,5 +84,7 @@ public class SimpleExample {
                 System.out.printf("ID: %d, Score: %f, %s\n", (long)result.getId(), result.getScore(), result.getEntity().toString());
             }
         }
+
+        client.close();
     }
 }

+ 161 - 0
examples/main/java/io/milvus/v2/SparseVectorExample.java

@@ -0,0 +1,161 @@
+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.SparseFloatVec;
+import io.milvus.v2.service.vector.response.QueryResp;
+import io.milvus.v2.service.vector.response.SearchResp;
+
+import java.util.*;
+
+public class SparseVectorExample {
+    private static final String COLLECTION_NAME = "java_sdk_example_sparse_vector_v2";
+    private static final String ID_FIELD = "id";
+    private static final String VECTOR_FIELD = "vector";
+
+
+    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.SparseFloatVector)
+                .build());
+
+        List<IndexParam> indexes = new ArrayList<>();
+        indexes.add(IndexParam.builder()
+                .fieldName(VECTOR_FIELD)
+                .indexType(IndexParam.IndexType.SPARSE_WAND)
+                .metricType(IndexParam.MetricType.IP)
+                .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<JsonObject> rows = new ArrayList<>();
+        Gson gson = new Gson();
+        List<SortedMap<Long, Float>> vectors = new ArrayList<>();
+        for (long i = 0L; i < rowCount; ++i) {
+            JsonObject row = new JsonObject();
+            row.addProperty(ID_FIELD, i);
+            SortedMap<Long, Float> vector = CommonUtils.generateSparseVector();
+            vectors.add(vector);
+            row.add(VECTOR_FIELD, gson.toJsonTree(vector));
+            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);
+            SortedMap<Long, Float> targetVector = vectors.get(k);
+            Map<String,Object> params = new HashMap<>();
+            params.put("drop_ratio_search",0.2);
+            SearchResp searchResp = client.search(SearchReq.builder()
+                    .collectionName(COLLECTION_NAME)
+                    .data(Collections.singletonList(new SparseFloatVec(targetVector)))
+                    .annsField(VECTOR_FIELD)
+                    .outputFields(Collections.singletonList(VECTOR_FIELD))
+                    .searchParams(params)
+                    .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("The result of No.%d target vector:\n", i);
+            for (SearchResp.SearchResult result : results) {
+                System.out.println(result.getEntity());
+            }
+
+            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",
+                        firstResult.getId(), k));
+            }
+            SortedMap<Long, Float> sparse = (SortedMap<Long, Float>) firstResult.getEntity().get(VECTOR_FIELD);
+            if (!sparse.equals(targetVector)) {
+                throw new RuntimeException("The query result is incorrect");
+            }
+        }
+        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 {
+            SortedMap<Long, Float> sparse = (SortedMap<Long, Float>) queryResults.get(0).getEntity().get(VECTOR_FIELD);
+            if (!sparse.equals(vectors.get(n))) {
+                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();
+    }
+}

+ 76 - 0
examples/main/java/io/milvus/v2/TLSExample.java

@@ -0,0 +1,76 @@
+package io.milvus.v2;
+
+import io.milvus.v2.client.ConnectConfig;
+import io.milvus.v2.client.MilvusClientV2;
+
+import java.io.File;
+import java.net.URL;
+
+
+// Note: read the following description before running this example
+// 1. cmd into the "tls" folder, generate certificate by the following commands.
+// (more details read the https://milvus.io/docs/tls.md)
+//   chmod +x gen.sh
+//   ./gen.sh
+//
+// 2. Configure the file paths of server.pem, server.key, and ca.pem for the server in config/milvus.yaml.
+//    Set tlsMode to 1 for one-way authentication. Set tlsMode to 2 for two-way authentication.
+// (read the doc to know how to config milvus: https://milvus.io/docs/configure-docker.md)
+//    tls:
+//        serverPemPath: [path_to_tls]/tls/server.pem
+//        serverKeyPath: [path_to_tls]/tls/server.key
+//        caPemPath: [path_to_tls]/tls/ca.pem
+//
+//    common:
+//        security:
+//            tlsMode: 2
+//
+// 3. Start milvus server
+// 4. Run this example.
+//    Connect server by oneWayAuth() if the server tlsMode=1, connect server by twoWayAuth() if the server tlsMode=2.
+//
+public class TLSExample {
+    private static void oneWayAuth() {
+        ClassLoader classLoader = TLSExample.class.getClassLoader();
+        URL resourceUrl = classLoader.getResource("tls");
+        String path = new File(resourceUrl.getFile()).getAbsolutePath();
+        ConnectConfig config = ConnectConfig.builder()
+                .uri("http://localhost:19530")
+                .serverName("localhost")
+                .serverPemPath(path + "/server.pem")
+                .build();
+        MilvusClientV2 client = new MilvusClientV2(config);
+
+        String version = client.getServerVersion();
+        System.out.println("Server version: " + version);
+    }
+
+    private static void twoWayAuth() {
+        ClassLoader classLoader = TLSExample.class.getClassLoader();
+        URL resourceUrl = classLoader.getResource("tls");
+        String path = new File(resourceUrl.getFile()).getAbsolutePath();
+        ConnectConfig config = ConnectConfig.builder()
+                .uri("http://localhost:19530")
+                .serverName("localhost")
+                .caPemPath(path + "/ca.pem")
+                .clientKeyPath(path + "/client.key")
+                .clientPemPath(path + "/client.pem")
+                .build();
+        MilvusClientV2 client = new MilvusClientV2(config);
+
+        String version = client.getServerVersion();
+        System.out.println("Server version: " + version);
+    }
+
+    // tlsMode=1, set oneWay=true
+    // tlsMode=2, set oneWay=false
+    private static final boolean oneWay = false;
+
+    public static void main(String[] args) {
+        if (oneWay) {
+            oneWayAuth();
+        } else {
+            twoWayAuth();
+        }
+    }
+}

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

@@ -84,7 +84,7 @@ class MilvusClientDockerTest {
     private static final Random RANDOM = new Random();
 
     @Container
-    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:master-20240729-a4957540-amd64");
+    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:v2.4.7");
 
     @BeforeAll
     public static void setUp() {

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

@@ -80,7 +80,7 @@ class MilvusClientV2DockerTest {
     private static final Random RANDOM = new Random();
 
     @Container
-    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:master-20240729-a4957540-amd64");
+    private static final MilvusContainer milvus = new MilvusContainer("milvusdb/milvus:v2.4.7");
 
     @BeforeAll
     public static void setUp() {