2828import io .milvus .v2 .common .ConsistencyLevel ;
2929import io .milvus .v2 .common .DataType ;
3030import io .milvus .v2 .common .IndexParam ;
31- import io .milvus .v2 .service .collection .request .AddFieldReq ;
32- import io .milvus .v2 .service .collection .request .CreateCollectionReq ;
33- import io .milvus .v2 .service .collection .request .DropCollectionReq ;
34- import io .milvus .v2 .service .collection .request .LoadCollectionReq ;
31+ import io .milvus .v2 .service .collection .request .*;
32+ import io .milvus .v2 .service .collection .response .DescribeCollectionResp ;
3533import io .milvus .v2 .service .index .request .CreateIndexReq ;
3634import io .milvus .v2 .service .vector .request .InsertReq ;
3735import io .milvus .v2 .service .vector .request .QueryReq ;
4341import io .milvus .v2 .service .vector .response .QueryResp ;
4442import io .milvus .v2 .service .vector .response .SearchResp ;
4543
46- import java .util .*;
44+ import java .util .ArrayList ;
45+ import java .util .Arrays ;
46+ import java .util .Collections ;
47+ import java .util .List ;
48+ import java .util .Map ;
49+ import java .util .Random ;
4750
4851public class StructExample {
4952 private static final MilvusClientV2 client ;
@@ -62,7 +65,12 @@ public class StructExample {
6265 private static final String CLIP_VECTOR_FIELD = "clip_embedding" ;
6366 private static final String DESC_FIELD = "clip_desc" ;
6467 private static final String DESC_VECTOR_FIELD = "description_embedding" ;
68+ private static final String EXTRA_STRUCT_FIELD = "metadata" ;
69+ private static final String RATING_FIELD = "rating" ;
70+ private static final String TAG_FIELD = "tag" ;
6571 private static final Integer VECTOR_DIM = 128 ;
72+ private static final Random RANDOM = new Random ();
73+ private static final long EXTRA_ROW_ID = 5000L ;
6674
6775 private static void createCollection () {
6876 CreateCollectionReq .CollectionSchema collectionSchema = CreateCollectionReq .CollectionSchema .builder ()
@@ -77,7 +85,7 @@ private static void createCollection() {
7785 .dataType (DataType .VarChar )
7886 .maxLength (1024 )
7987 .build ());
80- // define struct field schema, note that each name of sub-field must be unique in the entire collection
88+
8189 collectionSchema .addField (AddFieldReq .builder ()
8290 .fieldName (STRUCT_FIELD )
8391 .description ("clips of a film" )
@@ -109,7 +117,6 @@ private static void createCollection() {
109117 .build ())
110118 .build ());
111119
112- // define another struct field schema, note that it has a same name subfield to the STRUCT_FIELD
113120 collectionSchema .addField (AddFieldReq .builder ()
114121 .fieldName ("simplify_clips" )
115122 .description ("simplify clips" )
@@ -128,13 +135,11 @@ private static void createCollection() {
128135 .collectionName (COLLECTION_NAME )
129136 .build ());
130137
131- CreateCollectionReq requestCreate = CreateCollectionReq .builder ()
138+ client . createCollection ( CreateCollectionReq .builder ()
132139 .collectionName (COLLECTION_NAME )
133140 .collectionSchema (collectionSchema )
134- .build ();
135- client .createCollection (requestCreate );
141+ .build ());
136142
137- // struct vector uses special index/metric type
138143 List <IndexParam > indexParams = new ArrayList <>();
139144 indexParams .add (IndexParam .builder ()
140145 .fieldName (String .format ("%s[%s]" , STRUCT_FIELD , CLIP_VECTOR_FIELD ))
@@ -162,45 +167,51 @@ private static void createCollection() {
162167 .collectionName (COLLECTION_NAME )
163168 .build ());
164169 System .out .println ("Collection created: " + COLLECTION_NAME );
170+
171+ describeCollection ();
172+ }
173+
174+ private static void describeCollection () {
175+ DescribeCollectionResp resp = client .describeCollection (DescribeCollectionReq .builder ()
176+ .collectionName (COLLECTION_NAME )
177+ .build ());
178+ System .out .println (resp .getCollectionSchema ());
179+ }
180+
181+ private static JsonObject buildBaseRow (long id ) {
182+ JsonObject row = new JsonObject ();
183+ row .addProperty (ID_FIELD , id );
184+ row .addProperty (NAME_FIELD , "film_" + id );
185+
186+ JsonArray structArr = new JsonArray ();
187+ for (int k = 0 ; k < 5 ; k ++) {
188+ JsonObject struct = new JsonObject ();
189+ struct .addProperty (FRAME_FIELD , RANDOM .nextInt (10000 ));
190+ struct .add (CLIP_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (VECTOR_DIM )));
191+ struct .addProperty (DESC_FIELD , "clip_description_" + id );
192+ struct .add (DESC_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (VECTOR_DIM )));
193+ structArr .add (struct );
194+ }
195+ row .add (STRUCT_FIELD , structArr );
196+
197+ JsonArray simplifyClips = new JsonArray ();
198+ for (int k = 0 ; k < 2 ; k ++) {
199+ JsonObject struct = new JsonObject ();
200+ struct .add (CLIP_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (32 )));
201+ simplifyClips .add (struct );
202+ }
203+ row .add ("simplify_clips" , simplifyClips );
204+ return row ;
165205 }
166206
167207 private static void insertData (int rowCount ) {
168208 final int batchSize = 300 ;
169209 int insertedCount = 0 ;
170- Random ran = new Random ();
171210 while (insertedCount < rowCount ) {
172- int nextBatch = batchSize ;
173- int leftCount = rowCount - insertedCount ;
174- if (nextBatch > leftCount ) {
175- nextBatch = leftCount ;
176- }
211+ int nextBatch = Math .min (batchSize , rowCount - insertedCount );
177212 List <JsonObject > rows = new ArrayList <>();
178213 for (int i = 0 ; i < nextBatch ; i ++) {
179- JsonObject row = new JsonObject ();
180- int id = insertedCount + i ;
181- row .addProperty (ID_FIELD , id );
182- row .addProperty (NAME_FIELD , "film_" + id );
183- JsonArray structArr = new JsonArray ();
184- for (int k = 0 ; k < 5 ; k ++) {
185- JsonObject struct = new JsonObject ();
186- struct .addProperty (FRAME_FIELD , ran .nextInt (10000 ));
187- struct .add (CLIP_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (VECTOR_DIM )));
188- struct .addProperty (DESC_FIELD , "clip_description_" + id );
189- struct .add (DESC_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (VECTOR_DIM )));
190- structArr .add (struct );
191- }
192- row .add (STRUCT_FIELD , structArr );
193-
194- // for the "simplify_clips"
195- structArr = new JsonArray ();
196- for (int k = 0 ; k < 2 ; k ++) {
197- JsonObject struct = new JsonObject ();
198- struct .add (CLIP_VECTOR_FIELD , JsonUtils .toJsonTree (CommonUtils .generateFloatVector (32 )));
199- structArr .add (struct );
200- }
201- row .add ("simplify_clips" , structArr );
202-
203- rows .add (row );
214+ rows .add (buildBaseRow (insertedCount + i ));
204215 }
205216
206217 InsertResp insertResp = client .insert (InsertReq .builder ()
@@ -217,7 +228,6 @@ private static void insertData(int rowCount) {
217228 .consistencyLevel (ConsistencyLevel .STRONG )
218229 .build ());
219230 System .out .printf ("%d rows persisted\n " , (long ) countR .getQueryResults ().get (0 ).getEntity ().get ("count(*)" ));
220-
221231 }
222232
223233 private static List <QueryResp .QueryResult > query (String filter ) {
@@ -238,18 +248,14 @@ private static List<QueryResp.QueryResult> query(String filter) {
238248
239249 private static void search (String annsField , List <BaseVector > searchData ) {
240250 System .out .println ("===================================================" );
241- String msg = String . format ("Search on field '%s' in struct '%s' with nq=%d" ,
251+ System . out . printf ("Search on field '%s' in struct '%s' with nq=%d\n " ,
242252 annsField , STRUCT_FIELD , searchData .size ());
243- System .out .println (msg );
244253
245-
246- String annFullName = String .format ("%s[%s]" , STRUCT_FIELD , annsField );
247- int topK = 5 ;
248254 SearchResp searchResp = client .search (SearchReq .builder ()
249255 .collectionName (COLLECTION_NAME )
250- .annsField (annFullName )
256+ .annsField (String . format ( "%s[%s]" , STRUCT_FIELD , annsField ) )
251257 .data (searchData )
252- .limit (topK )
258+ .limit (5 )
253259 .consistencyLevel (ConsistencyLevel .BOUNDED )
254260 .outputFields (Arrays .asList (NAME_FIELD ,
255261 String .format ("%s[%s]" , STRUCT_FIELD , FRAME_FIELD ),
@@ -259,32 +265,112 @@ private static void search(String annsField, List<BaseVector> searchData) {
259265 List <List <SearchResp .SearchResult >> searchResults = searchResp .getSearchResults ();
260266 for (int i = 0 ; i < searchResults .size (); i ++) {
261267 System .out .println ("Results of No." + i + " embedding list" );
262- List <SearchResp .SearchResult > results = searchResults .get (i );
263- for (SearchResp .SearchResult result : results ) {
268+ for (SearchResp .SearchResult result : searchResults .get (i )) {
264269 System .out .println (result );
265270 }
266271 }
267272 }
268273
274+ private static JsonArray buildMetadataStructArray () {
275+ JsonArray metadata = new JsonArray ();
276+
277+ JsonObject first = new JsonObject ();
278+ first .addProperty (RATING_FIELD , 5 );
279+ first .addProperty (TAG_FIELD , "favorite" );
280+ metadata .add (first );
281+
282+ JsonObject second = new JsonObject ();
283+ second .addProperty (RATING_FIELD , 4 );
284+ second .addProperty (TAG_FIELD , "classic" );
285+ metadata .add (second );
286+ return metadata ;
287+ }
288+
289+ private static void addCollectionStructField () {
290+ System .out .println ("===================================================" );
291+ System .out .println ("Add a new struct field to the existing collection" );
292+ client .addCollectionStructField (AddCollectionStructFieldReq .builder ()
293+ .collectionName (COLLECTION_NAME )
294+ .fieldName (EXTRA_STRUCT_FIELD )
295+ .description ("additional metadata for films" )
296+ .maxCapacity (8 )
297+ .addStructField (AddFieldReq .builder ()
298+ .fieldName (RATING_FIELD )
299+ .dataType (DataType .Int32 )
300+ .build ())
301+ .addStructField (AddFieldReq .builder ()
302+ .fieldName (TAG_FIELD )
303+ .dataType (DataType .VarChar )
304+ .maxLength (128 )
305+ .build ())
306+ .build ());
307+ System .out .println ("Added struct field: " + EXTRA_STRUCT_FIELD );
308+
309+ describeCollection ();
310+
311+ QueryResp existingRow = client .query (QueryReq .builder ()
312+ .collectionName (COLLECTION_NAME )
313+ .filter (ID_FIELD + " == 5" )
314+ .consistencyLevel (ConsistencyLevel .STRONG )
315+ .outputFields (Arrays .asList (ID_FIELD , EXTRA_STRUCT_FIELD ))
316+ .build ());
317+ System .out .println ("Existing row after addCollectionStructField() - new field is null" );
318+ for (QueryResp .QueryResult result : existingRow .getQueryResults ()) {
319+ System .out .println (result .getEntity ());
320+ }
321+
322+ JsonObject newRow = buildBaseRow (EXTRA_ROW_ID );
323+ newRow .add (EXTRA_STRUCT_FIELD , buildMetadataStructArray ());
324+ client .insert (InsertReq .builder ()
325+ .collectionName (COLLECTION_NAME )
326+ .data (Collections .singletonList (newRow ))
327+ .build ());
328+
329+ QueryResp newRowResp = client .query (QueryReq .builder ()
330+ .collectionName (COLLECTION_NAME )
331+ .filter (ID_FIELD + " == " + EXTRA_ROW_ID )
332+ .consistencyLevel (ConsistencyLevel .STRONG )
333+ .outputFields (Arrays .asList (ID_FIELD , NAME_FIELD , EXTRA_STRUCT_FIELD ))
334+ .build ());
335+ System .out .println ("New row with the added struct field" );
336+ for (QueryResp .QueryResult result : newRowResp .getQueryResults ()) {
337+ System .out .println (result .getEntity ());
338+ }
339+
340+ QueryResp projectedResp = client .query (QueryReq .builder ()
341+ .collectionName (COLLECTION_NAME )
342+ .filter (ID_FIELD + " == " + EXTRA_ROW_ID )
343+ .consistencyLevel (ConsistencyLevel .STRONG )
344+ .outputFields (Arrays .asList (ID_FIELD , NAME_FIELD ,
345+ String .format ("%s[%s]" , EXTRA_STRUCT_FIELD , RATING_FIELD ),
346+ String .format ("%s[%s]" , EXTRA_STRUCT_FIELD , TAG_FIELD )))
347+ .build ());
348+ System .out .println ("Projected subfields from the added struct field" );
349+ for (QueryResp .QueryResult result : projectedResp .getQueryResults ()) {
350+ System .out .println (result .getEntity ());
351+ }
352+ }
353+
269354 public static void main (String [] args ) {
270- createCollection ();
271- insertData (2000 );
272-
273- // fetch 2 rows
274- List <QueryResp .QueryResult > results = query (ID_FIELD + " in [5, 8]" );
275-
276- // use the fetched data to search struct
277- for (QueryResp .QueryResult result : results ) {
278- // in the insertData() method, we inserted 5 structures for each row
279- // in query results, each struct is represented as a Map
280- Map <String , Object > fetchedEntity = result .getEntity ();
281- List <Map <String , Object >> structs = (List <Map <String , Object >>) fetchedEntity .get (STRUCT_FIELD );
282- EmbeddingList embList = new EmbeddingList ();
283- for (Map <String , Object > struct : structs ) {
284- List <Float > vector = (List <Float >) struct .get (CLIP_VECTOR_FIELD );
285- embList .add (new FloatVec (vector ));
355+ try {
356+ createCollection ();
357+ insertData (2000 );
358+
359+ List <QueryResp .QueryResult > results = query (ID_FIELD + " in [5, 8]" );
360+ for (QueryResp .QueryResult result : results ) {
361+ Map <String , Object > fetchedEntity = result .getEntity ();
362+ List <Map <String , Object >> structs = (List <Map <String , Object >>) fetchedEntity .get (STRUCT_FIELD );
363+ EmbeddingList embList = new EmbeddingList ();
364+ for (Map <String , Object > struct : structs ) {
365+ List <Float > vector = (List <Float >) struct .get (CLIP_VECTOR_FIELD );
366+ embList .add (new FloatVec (vector ));
367+ }
368+ search (CLIP_VECTOR_FIELD , Collections .singletonList (embList ));
286369 }
287- search (CLIP_VECTOR_FIELD , Collections .singletonList (embList ));
370+
371+ addCollectionStructField ();
372+ } finally {
373+ client .close ();
288374 }
289375 }
290376}
0 commit comments