it-swarm.com.de

Mongodb, Aggregatabfrage mit $ Lookup

Ich habe zwei Sammlungen, Tags und Personen. 

tags Modell:

{
  en: String,
  sv: String
}

personenmodell:

{
  name: String,
  projects: [
    title: String,
    tags: [
      {
        type: Schema.ObjectId,
        ref: 'tag'
      }
    ]
  ]

}

Ich möchte eine Abfrage, die alle im Personenmodell verwendeten Tags zurückgibt. Alle Dokumente.

Etwas wie

var query = mongoose.model('tag').find({...});

Oder sollte ich irgendwie den Gesamtansatz dazu verwenden? 

8
Joe

Für ein bestimmtes Personendokument können Sie die Funktion populate() gerne verwenden 

var query = mongoose.model("person").find({ "name": "foo" }).populate("projects.tags");

Wenn Sie beispielsweise nach Personen suchen möchten, die über ein Kennzeichen mit 'MongoDB' oder 'Node JS' verfügen, können Sie die Abfrageoption in die Funktion populate() einfügen:

var query = mongoose.model("person").find({ "name": "foo" }).populate({
    "path": "projects.tags",
    "match": { "en": { "$in": ["MongoDB", "Node JS"] } }
});

Wenn alle Tags in "project.tags" für alle Personen vorhanden sein sollen, ist das Aggregations-Framework der richtige Weg. Ziehen Sie in Betracht, diese Pipeline für die Personensammlung auszuführen, und verwenden Sie den Operator $lookup, um einen Links-Join für die Tags-Sammlung durchzuführen:

mongoose.model('person').aggregate([
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },
    {
        "$lookup": {
            "from": "tags",
            "localField": "projects.tags",
            "foreignField": "_id",
            "as": "resultingTagsArray"
        }
    },
    { "$unwind": "$resultingTagsArray" },
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$resultingTagsArray" },
            "count": { "$sum": 1 }
        }
    }
 ]).exec(function(err, results){
    console.log(results);
 })

Wenden Sie dann für eine bestimmte Person eine $match - Pipeline als ersten Schritt an, um die Dokumente zu filtern: 

mongoose.model('person').aggregate([
    { "$match": { "name": "foo" } },
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },
    {
        "$lookup": {
            "from": "tags",
            "localField": "projects.tags",
            "foreignField": "_id",
            "as": "resultingTagsArray"
        }
    },
    { "$unwind": "$resultingTagsArray" },
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$resultingTagsArray" },
            "count": { "$sum": 1 }
        }
    }
 ]).exec(function(err, results){
    console.log(results);
 })

Eine andere Problemumgehung, wenn Sie MongoDB-Versionen> = 2.6 oder <= 3.0 verwenden, die den Operator $lookup nicht unterstützen, besteht darin, die Ergebnisse der Aggregation folgendermaßen auszufüllen:

mongoose.model('person').aggregate([
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },    
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$projects.tags" }
        }
    }
 ], function(err, result) {
    mongoose.model('person')
    .populate(result, { "path": "allTags" }, function(err, results) {
        if (err) throw err;
        console.log(JSON.stringify(results, undefined, 4 ));
    });
});
27
chridam

Wenn Sie MongoDb Version 3.2 verwenden, können Sie $ lookup verwenden, um einen Left Outer Join durchzuführen.

1