2016-07-25 71 views
4

子文檔領域鑑於以下集合:排序在蒙戈

[ 
    { 
     name: User1 
     metadata: [ 
      {k:"score", v: 5}, 
      {k:"other", v: 10} 
     ] 
    }, 
    { 
     name: User2 
     metadata: [ 
      {k:"score", v: 1}, 
      {k:"other", v: 1} 
     ] 
    }, 
    { 
     name: User3 
     metadata: [ 
      {k:"score", v: 2}, 
      {k:"other", v: 0} 
     ] 
    } 
] 

你怎麼能這些項目通過他們的「得分」排序?我可以通過metadata.v場​​進行排序,但我不知道如何只考慮「V」值匹配的「k」是子文檔:「分數」

預期結果將是:

[ 
    { 
     name: User2 
     metadata: [ 
      {k:"score", v: 1}, 
      {k:"other", v: 1} 
     ] 
    }, 
    { 
     name: User3 
     metadata: [ 
      {k:"score", v: 2}, 
      {k:"other", v: 0} 
     ] 
    }, 
    { 
     name: User1 
     metadata: [ 
      {k:"score", v: 5}, 
      {k:"other", v: 10} 
     ] 
    } 
] 
+0

目前還不清楚你問什麼在這裏彰顯的標準。請考慮將預期的輸出添加到您的問題中。 – styvane

+0

你在結果中是否需要其他元數據元素(例如'k:「其他」元素)? – JohnnyHK

+0

@JohnnyHK是的,我想擁有文檔中的所有數據。 –

回答

2

另一種解決方案可能看起來像這樣,如果您可以使用額外的scoredata屬性,則最終投影階段是可選的。

db.yourCollection.aggregate([ 
    { 
     $project: { 
      name: 1, 
      metadata: 1, 
      scoredata: { 
       $filter: { 
        input: '$metadata', 
        as: 'metadoc', 
        cond: { 
         $eq: [ '$$metadoc.k', 'score' ] 
        } 
       } 
      } 
     } 
    }, 
    { 
     $sort: { 
      scoredata: 1 
     } 
    }, 
    { 
     $project: { 
      name: 1, 
      metadata: 1    
     } 
    } 
]) 

輸出看起來像這樣

/* 1 */ 
{ 
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc3"), 
    "name" : "User2", 
    "metadata" : [ 
     { 
      "k" : "score", 
      "v" : 1 
     }, 
     { 
      "k" : "other", 
      "v" : 1 
     } 
    ] 
} 

/* 2 */ 
{ 
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc4"), 
    "name" : "User3", 
    "metadata" : [ 
     { 
      "k" : "score", 
      "v" : 2 
     }, 
     { 
      "k" : "other", 
      "v" : 0 
     } 
    ] 
} 

/* 3 */ 
{ 
    "_id" : ObjectId("5796387b3360e0a2e9dd9fc2"), 
    "name" : "User1", 
    "metadata" : [ 
     { 
      "k" : "score", 
      "v" : 5 
     }, 
     { 
      "k" : "other", 
      "v" : 10 
     } 
    ] 
} 
+1

好的。這應該比我的答案更有效率。 – JohnnyHK

+0

Thx爲誠實評論! – DAXaholic

+0

@DAXholic這很好用!你知道是否有辦法讓原始文檔中的所有字段無需明確指定? –

1

您可以通過沿突出的每個文件的副本,這樣做有其aggregate展開metadata的元數據過濾和排序前:

db.test.aggregate([ 
    // Project a copy of each document along with its metadata 
    {$project: {doc: '$$ROOT', metadata: '$metadata'}}, 
    // Duplicate the docs, one per metadata element 
    {$unwind: '$metadata'}, 
    // Filter to just the score metadata for sorting 
    {$match: {'metadata.k': 'score'}}, 
    // Sort on the score values 
    {$sort: {'metadata.v': 1}}, 
    // Project just the original docs in their sorted order 
    {$project: {_id: 0, doc: '$doc'}} 
]) 

輸出:

{ 
    "doc" : { 
     "_id" : ObjectId("57962f891be8975795eee18a"), 
     "name" : "User2", 
     "metadata" : [ 
      { 
       "k" : "score", 
       "v" : 1.0 
      }, 
      { 
       "k" : "other", 
       "v" : 1.0 
      } 
     ] 
    } 
} 
{ 
    "doc" : { 
     "_id" : ObjectId("57962f891be8975795eee18b"), 
     "name" : "User3", 
     "metadata" : [ 
      { 
       "k" : "score", 
       "v" : 2.0 
      }, 
      { 
       "k" : "other", 
       "v" : 0.0 
      } 
     ] 
    } 
} 
{ 
    "doc" : { 
     "_id" : ObjectId("57962f891be8975795eee189"), 
     "name" : "User1", 
     "metadata" : [ 
      { 
       "k" : "score", 
       "v" : 5.0 
      }, 
      { 
       "k" : "other", 
       "v" : 10.0 
      } 
     ] 
    } 
} 

如果需要,您可以更改最終的$project以重塑文檔以將doc字段升級回頂層。

0

使用$elemMatchaggregate和排序metadata.v檢查下面的查詢

db.collection.aggregate({"$match":{"metadata":{"$elemMatch":{"k":"score"}}}}, 
         {"$sort":{"metadata.v":1}}).pretty() 
+0

使用所有的metadata.v值進行排序,而不僅僅是匹配{「k」:「score」}的值 –