HybridSearchExample.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package io.milvus;
  20. import com.google.gson.Gson;
  21. import com.google.gson.JsonObject;
  22. import io.milvus.client.MilvusClient;
  23. import io.milvus.client.MilvusServiceClient;
  24. import io.milvus.common.clientenum.ConsistencyLevelEnum;
  25. import io.milvus.grpc.DataType;
  26. import io.milvus.grpc.GetCollectionStatisticsResponse;
  27. import io.milvus.grpc.MutationResult;
  28. import io.milvus.grpc.SearchResults;
  29. import io.milvus.param.*;
  30. import io.milvus.param.collection.*;
  31. import io.milvus.param.dml.*;
  32. import io.milvus.param.dml.ranker.*;
  33. import io.milvus.param.index.CreateIndexParam;
  34. import io.milvus.response.GetCollStatResponseWrapper;
  35. import io.milvus.response.SearchResultsWrapper;
  36. import java.util.ArrayList;
  37. import java.util.Arrays;
  38. import java.util.List;
  39. public class HybridSearchExample {
  40. private static final MilvusClient milvusClient;
  41. static {
  42. milvusClient = new MilvusServiceClient(ConnectParam.newBuilder()
  43. .withHost("localhost")
  44. .withPort(19530)
  45. .build());
  46. }
  47. private static final String COLLECTION_NAME = "java_sdk_example_hybrid_search";
  48. private static final String ID_FIELD = "ID";
  49. private static final String FLOAT_VECTOR_FIELD = "float_vector";
  50. private static final Integer FLOAT_VECTOR_DIM = 128;
  51. private static final MetricType FLOAT_VECTOR_METRIC = MetricType.COSINE;
  52. private static final String BINARY_VECTOR_FIELD = "binary_vector";
  53. private static final Integer BINARY_VECTOR_DIM = 256;
  54. private static final MetricType BINARY_VECTOR_METRIC = MetricType.JACCARD;
  55. private static final String FLOAT16_VECTOR_FIELD = "float16_vector";
  56. private static final Integer FLOAT16_VECTOR_DIM = 256;
  57. private static final MetricType FLOAT16_VECTOR_METRIC = MetricType.L2;
  58. private static final String SPARSE_VECTOR_FIELD = "sparse_vector";
  59. private static final MetricType SPARSE_VECTOR_METRIC = MetricType.IP;
  60. private void createCollection() {
  61. R<RpcStatus> resp = milvusClient.dropCollection(DropCollectionParam.newBuilder()
  62. .withCollectionName(COLLECTION_NAME)
  63. .build());
  64. CommonUtils.handleResponseStatus(resp);
  65. // Define fields
  66. // Note: There is a configuration in milvus.yaml to define the max vector fields in a collection
  67. // proxy.maxVectorFieldNum: 4
  68. // By default, the max vector fields number is 4
  69. List<FieldType> fieldsSchema = Arrays.asList(
  70. FieldType.newBuilder()
  71. .withName(ID_FIELD)
  72. .withDataType(DataType.Int64)
  73. .withPrimaryKey(true)
  74. .withAutoID(false)
  75. .build(),
  76. FieldType.newBuilder()
  77. .withName(FLOAT_VECTOR_FIELD)
  78. .withDataType(DataType.FloatVector)
  79. .withDimension(FLOAT_VECTOR_DIM)
  80. .build(),
  81. FieldType.newBuilder()
  82. .withName(BINARY_VECTOR_FIELD)
  83. .withDataType(DataType.BinaryVector)
  84. .withDimension(BINARY_VECTOR_DIM)
  85. .build(),
  86. FieldType.newBuilder()
  87. .withName(FLOAT16_VECTOR_FIELD)
  88. .withDataType(DataType.Float16Vector)
  89. .withDimension(FLOAT16_VECTOR_DIM)
  90. .build(),
  91. FieldType.newBuilder()
  92. .withName(SPARSE_VECTOR_FIELD)
  93. .withDataType(DataType.SparseFloatVector)
  94. .build()
  95. );
  96. // Create the collection with multi vector fields
  97. resp = milvusClient.createCollection(CreateCollectionParam.newBuilder()
  98. .withCollectionName(COLLECTION_NAME)
  99. .withSchema(CollectionSchemaParam.newBuilder().withFieldTypes(fieldsSchema).build())
  100. .build());
  101. CommonUtils.handleResponseStatus(resp);
  102. // Specify an index types on the vector fields.
  103. resp = milvusClient.createIndex(CreateIndexParam.newBuilder()
  104. .withCollectionName(COLLECTION_NAME)
  105. .withFieldName(FLOAT_VECTOR_FIELD)
  106. .withIndexType(IndexType.IVF_PQ)
  107. .withExtraParam("{\"nlist\":128, \"m\":16, \"nbits\":8}")
  108. .withMetricType(FLOAT_VECTOR_METRIC)
  109. .build());
  110. CommonUtils.handleResponseStatus(resp);
  111. resp = milvusClient.createIndex(CreateIndexParam.newBuilder()
  112. .withCollectionName(COLLECTION_NAME)
  113. .withFieldName(BINARY_VECTOR_FIELD)
  114. .withIndexType(IndexType.BIN_FLAT)
  115. .withMetricType(BINARY_VECTOR_METRIC)
  116. .build());
  117. CommonUtils.handleResponseStatus(resp);
  118. resp = milvusClient.createIndex(CreateIndexParam.newBuilder()
  119. .withCollectionName(COLLECTION_NAME)
  120. .withFieldName(FLOAT16_VECTOR_FIELD)
  121. .withIndexType(IndexType.HNSW)
  122. .withExtraParam("{\"M\":16,\"efConstruction\":64}")
  123. .withMetricType(FLOAT16_VECTOR_METRIC)
  124. .build());
  125. CommonUtils.handleResponseStatus(resp);
  126. resp = milvusClient.createIndex(CreateIndexParam.newBuilder()
  127. .withCollectionName(COLLECTION_NAME)
  128. .withFieldName(SPARSE_VECTOR_FIELD)
  129. .withIndexType(IndexType.SPARSE_INVERTED_INDEX)
  130. .withExtraParam("{\"drop_ratio_build\":0.2}")
  131. .withMetricType(SPARSE_VECTOR_METRIC)
  132. .build());
  133. CommonUtils.handleResponseStatus(resp);
  134. // Call loadCollection() to enable automatically loading data into memory for searching
  135. milvusClient.loadCollection(LoadCollectionParam.newBuilder()
  136. .withCollectionName(COLLECTION_NAME)
  137. .build());
  138. System.out.println("Collection created");
  139. }
  140. private void insertData() {
  141. long idCount = 0;
  142. int rowCount = 10000;
  143. // Insert entities by rows
  144. List<JsonObject> rows = new ArrayList<>();
  145. Gson gson = new Gson();
  146. for (long i = 1L; i <= rowCount; ++i) {
  147. JsonObject row = new JsonObject();
  148. row.addProperty(ID_FIELD, idCount++);
  149. row.add(FLOAT_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloatVector(FLOAT_VECTOR_DIM)));
  150. row.add(BINARY_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateBinaryVector(BINARY_VECTOR_DIM).array()));
  151. row.add(FLOAT16_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateFloat16Vector(FLOAT16_VECTOR_DIM, false).array()));
  152. row.add(SPARSE_VECTOR_FIELD, gson.toJsonTree(CommonUtils.generateSparseVector()));
  153. rows.add(row);
  154. }
  155. R<MutationResult> resp = milvusClient.insert(InsertParam.newBuilder()
  156. .withCollectionName(COLLECTION_NAME)
  157. .withRows(rows)
  158. .build());
  159. CommonUtils.handleResponseStatus(resp);
  160. System.out.printf("%d entities inserted by rows\n", rowCount);
  161. // Insert entities by columns
  162. List<Long> ids = new ArrayList<>();
  163. for (long i = 1L; i <= 10000; ++i) {
  164. ids.add(idCount++);
  165. }
  166. List<InsertParam.Field> fieldsInsert = new ArrayList<>();
  167. fieldsInsert.add(new InsertParam.Field(ID_FIELD, ids));
  168. fieldsInsert.add(new InsertParam.Field(FLOAT_VECTOR_FIELD,
  169. CommonUtils.generateFloatVectors(FLOAT_VECTOR_DIM, rowCount)));
  170. fieldsInsert.add(new InsertParam.Field(BINARY_VECTOR_FIELD,
  171. CommonUtils.generateBinaryVectors(BINARY_VECTOR_DIM, rowCount)));
  172. fieldsInsert.add(new InsertParam.Field(FLOAT16_VECTOR_FIELD,
  173. CommonUtils.generateFloat16Vectors(FLOAT16_VECTOR_DIM, rowCount, false)));
  174. fieldsInsert.add(new InsertParam.Field(SPARSE_VECTOR_FIELD,
  175. CommonUtils.generateSparseVectors(rowCount)));
  176. resp = milvusClient.insert(InsertParam.newBuilder()
  177. .withCollectionName(COLLECTION_NAME)
  178. .withFields(fieldsInsert)
  179. .build());
  180. CommonUtils.handleResponseStatus(resp);
  181. System.out.printf("%d entities inserted by columns\n", rowCount);
  182. }
  183. private void hybridSearch() {
  184. // Get the row count
  185. R<GetCollectionStatisticsResponse> resp = milvusClient.getCollectionStatistics(GetCollectionStatisticsParam
  186. .newBuilder()
  187. .withCollectionName(COLLECTION_NAME)
  188. .withFlush(true)
  189. .build());
  190. CommonUtils.handleResponseStatus(resp);
  191. GetCollStatResponseWrapper stat = new GetCollStatResponseWrapper(resp.getData());
  192. System.out.println("Collection row count: " + stat.getRowCount());
  193. // Search on multiple vector fields
  194. int NQ = 2;
  195. AnnSearchParam req1 = AnnSearchParam.newBuilder()
  196. .withVectorFieldName(FLOAT_VECTOR_FIELD)
  197. .withFloatVectors(CommonUtils.generateFloatVectors(FLOAT_VECTOR_DIM, NQ))
  198. .withMetricType(FLOAT_VECTOR_METRIC)
  199. .withParams("{\"nprobe\": 32}")
  200. .withTopK(10)
  201. .build();
  202. AnnSearchParam req2 = AnnSearchParam.newBuilder()
  203. .withVectorFieldName(BINARY_VECTOR_FIELD)
  204. .withBinaryVectors(CommonUtils.generateBinaryVectors(BINARY_VECTOR_DIM, NQ))
  205. .withMetricType(BINARY_VECTOR_METRIC)
  206. .withTopK(15)
  207. .build();
  208. AnnSearchParam req3 = AnnSearchParam.newBuilder()
  209. .withVectorFieldName(FLOAT16_VECTOR_FIELD)
  210. .withFloat16Vectors(CommonUtils.generateFloat16Vectors(FLOAT16_VECTOR_DIM, NQ, false))
  211. .withMetricType(FLOAT16_VECTOR_METRIC)
  212. .withParams("{\"ef\":64}")
  213. .withTopK(20)
  214. .build();
  215. AnnSearchParam req4 = AnnSearchParam.newBuilder()
  216. .withVectorFieldName(SPARSE_VECTOR_FIELD)
  217. .withSparseFloatVectors(CommonUtils.generateSparseVectors(NQ))
  218. .withMetricType(SPARSE_VECTOR_METRIC)
  219. .withParams("{\"drop_ratio_search\":0.2}")
  220. .withTopK(20)
  221. .build();
  222. HybridSearchParam searchParam = HybridSearchParam.newBuilder()
  223. .withCollectionName(COLLECTION_NAME)
  224. .addOutField(FLOAT_VECTOR_FIELD)
  225. .addOutField(BINARY_VECTOR_FIELD)
  226. .addOutField(FLOAT16_VECTOR_FIELD)
  227. .addOutField(SPARSE_VECTOR_FIELD)
  228. .addSearchRequest(req1)
  229. .addSearchRequest(req2)
  230. .addSearchRequest(req3)
  231. .addSearchRequest(req4)
  232. .withTopK(5)
  233. .withConsistencyLevel(ConsistencyLevelEnum.STRONG)
  234. .withRanker(RRFRanker.newBuilder()
  235. .withK(2)
  236. .build())
  237. .build();
  238. R<SearchResults> searchR = milvusClient.hybridSearch(searchParam);
  239. CommonUtils.handleResponseStatus(searchR);
  240. // Print search result
  241. SearchResultsWrapper results = new SearchResultsWrapper(searchR.getData().getResults());
  242. for (int k = 0; k < NQ; k++) {
  243. System.out.printf("============= Search result of No.%d vector =============\n", k);
  244. List<SearchResultsWrapper.IDScore> scores = results.getIDScore(0);
  245. for (SearchResultsWrapper.IDScore score : scores) {
  246. System.out.println(score);
  247. }
  248. }
  249. }
  250. private void dropCollection() {
  251. R<RpcStatus> resp = milvusClient.dropCollection(DropCollectionParam.newBuilder()
  252. .withCollectionName(COLLECTION_NAME)
  253. .build());
  254. CommonUtils.handleResponseStatus(resp);
  255. System.out.println("Collection dropped");
  256. }
  257. public static void main(String[] args) {
  258. HybridSearchExample example = new HybridSearchExample();
  259. example.createCollection();
  260. example.insertData();
  261. example.hybridSearch();
  262. example.dropCollection();
  263. }
  264. }