Below is a MongoDB Atlas Search index and $search query sample for doing vector search inside the $search stage with a geo bounding filter.
Important: because you want a geo filter such as geoWithin, use Atlas Search $search.vectorSearch, not the standalone $vectorSearch aggregation stage.
Example index name: places_vector_geo_idx
json
{
"mappings": {
"dynamic": false,
"fields": {
"embedding": {
"type": "vector",
"numDimensions": 1536,
"similarity": "cosine"
},
"location": {
"type": "geo"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
Assumed document shape:
js
{
_id: ObjectId("..."),
title: "Some Place",
description: "A description of the place",
embedding: [/ 1536-dimension vector /],
location: {
type: "Point",
coordinates: [-73.9857, 40.7484] // [longitude, latitude]
}
}
MongoDB GeoJSON coordinates are always:
js
[longitude, latitude]
Use this when the user provides a rectangular area, usually as:
js
const queryVector = [
/ embedding generated from the user's query text /
];
const bottomLeftLongitude = -74.10;
const bottomLeftLatitude = 40.60;
const topRightLongitude = -73.70;
const topRightLatitude = 40.90;
db.places.aggregate([
{
$search: {
index: "places_vector_geo_idx",
vectorSearch: {
path: "embedding",
queryVector: queryVector,
numCandidates: 200,
limit: 10,
filter: {
geoWithin: {
path: "location",
box: {
bottomLeft: {
type: "Point",
coordinates: [
bottomLeftLongitude,
bottomLeftLatitude
]
},
topRight: {
type: "Point",
coordinates: [
topRightLongitude,
topRightLatitude
]
}
}
}
}
}
}
},
{
$project: {
title: 1,
description: 1,
location: 1,
score: {
$meta: "searchScore"
}
}
}
]);
If the user will provide exact lat/long values, structure it like this:
js
const queryVector = [/ generated query embedding /];
// User-provided bounding area
const southwest = {
lat: USER_PROVIDED_SW_LAT,
lng: USER_PROVIDED_SW_LNG
};
const northeast = {
lat: USER_PROVIDED_NE_LAT,
lng: USER_PROVIDED_NE_LNG
};
db.places.aggregate([
{
$search: {
index: "places_vector_geo_idx",
vectorSearch: {
path: "embedding",
queryVector: queryVector,
numCandidates: 200,
limit: 10,
filter: {
geoWithin: {
path: "location",
box: {
bottomLeft: {
type: "Point",
coordinates: [southwest.lng, southwest.lat]
},
topRight: {
type: "Point",
coordinates: [northeast.lng, northeast.lat]
}
}
}
}
}
}
},
{
$project: {
title: 1,
location: 1,
score: {
$meta: "searchScore"
}
}
}
]);
If the user gives a center point and radius instead of a bounding box:
js
const queryVector = [/ generated query embedding /];
const centerLatitude = 40.7484;
const centerLongitude = -73.9857;
const radiusMeters = 5000;
db.places.aggregate([
{
$search: {
index: "places_vector_geo_idx",
vectorSearch: {
path: "embedding",
queryVector: queryVector,
numCandidates: 200,
limit: 10,
filter: {
geoWithin: {
path: "location",
circle: {
center: {
type: "Point",
coordinates: [centerLongitude, centerLatitude]
},
radius: radiusMeters
}
}
}
}
}
},
{
$project: {
title: 1,
location: 1,
score: {
$meta: "searchScore"
}
}
}
]);
js
[longitude, latitude]
js
{ $meta: "searchScore" }
not:
js
{ $meta: "vectorSearchScore" }