2010-11-08 72 views
42

Tôi có một tài liệu MongoDB với 2 mức mảng lồng nhau sâu sắc về đối tượng mà tôi cần phải cập nhật, một cái gì đó như thế này:Đang cập nhật mảng lồng trong MongoDB

{ 
    id: 1, 
    items: [ 
     { 
      id: 2, 
      blocks: [ 
       { 
        id: 3 
        txt: 'hello' 
       } 
      ] 
     } 
    ] 
} 

Nếu chỉ có một mảng sâu cấp I có thể sử dụng khai thác vị trí để cập nhật các đối tượng trong nó nhưng đối với mức độ thứ hai lựa chọn duy nhất tôi đã đưa ra là sử dụng toán tử vị trí với chỉ số lồng nhau của đối tượng, như thế này:

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}}) 

cách tiếp cận này hoạt động nhưng có vẻ như nguy hiểm đối với tôi kể từ khi tôi 'xây dựng một dịch vụ web và số chỉ số sẽ đến từ clie nt có thể gửi 100000 là chỉ mục và điều này sẽ buộc mongodb tạo một mảng với 100000 chỉ mục có giá trị null. Có bất kỳ cách nào khác để cập nhật các đối tượng lồng nhau trong đó tôi có thể tham chiếu đến ID của đối tượng thay vì vị trí của nó hay có thể là cách kiểm tra xem chỉ mục được cung cấp có vượt quá giới hạn trước khi sử dụng nó trong truy vấn không? Không.

+4

Tôi khuyên bạn nên truy cập lại lược đồ này và tìm thiết kế khác để bạn có thể tận dụng sức mạnh mà MongoDB cung cấp. Sẽ không có cách nào dễ dàng/siêu hiệu quả để cập nhật một mục cụ thể trong mảng AFAIK. Bạn có thể thiết kế lại nó để bạn có thể tận dụng 'addToSet',' pop' và các toán tử mảng khác không? – brianz

+0

Cảm ơn bạn đã đề xuất, vâng tôi có thể và trên thực tế tôi đã làm điều đó. Đặt câu hỏi này tôi chỉ muốn chắc chắn rằng tôi không thiếu gì cả. – Anton

+2

Tôi cũng phải đối mặt với cùng một vấn đề. Bạn có thể đăng lược đồ được thiết kế lại mẫu của mình không? – Damodaran

Trả lời

26

Đây là câu hỏi lớn, bạn có cần tận dụng các hoạt động "addToSet" và "push" của Mongo không? Nếu bạn thực sự có kế hoạch sửa đổi các mục riêng lẻ trong mảng, thì có lẽ bạn nên xây dựng các mảng này làm đối tượng.

Đây là cách tôi sẽ cấu trúc này:

{ 
    id: 1, 
    items: 
     { 
      "2" : { "blocks" : { "3" : { txt : 'hello' } } }, 
      "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } } 
     } 
} 

này về cơ bản biến đổi tất cả mọi thứ để JSON đối tượng thay vì mảng. Bạn mất khả năng sử dụng $push$addToSet nhưng tôi nghĩ điều này giúp mọi thứ trở nên dễ dàng hơn. Ví dụ, truy vấn của bạn sẽ trông như thế này:

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

Bạn cũng sẽ nhận thấy rằng tôi đã đổ các "ID". Khi bạn đang lồng ghép những thứ như thế này, thông thường bạn có thể thay thế "ID" bằng cách sử dụng số đó làm chỉ mục. Khái niệm "ID" hiện được ngụ ý.

Tính năng này đã được thêm vào 3.6 với expressive updates.

db.objects.update({id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] })

+0

cách thức hoạt động của nó là một mảng. – kapad

+0

nếu 'mục' là một mảng, lệnh' $ set' đó không hoạt động. Bạn sẽ cần phải cấu trúc dữ liệu khác nhau và tận dụng '$ addToSet'. –

+1

Tôi đang tìm kiếm một giải pháp sử dụng '$'. Một cái gì đó giống như 'mục. $' Mà sẽ lặp qua tất cả các yếu tố của mảng mục và thực hiện thay đổi mà tôi muốn. Bất kỳ ý tưởng nào cú pháp sẽ cho một cái gì đó như thế này. Thậm chí nó có thể được thực hiện? – kapad

3

Các id mà bạn đang sử dụng là số tuyến tính và nó phải đến từ nơi nào đó như một trường bổ sung như 'max_idx' hoặc cái gì đó tương tự. Điều này có nghĩa là một tra cứu cho id và sau đó cập nhật. UUID/ObjectId có thể được sử dụng cho các id sẽ đảm bảo rằng bạn có thể sử dụng CRUD phân tán.

Các vấn đề liên quan