Khắc phục lỗi MongoDB: '$near requires a 2d or 2dsphere index'

intermediate🍃 MongoDB2026-06-24| MongoDB 3.6+, Node.js (Mongoose), Python (PyMongo), MongoDB Compass, Linux/macOS/Windows

Error Message

planner returned error :: internal error: $near requires a 2d or 2dsphere index
#mongodb#geospatial#index#2dsphere#mongoose

Tình huống lỗiGần đây tôi có xây dựng tính năng 'tìm quán cà phê gần đây' cho một khách hàng. Mọi thứ trên lý thuyết đều hoàn hảo. Tôi đã lưu trữ các đối tượng GeoJSON và chuẩn bị sẵn toán tử $near. Nhưng ngay khi tôi thử nghiệm tìm kiếm trên một collection gồm 50.000 địa điểm, cơ sở dữ liệu đã báo lỗi:

MongoServerError: planner returned error :: internal error: $near requires a 2d or 2dsphere index

Không giống như các tìm kiếm thông thường nơi MongoDB có thể chấp nhận quét toàn bộ collection (collection scan) chậm chạp, các truy vấn không gian (geospatial) thì khác. MongoDB từ chối thực hiện truy vấn $near hoặc $nearSphere trừ khi có một index chuyên dụng để hỗ trợ. Nó thậm chí sẽ không thử dự đoán.

Tại sao MongoDB yêu cầu IndexViệc tính toán khoảng cách giữa hai điểm trên 10 triệu tài liệu cực kỳ tiêu tốn tài nguyên CPU. Để bảo vệ hiệu suất, MongoDB bắt buộc phải có index 2d cho các mặt phẳng Euclidean hoặc index 2dsphere cho hình học dạng bề mặt Trái Đất. Nếu không có, bộ lập kế hoạch truy vấn (query planner) sẽ dừng ngay lập tức thay vì mạo hiểm làm chậm toàn bộ cơ sở dữ liệu.

Bạn có thể đang gặp phải vấn đề này vì:

  • Index chưa từng được tạo trên cơ sở dữ liệu production.- Collection bị xóa và tạo lại, làm mất index.- Bạn đang truy vấn trên trường location, nhưng index thực tế lại nằm trên address.location.- Trong Mongoose, autoIndex bị tắt, nên các định nghĩa schema của bạn không được gửi đến server.## Cách khắc phục ngay lập tức: Sử dụng ShellNếu bạn cần đưa ứng dụng hoạt động trở lại ngay lập tức, hãy tạo index thủ công bằng MongoDB shell (mongosh) hoặc MongoDB Compass. Đối với dữ liệu GeoJSON được lưu trong trường có tên là location, hãy chạy lệnh sau:
db.places.createIndex({ location: "2dsphere" })

Đối với các cặp tọa độ cũ như [longitude, latitude], index 2d sẽ hoạt động. Tuy nhiên, 2dsphere là tiêu chuẩn vàng cho bản đồ web hiện đại vì nó tính đến độ cong của Trái Đất.

Cách khắc phục lâu dài: Tự động hóa Index trong mã nguồnCác bản sửa lỗi thủ công rất tốt cho trường hợp khẩn cấp, nhưng mã nguồn của bạn nên xử lý việc tạo index để giữ cho các môi trường Dev, Staging và Production luôn đồng bộ.

1. Sử dụng Mongoose (Node.js)Trong Mongoose, hãy định nghĩa index trực tiếp trong schema của bạn. Đảm bảo trường của bạn được định kiểu là một đối tượng GeoJSON.

const placeSchema = new mongoose.Schema({
  name: String,
  location: {
    type: {
      type: String,
      enum: ['Point'],
      required: true
    },
    coordinates: {
      type: [Number],
      required: true
    }
  }
});

// Định nghĩa rõ ràng index 2dsphere
placeSchema.index({ location: "2dsphere" });

const Place = mongoose.model('Place', placeSchema);

Mẹo chuyên nghiệp: Nếu bạn đặt autoIndex: false trong chuỗi kết nối để cải thiện hiệu suất, Mongoose sẽ không tự động xây dựng index này. Bạn phải gọi await Place.createIndexes() trong chuỗi khởi động ứng dụng.

2. Sử dụng PyMongo (Python)Các nhà phát triển Python có thể đảm bảo index tồn tại trước khi truy vấn đầu tiên được thực thi:

from pymongo import MongoClient, GEOSPHERE

client = MongoClient('mongodb://localhost:27017/')
db = client['store_locator']
collection = db['branches']

# Đảm bảo index tồn tại; không làm gì nếu nó đã có sẵn
collection.create_index([("location", GEOSPHERE)])

Sử dụng Compound Index (Index hỗn hợp)Nếu bạn muốn tìm "nhà hàng Sushi" gần một điểm cụ thể thì sao? Một index không gian đơn giản là không đủ cho các ứng dụng có lưu lượng truy cập cao. Bạn cần một compound index. Để các index này hoạt động, trường không gian phải là khóa đầu tiên trong index.

db.places.createIndex({ location: "2dsphere", category: 1 })

Cấu trúc này cho phép MongoDB thu hẹp khu vực địa lý trước khi lọc theo danh mục.

Xác minh: Đã hoạt động chưa?Đừng coi việc không có lỗi là bằng chứng. Hãy xác minh index đang hoạt động bằng cách chạy:

db.places.getIndexes()

Tìm mục "key": { "location": "2dsphere" }. Để xem nó hoạt động thực tế, hãy thêm .explain("executionStats") vào truy vấn của bạn. Nếu winningPlan hiển thị giai đoạn GEO_NEAR_2DSPHERE, nghĩa là bạn đã sử dụng index thành công và truy vấn đã được tối ưu hóa.

Các lỗi thường gặp- Quy tắc Kinh độ trước (Longitude-First): MongoDB yêu cầu định dạng [longitude, latitude]. Nếu bạn truyền vào [40.7128, -74.0060] (New York theo thứ tự Vĩ độ/Kinh độ), truy vấn sẽ thất bại hoặc trả về không có kết quả.- Dữ liệu sai: Nếu một tài liệu có vĩ độ là 120 (tối đa là 90), việc tạo index sẽ thất bại với lỗi 'location object expected'. Hãy làm sạch dữ liệu trước.- Collection lớn: Việc xây dựng index trên 5 triệu bản ghi có thể làm khóa cơ sở dữ liệu. Sử dụng { background: true } để giữ cho ứng dụng của bạn vẫn phản hồi trong quá trình tạo index.

Related Error Notes