Milvus
Zilliz
Home
  • User Guide
  • Home
  • Docs
  • User Guide

  • Schema & Data Fields

  • Geometry Field

Geometry FieldCompatible with Milvus 2.6.4+

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”

What is a GEOMETRY field?

A GEOMETRY field is a schema-defined data type (DataType.GEOMETRY) in Milvus 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, Milvus 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("http://localhost:19530")

# 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")

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)

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 the GEOMETRY field, use a geometry-specific operator with the following expression format: "{operator}(geo_field,'{wkt}')", where:

  • {operator} is a supported geometry operator (e.g., ST_CONTAINS, ST_INTERSECTS). For a full list of available operators, refer to Geometry Operators.

  • geo_field is the name of the GEOMETRY field defined in your collection schema.

  • '{wkt}' is the WKT string representing the geometry object you are filtering on.

Some operators, such as ST_DWITHIN, may require additional parameters. For details and usage examples of each operator, refer to Geometry Operators.

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)

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)

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}")

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 RTREE index on your GEOMETRY field.

For details, refer to RTREE.

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.