Skip to main content
Version: User Guides (Cloud)

Geometry Field
Public Preview

When building applications like Geographic Information Systems (GIS), mapping tools, or location-based services, you often need to store and query geometric data. The GEOMETRY data type in Milvus solves this challenge by providing a native way to store and query flexible geometric data.

Use a GEOMETRY field when you need to combine vector similarity with spatial constraints, for example:

  • Location-Base Service (LBS): "find similar POIs within this city block"

  • Multi‑modal search: "retrieve similar photos within 1km of this point"

  • Maps & logistics: "assets inside a region" or "routes intersecting a path"

📘Notes

To use the GEOMETRY field, upgrade your SDK to the latest version.

What is a GEOMETRY field?

A GEOMETRY field is a schema-defined data type (DataType.GEOMETRY) in Zilliz Cloud that stores geometric data. When working with geometry fields, you interact with the data using the Well-Known Text (WKT) format, a human-readable representation used for both inserting data and querying. Internally, Zilliz Cloud converts WKT to Well-Known Binary (WKB) for efficient storage and processing, but you do not need to handle WKB directly.

The GEOMETRY data type supports the following geometric objects:

  • POINT: POINT (x y); for example, POINT (13.403683 52.520711) where x = longitude and y = latitude

  • LINESTRING: LINESTRING (x1 y1, x2 y2, …); for example, LINESTRING (13.40 52.52, 13.41 52.51)

  • POLYGON: POLYGON ((x1 y1, x2 y2, x3 y3, x1 y1)); for example, POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))

  • MULTIPOINT: MULTIPOINT ((x1 y1), (x2 y2), …), for example, MULTIPOINT ((10 40), (40 30), (20 20), (30 10))

  • MULTILINESTRING: MULTILINESTRING ((x1 y1, …), (xk yk, …)), for example, MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))

  • MULTIPOLYGON: MULTIPOLYGON (((outer ring ...)), ((outer ring ...))), for example, MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))

  • GEOMETRYCOLLECTION: GEOMETRYCOLLECTION(POINT(x y), LINESTRING(x1 y1, x2 y2), ...), for example, GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)))

Basic operations

The workflow for using a GEOMETRY field involves defining it in your collection schema, inserting geometric data, and then querying the data using specific filter expressions.

Step 1: Define a GEOMETRY field

To use a GEOMETRY field, explicitly define it in your collection schema when creating the collection. The following example demonstrates how to create a collection with a geo field of type DataType.GEOMETRY.

from pymilvus import MilvusClient, DataType
import numpy as np

dim = 8
collection_name = "geo_collection"
milvus_client = MilvusClient("YOUR_CLUSTER_ENDPOINT")

# Create schema with a GEOMETRY field
schema = milvus_client.create_schema(enable_dynamic_field=True)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("embeddings", DataType.FLOAT_VECTOR, dim=dim)
schema.add_field("geo", DataType.GEOMETRY, nullable=True)
schema.add_field("name", DataType.VARCHAR, max_length=128)

milvus_client.create_collection(collection_name, schema=schema, consistency_level="Strong")
📘Notes

In this example, the GEOMETRY field defined in the collection schema allows null values with nullable=True. For details, refer to Nullable & Default.

Step 2: Insert data

Insert entities with geometry data in WKT format. Here’s an example with several geo points:

rng = np.random.default_rng(seed=19530)
geo_points = [
'POINT(13.399710 52.518010)',
'POINT(13.403934 52.522877)',
'POINT(13.405088 52.521124)',
'POINT(13.408223 52.516876)',
'POINT(13.400092 52.521507)',
'POINT(13.408529 52.519274)',
]

rows = [
{"id": 1, "name": "Shop A", "embeddings": rng.random((1, dim))[0], "geo": geo_points[0]},
{"id": 2, "name": "Shop B", "embeddings": rng.random((1, dim))[0], "geo": geo_points[1]},
{"id": 3, "name": "Shop C", "embeddings": rng.random((1, dim))[0], "geo": geo_points[2]},
{"id": 4, "name": "Shop D", "embeddings": rng.random((1, dim))[0], "geo": geo_points[3]},
{"id": 5, "name": "Shop E", "embeddings": rng.random((1, dim))[0], "geo": geo_points[4]},
{"id": 6, "name": "Shop F", "embeddings": rng.random((1, dim))[0], "geo": geo_points[5]},
]

insert_result = milvus_client.insert(collection_name, rows)
print(insert_result)

# Expected output:
# {'insert_count': 6, 'ids': [1, 2, 3, 4, 5, 6]}

Step 3: Filtering operations

