2015-11-08 81 views
0

嵌入對象值我有這樣一個模式:排序MongoDB中

{ 
    tags:[ 
     { 
     id:"t1", 
     score:70 
     }, 
     { 
     id:"t1", 
     score:60 
     } 
    ] 
} 

我想在tag.id搜索查詢排序得到由相應的分數排序。因此,如果我搜索db.collection.find({tags.id:「t1」})。sort({tags.score:-1}),它會根據「t1」對象的評分而不是其他標籤進行排序。

什麼建議嗎?

+0

如果您的數組樣本包含的其他值不只是「t1」,並且可能包含多個文檔和預期結果,可能會有更清晰的問題。但我想我明白你的意思了。 –

回答

1

如果您需要計算出在運行時是這樣的,從陣列確定排序順序「過濾」的內容,那麼你最好做.aggregate()東西重塑和確定排序值是這樣的:

db.collection.aggregate([ 
    // Pre-filter the array elements 
    { "$project": { 
     "tags": 1, 
     "score": { 
      "$setDifference": [ 
       { "$map": { 
        "input": "$tags", 
        "as": "tag", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$el.id", "t1" ] }, 
          "$$el.score", 
          false 
         ] 
        } 
       }}, 
       [false] 
      ] 
     } 
    }}, 
    // Unwind to denormalize 
    { "$unwind": "$score" }, 
    // Group back the "max" score 
    { "$group": { 
     "_id": "$_id", 
     "tags": { "$first": "$tags" }, 
     "score": { "$max": "$score" } 
    }}, 
    // Sort descending by score 
    { "$sort": { "score": -1 } } 
]) 

凡管道的第一部分是用來「預過濾器」數組內容(以及保持原始字段)到只是這些價值其中id等於「t1」的「分數」。這通過處理$map來完成,其通過$cond向每個元素應用條件以確定是否返回該元素的「分數」或false

$setDifference操作與單個元素數組[false]進行比較,該數組有效地移除從$map返回的任何false值。作爲一個「集合」,這也會刪除重複的條目,但出於排序的目的,這是一件好事。

將陣列縮小並重新設置爲值後,您將處理$unwind準備進入下一個階段,將這些值作爲單個元素處理。 $group階段實質上在「分數」上應用$max以返回過濾結果中包含的最高值。

然後,它只是在確定的值上應用$sort來訂購文檔。自然如果你想這個反過來,然後使用$min,而不是按升序排序。

當然,如果您真正想要的是在標籤內實際包含id的「t1」值的文檔,那麼當然要在起始處添加一個$match階段。但該部分與您想要實現的篩選結果的排序關係最小。

計算的替代方法是在向文檔中的數組寫入條目時執行此操作。種類雜亂,但它是這樣的:

db.collection.update(
    { "_id": docId }, 
    { 
     "$push": { "tags": { "id": "t1", "score": 60 } }, 
     "$max": { "maxt1score": 60 }, 
     "$min": { "mint1score": 60 } 
    } 
) 

這裏如果新值比現有的值或其它無屬性尚不存在較大的$max更新操作只設置指定字段的值。相反的情況是$min,如果小於它將被替換爲新的值。

這當然有增加各種附加屬性,以文件的效果,但最終的結果排序大爲簡化:

db.collection.find().sort({ "maxt1score": -1 }) 

而且它會比使用聚合計算速度跑了很多管道。

所以考慮設計原則。數組中需要篩選和配對結果的結構化數據意味着在運行時計算排序的值。在.update()上向文檔添加附加屬性意味着您只需引用這些屬性即可直接對結果進行排序。

+0

感謝您的解釋。第二種方法不適合我的情況,因爲我可以有50-100個變量的標籤。雖然它非常有效。聚合函數給了我一個錯誤,沒有定義el,當它變成$ el時,它沒有顯示任何結果,我在這裏丟失了什麼? – MKoosej

+0

按照預期在您的答案中使用了小編輯。 – MKoosej

+0

如何將整個文檔恢復到組中?我應該在投影中逐一添加它們嗎? – MKoosej

0

根據上述描述的查詢

db.collection.find({tags.id:"t1"}).sort({tags.score:-1}) 

將過濾由標籤id 1的文件和將按降序返回由得分排序的結果。

也請提供例如文檔一起詳細描述和預期的輸出結果

+0

公平地確定OP的問題清楚地表明,「排序」僅適用於匹配「t1」的數組中的值。這不會那樣做,只匹配包含「t1」的文檔,並且不會像這樣限制排序。至少如果「點符號」語法在這裏是正確的而不是非法的。但它仍然不是被問到的問題的答案。 –