2013-03-14 39 views
5

Tôi đang sử dụng mongoose.js để thực hiện truy vấn đến mongodb, nhưng tôi nghĩ rằng sự cố của tôi không cụ thể đối với mongoose.js.MongoDB: chọn các phần tử phù hợp của phần phụ

Nói rằng tôi chỉ có một kỷ lục trong bộ sưu tập:

var album = new Album({ 
    tracks: [{ 
     title: 'track0', 
     language: 'en', 

    },{ 
     title: 'track1', 
     language: 'en', 
    },{ 
     title: 'track2', 
     language: 'es', 
    }] 
}) 

Tôi muốn chọn tất cả các bài hát với lĩnh vực ngôn ngữ bằng 'en', vì vậy tôi cố gắng hai biến thể:

Album.find({'tracks.language':'en'}, {'tracks.$': 1}, function(err, albums){ 

và gắn với cùng một điều với phép chiếu $ elemMatch:

Album.find({}, {tracks: {$elemMatch: {'language': 'en'}}}, function(err, albums){ 

trong cả hai trường hợp, tôi có cùng kết quả:

{tracks:[{title: 'track0', language: 'en'}]} 

album.tracks chọn chỉ chứa ONE yếu tố theo dõi với tiêu đề 'track0' (nhưng không nên có cả hai 'track0', 'track1'):

{tracks:[{title: 'track0', language: 'en'}, {title: 'track1', language: 'en'}]} 

Tôi đang làm gì sai?

+4

Bạn sẽ không làm bất cứ điều gì sai, đó chỉ là cách những truy vấn làm việc - họ chỉ bao gồm các yếu tố phù hợp đầu tiên. Tôi nghĩ bạn sẽ cần phải sử dụng khung tổng hợp để làm những gì bạn muốn. – JohnnyHK

+0

Cảm ơn, bạn có thể đưa ra mẹo cụ thể hơn của tôi không? – WHITECOLOR

Trả lời

11

Giống như @JohnnyHK đã nói, bạn sẽ phải sử dụng khung tổng hợp để thực hiện điều đó vì cả hai $$elemMatch chỉ trả về kết quả trùng khớp đầu tiên.

Đây là cách:

db.Album.aggregate(
    // This is optional. It might make your query faster if you have 
    // many albums that don't have any English tracks. Take a larger 
    // collection and measure the difference. YMMV. 
    { $match: {tracks: {$elemMatch: {'language': 'en'}} } }, 

    // This will create an 'intermediate' document for each track 
    { $unwind : "$tracks" }, 

    // Now filter out the documents that don't contain an English track 
    // Note: at this point, documents' 'tracks' element is not an array 
    { $match: { "tracks.language" : "en" } }, 

    // Re-group so the output documents have the same structure, ie. 
    // make tracks a subdocument/array again 
    { $group : { _id : "$_id", tracks : { $addToSet : "$tracks" } }} 
); 

Bạn có thể muốn thử truy vấn tổng hợp chỉ với những biểu hiện đầu tiên và sau đó thêm các biểu thức từng dòng để xem cách các đầu ra được thay đổi. Điều đặc biệt quan trọng là hiểu cách $unwind tạo tài liệu trung gian sau này được hợp nhất lại bằng cách sử dụng $group$addToSet.

Kết quả:

> db.Album.aggregate(
    { $match: {tracks: {$elemMatch: {'language': 'en'}} } }, 
    { $unwind : "$tracks" }, 
    { $match: { "tracks.language" : "en" } }, 
    { $group : { _id : "$_id", tracks : { $addToSet : "$tracks" } }} ); 
{ 
    "result" : [ 
      { 
        "_id" : ObjectId("514217b1c99766f4d210c20b"), 
        "tracks" : [ 
          { 
            "title" : "track1", 
            "language" : "en" 
          }, 
          { 
            "title" : "track0", 
            "language" : "en" 
          } 
        ] 
      } 
    ], 
    "ok" : 1 
} 
+0

Cảm ơn bạn rất nhiều! – WHITECOLOR

+0

Một câu hỏi liên quan khác nếu bạn không nhớ cách hạn chế các bản nhạc đã chọn để có trường tiêu đề olny (không có trường ngôn ngữ khác). – WHITECOLOR

+0

@mnemosyn Tôi đã thử giải pháp này về vấn đề liên quan của tôi và nó đã hoạt động. Bây giờ tôi phải quay lại và hiểu nó! – carbontax

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