Before you can perform filtering operations on GEOMETRY fields, make sure:

  • You have created an index on each vector field.

  • The collection is loaded into memory.

Show code
index_params = milvus_client.prepare_index_params()
index_params.add_index(field_name="embeddings", metric_type="L2")

milvus_client.create_index(collection_name, index_params)
milvus_client.load_collection(collection_name)

Once these requirements are met, you can use expressions with dedicated geometry operators to filter your collection based on the geometric values.

Define filter expressions

To filter on a GEOMETRY field, use a geometry operator in an expression:

  • General: {operator}(geo_field, '{wkt}')

  • Distance-based: ST_DWITHIN(geo_field, '{wkt}', distance)

Where:

  • operator is one of the supported geometry operators (e.g., ST_CONTAINS, ST_INTERSECTS). Operator names must be all uppercase or all lowercase. For a list of supported operators, refer to Supported geometry operators.

  • geo_field is the name of your GEOMETRY field.

  • '{wkt}' is the WKT representation of the geometry to query.

  • distance is the threshold specifically for ST_DWITHIN.

The following examples demonstrate how to use different geometry-specific operators in a filter expression:

Example 1: Find entities within a rectangular area

top_left_lon, top_left_lat = 13.403683, 52.520711
bottom_right_lon, bottom_right_lat = 13.455868, 52.495862
bounding_box_wkt = f"POLYGON(({top_left_lon} {top_left_lat}, {bottom_right_lon} {top_left_lat}, {bottom_right_lon} {bottom_right_lat}, {top_left_lon} {bottom_right_lat}, {top_left_lon} {top_left_lat}))"

query_results = milvus_client.query(
collection_name,
filter=f"st_within(geo, '{bounding_box_wkt}')",
output_fields=["name", "geo"]
)
for ret in query_results:
print(ret)

# Expected output:
# {'name': 'Shop D', 'geo': 'POINT (13.408223 52.516876)', 'id': 4}
# {'name': 'Shop F', 'geo': 'POINT (13.408529 52.519274)', 'id': 6}
# {'name': 'Shop A', 'geo': 'POINT (13.39971 52.51801)', 'id': 1}
# {'name': 'Shop B', 'geo': 'POINT (13.403934 52.522877)', 'id': 2}
# {'name': 'Shop C', 'geo': 'POINT (13.405088 52.521124)', 'id': 3}
# {'name': 'Shop D', 'geo': 'POINT (13.408223 52.516876)', 'id': 4}
# {'name': 'Shop E', 'geo': 'POINT (13.400092 52.521507)', 'id': 5}
# {'name': 'Shop F', 'geo': 'POINT (13.408529 52.519274)', 'id': 6}

Example 2: Find entities within 1km of a central point

center_point_lon, center_point_lat = 13.403683, 52.520711
radius_meters = 1000.0
central_point_wkt = f"POINT({center_point_lon} {center_point_lat})"

query_results = milvus_client.query(
collection_name,
filter=f"st_dwithin(geo, '{central_point_wkt}', {radius_meters})",
output_fields=["name", "geo"]
)
for ret in query_results:
print(ret)

# Expected output:
# hit: {'id': 4, 'distance': 0.9823770523071289, 'entity': {'name': 'Shop D', 'geo': 'POINT (13.408223 52.516876)'}}

Example 3: Combine vector similarity with a spatial filter

vectors_to_search = rng.random((1, dim))
result = milvus_client.search(
collection_name,
vectors_to_search,
limit=3,
output_fields=["name", "geo"],
filter=f"st_within(geo, '{bounding_box_wkt}')"
)
for hits in result:
for hit in hits:
print(f"hit: {hit}")

# Expected output:
# hit: {'id': 6, 'distance': 1.3406795263290405, 'entity': {'name': 'Shop F', 'geo': 'POINT (13.408529 52.519274)'}}

Next: Accelerate queries

By default, queries on GEOMETRY fields without an index will perform a full scan of all rows, which can be slow on large datasets. To accelerate geometric queries, create an AUTOINDEX index on your GEOMETRY field.

For details, refer to Index Scalar Fields.

FAQ

If I've enabled the dynamic field feature for my collection, can I insert geometric data into a dynamic field key?

No, geometry data cannot be inserted into a dynamic field. Before inserting geometric data, make sure the GEOMETRY field has been explicitly defined in your collection schema.

Does the GEOMETRY field support the mmap feature?

Yes, the GEOMETRY field supports mmap. For more information, refer to Use mmap.

Can I define the GEOMETRY field as nullable or set a default value?

Yes, the GEOMETRY field supports the nullable attribute and a default value in WKT format. For more information, refer to Nullable & Default.