2016-07-25 5 views
4

die folgende Sammlung Gegeben:Sortieren nach Subdokumente Felder in Mongo

[ 
    { 
     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} 
     ] 
    } 
] 

Wie können Sie diese Elemente durch ihre „score“ sortieren? Ich kann durch das metadata.v Feld sortieren, aber ich bin nicht sicher, wie man nur die „v“ Werte für Subdokumente zu betrachten, die „k“ entsprechen: „Score“

Die erwartete Ausgabe wäre:

[ 
    { 
     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

Es ist unklar, was Sie hier fragen. Bitte denken Sie daran, die erwartete Ausgabe zu Ihrer Frage hinzuzufügen. – styvane

+0

Benötigen Sie die anderen 'metadata' Elemente in Ihrem Ergebnis (z. B. die' k: "other" 'Elemente)? – JohnnyHK

+0

@JohnnyHK Ja, ich möchte alle Daten im Dokument haben. –

Antwort

2

Eine alternative Lösung könnte so aussehen, wobei die endgültige Projektionsstufe optional ist, wenn Sie mit einer zusätzlichen Eigenschaft leben können.

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    
     } 
    } 
]) 

Ausgabe sieht wie folgt aus

/* 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

Gute Eins. Dies sollte effizienter sein als meine Antwort. – JohnnyHK

+0

Danke für den ehrlichen Kommentar! – DAXaholic

+0

@DAXaholic Das funktioniert super! Weißt du, ob es möglich ist, alle Felder im Originaldokument zu erhalten, ohne sie explizit angeben zu müssen? –

1

Sie können durch Projizieren eine Kopie von jedem Dokument mit aggregate dies tun, zusammen mit seiner metadata bevor die Metadaten zum filtern und Sortieren Abwickeln:

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'}} 
]) 

Output:

{ 
    "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 
      } 
     ] 
    } 
} 

Sie können die endgültige $project ändern, um die Dokumente so umzugestalten, dass die Felder doc bei Bedarf wieder auf die oberste Ebene gehoben werden.

0

Ihren Kriterien entsprechen mit $elemMatch in aggregate und sortieren nach metadata.v Prüfung unter Abfrage

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

Diese Sortierung erfolgt unter Verwendung aller metadata.v-Werte, nicht nur derjenigen, die mit {"k": "score"} übereinstimmen. –