Skip to content

Commit de4202d

Browse files
authored
Add geometry example (#1631)
Signed-off-by: yhmo <yihua.mo@zilliz.com>
1 parent 1861887 commit de4202d

File tree

5 files changed

+222
-59
lines changed

5 files changed

+222
-59
lines changed

‎docker-compose.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: '3.5'
33
services:
44
standalone:
55
container_name: milvus-javasdk-standalone-1
6-
image: milvusdb/milvus:master-20250927-cc53b25b
6+
image: milvusdb/milvus:master-20250929-ca1cc7c9-amd64
77
command: [ "milvus", "run", "standalone" ]
88
environment:
99
- COMMON_STORAGETYPE=local
@@ -24,7 +24,7 @@ services:
2424

2525
standaloneslave:
2626
container_name: milvus-javasdk-standalone-2
27-
image: milvusdb/milvus:master-20250927-cc53b25b
27+
image: milvusdb/milvus:master-20250929-ca1cc7c9-amd64
2828
command: [ "milvus", "run", "standalone" ]
2929
environment:
3030
- COMMON_STORAGETYPE=local
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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+
20+
package io.milvus.v2;
21+
22+
import com.google.gson.JsonObject;
23+
import io.milvus.common.utils.JsonUtils;
24+
import io.milvus.v1.CommonUtils;
25+
import io.milvus.v2.client.ConnectConfig;
26+
import io.milvus.v2.client.MilvusClientV2;
27+
import io.milvus.v2.common.ConsistencyLevel;
28+
import io.milvus.v2.common.DataType;
29+
import io.milvus.v2.common.IndexParam;
30+
import io.milvus.v2.service.collection.request.AddFieldReq;
31+
import io.milvus.v2.service.collection.request.CreateCollectionReq;
32+
import io.milvus.v2.service.collection.request.DropCollectionReq;
33+
import io.milvus.v2.service.collection.request.LoadCollectionReq;
34+
import io.milvus.v2.service.index.request.CreateIndexReq;
35+
import io.milvus.v2.service.vector.request.InsertReq;
36+
import io.milvus.v2.service.vector.request.QueryReq;
37+
import io.milvus.v2.service.vector.response.QueryResp;
38+
39+
import java.util.*;
40+
41+
public class GeometryExample {
42+
private static final MilvusClientV2 client;
43+
static {
44+
client = new MilvusClientV2(ConnectConfig.builder()
45+
.uri("http://localhost:19530")
46+
.build());
47+
}
48+
49+
private static final String COLLECTION_NAME = "java_sdk_example_geometry_v2";
50+
private static final String ID_FIELD = "id";
51+
private static final String GEO_FIELD = "geometry";
52+
private static final String VECTOR_FIELD = "vector";
53+
private static final Integer VECTOR_DIM = 128;
54+
55+
private static void createCollection() {
56+
CreateCollectionReq.CollectionSchema collectionSchema = CreateCollectionReq.CollectionSchema.builder()
57+
.build();
58+
collectionSchema.addField(AddFieldReq.builder()
59+
.fieldName(ID_FIELD)
60+
.dataType(DataType.Int64)
61+
.isPrimaryKey(true)
62+
.autoID(true)
63+
.build());
64+
collectionSchema.addField(AddFieldReq.builder()
65+
.fieldName(GEO_FIELD)
66+
.dataType(DataType.Geometry)
67+
.build());
68+
collectionSchema.addField(AddFieldReq.builder()
69+
.fieldName(VECTOR_FIELD)
70+
.dataType(DataType.FloatVector)
71+
.dimension(VECTOR_DIM)
72+
.build());
73+
74+
client.dropCollection(DropCollectionReq.builder()
75+
.collectionName(COLLECTION_NAME)
76+
.build());
77+
78+
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
79+
.collectionName(COLLECTION_NAME)
80+
.collectionSchema(collectionSchema)
81+
.build();
82+
client.createCollection(requestCreate);
83+
84+
List<IndexParam> indexParams = new ArrayList<>();
85+
indexParams.add(IndexParam.builder()
86+
.fieldName(VECTOR_FIELD)
87+
.indexType(IndexParam.IndexType.AUTOINDEX)
88+
.metricType(IndexParam.MetricType.COSINE)
89+
.build());
90+
// geometry index no need metric type
91+
indexParams.add(IndexParam.builder()
92+
.fieldName(GEO_FIELD)
93+
.indexType(IndexParam.IndexType.RTREE)
94+
.build());
95+
client.createIndex(CreateIndexReq.builder()
96+
.collectionName(COLLECTION_NAME)
97+
.indexParams(indexParams)
98+
.build());
99+
client.loadCollection(LoadCollectionReq.builder()
100+
.collectionName(COLLECTION_NAME)
101+
.build());
102+
System.out.println("Collection created: " + COLLECTION_NAME);
103+
}
104+
105+
private static void insertGeometry(String geo) {
106+
JsonObject row = new JsonObject();
107+
row.addProperty(GEO_FIELD, geo);
108+
row.add(VECTOR_FIELD, JsonUtils.toJsonTree(CommonUtils.generateFloatVector(VECTOR_DIM)));
109+
110+
client.insert(InsertReq.builder()
111+
.collectionName(COLLECTION_NAME)
112+
.data(Collections.singletonList(row))
113+
.build());
114+
System.out.println("Inserted geometry: " + geo);
115+
}
116+
117+
private static void printRowCount() {
118+
QueryResp countR = client.query(QueryReq.builder()
119+
.collectionName(COLLECTION_NAME)
120+
.outputFields(Collections.singletonList("count(*)"))
121+
.consistencyLevel(ConsistencyLevel.STRONG)
122+
.build());
123+
System.out.printf("%d rows persisted\n", (long)countR.getQueryResults().get(0).getEntity().get("count(*)"));
124+
}
125+
126+
private static void query(String filter) {
127+
System.out.println("===================================================");
128+
System.out.println("Query with filter expression: " + filter);
129+
QueryResp queryResp = client.query(QueryReq.builder()
130+
.collectionName(COLLECTION_NAME)
131+
.filter(filter)
132+
.consistencyLevel(ConsistencyLevel.BOUNDED)
133+
.outputFields(Collections.singletonList(GEO_FIELD))
134+
.build());
135+
List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
136+
System.out.println("Query results:");
137+
for (QueryResp.QueryResult result : queryResults) {
138+
System.out.println(result.getEntity());
139+
}
140+
}
141+
142+
public static void main(String[] args) {
143+
createCollection();
144+
insertGeometry("POINT (1 1)");
145+
insertGeometry("LINESTRING (10 10, 10 30, 40 40)");
146+
insertGeometry("POLYGON ((0 100, 100 100, 100 50, 0 50, 0 100))");
147+
printRowCount();
148+
149+
query("ST_EQUALS(" + GEO_FIELD + ", 'POINT (1 1)')");
150+
query("ST_TOUCHES(" + GEO_FIELD + ", 'LINESTRING (0 50, 0 100)')");
151+
query("ST_CONTAINS(" + GEO_FIELD + ", 'POINT (70 70)')");
152+
query("ST_CROSSES(" + GEO_FIELD + ", 'LINESTRING (20 0, 20 100)')");
153+
query("ST_WITHIN(" + GEO_FIELD + ", 'POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))')");
154+
}
155+
}

‎sdk-core/src/main/java/io/milvus/v2/common/IndexParam.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ public enum IndexType {
8888

8989
// Only for varchar type field
9090
TRIE("Trie", 100),
91+
92+
// Only for geometry type field
93+
RTREE("RTREE", 120),
94+
9195
// Only for scalar type field
9296
STL_SORT(200), // only for numeric type field
9397
INVERTED(201), // works for all scalar fields except JSON type field

‎sdk-core/src/test/java/io/milvus/TestUtils.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class TestUtils {
1111
private int dimension = 256;
1212
private static final Random RANDOM = new Random();
1313

14-
public static final String MilvusDockerImageID = "milvusdb/milvus:master-20250927-cc53b25b";
14+
public static final String MilvusDockerImageID = "milvusdb/milvus:master-20250929-ca1cc7c9-amd64";
1515

1616
public TestUtils(int dimension) {
1717
this.dimension = dimension;

‎sdk-core/src/test/java/io/milvus/v2/client/MilvusClientV2DockerTest.java‎

Lines changed: 60 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,10 @@ void testGeometry() {
12581258
.indexType(IndexParam.IndexType.HNSW)
12591259
.metricType(IndexParam.MetricType.COSINE)
12601260
.build());
1261+
indexParams.add(IndexParam.builder()
1262+
.fieldName(geoField)
1263+
.indexType(IndexParam.IndexType.RTREE)
1264+
.build());
12611265
client.createIndex(CreateIndexReq.builder()
12621266
.collectionName(randomCollectionName)
12631267
.indexParams(indexParams)
@@ -1276,62 +1280,62 @@ void testGeometry() {
12761280
Assertions.assertEquals(geoField, fields.get(2).getName());
12771281
Assertions.assertEquals(DataType.Geometry, fields.get(2).getDataType());
12781282

1279-
// // insert
1280-
// List<JsonObject> rows = new ArrayList<>();
1281-
// {
1282-
// JsonObject row = new JsonObject();
1283-
// row.addProperty(pkField, 1);
1284-
// row.addProperty(geoField, "POINT (1.0 -1.0)");
1285-
// row.add(vectorField, JsonUtils.toJsonTree(utils.generateFloatVector()));
1286-
// rows.add(row);
1287-
// }
1288-
// {
1289-
// JsonObject row = new JsonObject();
1290-
// row.addProperty(pkField, 2);
1291-
// row.addProperty(geoField, "POINT (2.0 2.0)");
1292-
// row.add(vectorField, JsonUtils.toJsonTree(utils.generateFloatVector()));
1293-
// rows.add(row);
1294-
// }
1295-
// InsertResp insertResp = client.insert(InsertReq.builder()
1296-
// .collectionName(randomCollectionName)
1297-
// .data(rows)
1298-
// .build());
1299-
// Assertions.assertEquals(rows.size(), insertResp.getInsertCnt());
1300-
//
1301-
// // query
1302-
// String filter = String.format("ST_WITHIN(%s, 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))')", geoField);
1303-
// QueryResp queryResp = client.query(QueryReq.builder()
1304-
// .collectionName(randomCollectionName)
1305-
// .limit(10)
1306-
// .filter(filter)
1307-
// .consistencyLevel(ConsistencyLevel.STRONG)
1308-
// .outputFields(Arrays.asList(pkField, geoField))
1309-
// .build());
1310-
// List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
1311-
// Assertions.assertEquals(1, queryResults.size());
1312-
// for (QueryResp.QueryResult res : queryResults) {
1313-
// Assertions.assertTrue(res.getEntity().containsKey(geoField));
1314-
// Assertions.assertEquals(res.getEntity().get(pkField), 2L);
1315-
// }
1316-
//
1317-
// // search
1318-
// SearchResp searchResp = client.search(SearchReq.builder()
1319-
// .collectionName(randomCollectionName)
1320-
// .annsField(vectorField)
1321-
// .data(Collections.singletonList(new FloatVec(utils.generateFloatVector())))
1322-
// .limit(10)
1323-
// .filter(filter)
1324-
// .outputFields(Arrays.asList(pkField, geoField))
1325-
// .build());
1326-
// List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
1327-
// Assertions.assertEquals(1, searchResults.size());
1328-
// for (List<SearchResp.SearchResult> oneResults : searchResults) {
1329-
// Assertions.assertEquals(1, oneResults.size());
1330-
// for (SearchResp.SearchResult res : oneResults) {
1331-
// Assertions.assertTrue(res.getEntity().containsKey(geoField));
1332-
// Assertions.assertEquals(res.getId(), 2L);
1333-
// }
1334-
// }
1283+
// insert
1284+
List<JsonObject> rows = new ArrayList<>();
1285+
{
1286+
JsonObject row = new JsonObject();
1287+
row.addProperty(pkField, 1);
1288+
row.addProperty(geoField, "POINT (1.0 -1.0)");
1289+
row.add(vectorField, JsonUtils.toJsonTree(utils.generateFloatVector()));
1290+
rows.add(row);
1291+
}
1292+
{
1293+
JsonObject row = new JsonObject();
1294+
row.addProperty(pkField, 2);
1295+
row.addProperty(geoField, "POINT (2.0 2.0)");
1296+
row.add(vectorField, JsonUtils.toJsonTree(utils.generateFloatVector()));
1297+
rows.add(row);
1298+
}
1299+
InsertResp insertResp = client.insert(InsertReq.builder()
1300+
.collectionName(randomCollectionName)
1301+
.data(rows)
1302+
.build());
1303+
Assertions.assertEquals(rows.size(), insertResp.getInsertCnt());
1304+
1305+
// query
1306+
String filter = String.format("ST_WITHIN(%s, 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))')", geoField);
1307+
QueryResp queryResp = client.query(QueryReq.builder()
1308+
.collectionName(randomCollectionName)
1309+
.limit(10)
1310+
.filter(filter)
1311+
.consistencyLevel(ConsistencyLevel.STRONG)
1312+
.outputFields(Arrays.asList(pkField, geoField))
1313+
.build());
1314+
List<QueryResp.QueryResult> queryResults = queryResp.getQueryResults();
1315+
Assertions.assertEquals(1, queryResults.size());
1316+
for (QueryResp.QueryResult res : queryResults) {
1317+
Assertions.assertTrue(res.getEntity().containsKey(geoField));
1318+
Assertions.assertEquals(res.getEntity().get(pkField), 2L);
1319+
}
1320+
1321+
// search
1322+
SearchResp searchResp = client.search(SearchReq.builder()
1323+
.collectionName(randomCollectionName)
1324+
.annsField(vectorField)
1325+
.data(Collections.singletonList(new FloatVec(utils.generateFloatVector())))
1326+
.limit(10)
1327+
.filter(filter)
1328+
.outputFields(Arrays.asList(pkField, geoField))
1329+
.build());
1330+
List<List<SearchResp.SearchResult>> searchResults = searchResp.getSearchResults();
1331+
Assertions.assertEquals(1, searchResults.size());
1332+
for (List<SearchResp.SearchResult> oneResults : searchResults) {
1333+
Assertions.assertEquals(1, oneResults.size());
1334+
for (SearchResp.SearchResult res : oneResults) {
1335+
Assertions.assertTrue(res.getEntity().containsKey(geoField));
1336+
Assertions.assertEquals(res.getId(), 2L);
1337+
}
1338+
}
13351339
}
13361340

13371341
@Test

0 commit comments

Comments
 (0)