Khắc phục lỗi MongoDB: $unwind: value at end of field path was not an array

intermediate🍃 MongoDB2026-06-12| MongoDB 3.2+, MongoDB Atlas, trình điều khiển Node.js/Python/Go, mọi hệ điều hành (Linux, Windows, macOS)

Error Message

PlanExecutor error during aggregation :: caused by :: $unwind: value at end of field path ... was not an array
#mongodb#aggregation#unwind#lỗi-cơ-sở-dữ-liệu#nosql

Vấn đềBạn đang xây dựng một aggregation pipeline trong MongoDB, mọi thứ dường như ổn, và rồi—lỗi xảy ra. Giai đoạn $unwind, vốn có nhiệm vụ làm phẳng các mảng thành các tài liệu riêng lẻ, gặp sự cố. Thay vì dữ liệu sạch như mong đợi, bạn nhận được thông báo lỗi gây khó chịu này:

PlanExecutor error during aggregation :: caused by :: $unwind: value at end of field path 'yourFieldName' was not an array

Điều này xảy ra vì $unwind rất khắt khe. Nó được thiết kế riêng để phân rã các mảng. Nếu pipeline của bạn gặp dù chỉ một tài liệu mà trường đó là chuỗi, số nguyên hoặc kiểu logic (boolean), toàn bộ hoạt động sẽ bị dừng lại.

Nguyên nhân gốc rễNguyên nhân hầu như luôn là do dữ liệu không nhất quán. Mặc dù tính chất không ràng buộc schema (schema-less) của MongoDB rất linh hoạt, nó có thể dẫn đến dữ liệu "bẩn" nếu logic ứng dụng của bạn không chặt chẽ. Bạn có thể gặp phải lỗi này nếu:

  • Một script cũ đã lưu một thẻ (tag) dưới dạng chuỗi ("marketing") thay vì một mảng (["marketing"]).- Một lỗi ở frontend cho phép người dùng gửi một ID số duy nhất thay vì một danh sách các ID.- Dữ liệu được nhập từ CSV hoặc API bên ngoài không bắt buộc kiểu mảng cho các mục đơn lẻ.## Cách khắc phục lỗi### Cách 1: Bộ lọc nhanhNếu bạn chỉ cần xử lý các tài liệu đã chứa mảng hợp lệ, cách sửa lỗi dễ nhất là thêm một giai đoạn $match ngay trước $unwind. Giai đoạn này đóng vai trò như một cổng an toàn, đảm bảo chỉ dữ liệu tương thích mới được đi tiếp.
db.collection.aggregate([
  {
    $match: {
      "yourFieldName": { $type: "array" }
    }
  },
  {
    $unwind: "$yourFieldName"
  }
])

Bằng cách sử dụng $type: "array", bạn sẽ bỏ qua hiệu quả mọi tài liệu mà trường đó là chuỗi, số hoặc null. Điều này giúp pipeline của bạn chạy trơn tru mà không bị crash do các kiểu dữ liệu không mong muốn.

Cách 2: Chuyển đổi trực tiếpĐôi khi bạn không thể bỏ qua dữ liệu chỉ vì nó định dạng kém. Nếu bạn cần xử lý mọi tài liệu—bất kể trường đó là một giá trị đơn hay một mảng—hãy sử dụng $addFields để bọc các giá trị đơn đó vào một mảng trước khi đến giai đoạn $unwind.

db.collection.aggregate([
  {
    $addFields: {
      "yourFieldName": {
        $cond: {
          if: { $isArray: "$yourFieldName" },
          then: "$yourFieldName",
          else: { 
            $ifNull: [ ["$yourFieldName"], [] ] 
          }
        }
      }
    }
  },
  {
    $unwind: "$yourFieldName"
  }
])

Logic này kiểm tra kiểu của trường. Nếu nó đã là một mảng, nó sẽ được giữ nguyên. Nếu nó là một giá trị đơn (như chuỗi "urgent"), nó sẽ được bọc trong dấu ngoặc vuông để trở thành ["urgent"], giúp nó hoàn toàn hợp lệ để unwind.

Cách 3: Khắc phục vĩnh viễn (Dọn dẹp dữ liệu)Các giải pháp tình thế trong truy vấn rất hữu ích, nhưng dọn dẹp cơ sở dữ liệu mới là giải pháp lâu dài đáng tin cậy nhất. Bạn có thể chạy một script di chuyển dữ liệu một lần để tìm và chuyển đổi tất cả các giá trị không phải mảng trong collection của mình.

// Cập nhật tất cả tài liệu có trường tồn tại nhưng không phải là mảng
db.collection.find({
  "yourFieldName": { $exists: true, $not: { $type: "array" } }
}).forEach(function(doc) {
  db.collection.updateOne(
    { _id: doc._id },
    { $set: { "yourFieldName": [doc.yourFieldName] } }
  );
});

Các bước xác minhĐừng chỉ giả định rằng việc sửa lỗi đã thành công. Sau khi áp dụng các thay đổi, hãy chạy một bước kiểm tra nhanh để đảm bảo dữ liệu của bạn đã sạch. Sử dụng countDocuments để tìm các giá trị ngoại lệ:

db.collection.countDocuments({ 
  "yourFieldName": { $exists: true, $not: { $type: "array" } } 
})

Nếu kết quả là 0, giai đoạn $unwind của bạn đã an toàn. Nếu bạn thấy con số hàng trăm hoặc hàng nghìn, bạn vẫn còn dữ liệu không nhất quán cần được xử lý.

Mẹo phòng ngừa- Schema Validation: Sử dụng MongoDB JSON Schema validation để bắt buộc một trường phải là mảng. Điều này ngăn chặn dữ liệu xấu xâm nhập vào collection của bạn.- Mongoose Schemas: Nếu bạn đang sử dụng Node.js, hãy định nghĩa trường của bạn là tags: [String]. Mongoose sẽ tự động cố gắng ép kiểu các giá trị thành mảng cho bạn.- Defensive Aggregation: Hãy coi dữ liệu của bạn luôn có thể bị lỗi. Sử dụng kiểm tra $isArray trong các pipeline giúp ứng dụng của bạn linh hoạt hơn nhiều trước các trạng thái dữ liệu không mong muốn.

Related Error Notes