web-dev-qa-db-fra.com

Mongoose - Comment grouper et peupler?

J'utilise MongoDB et Mongoose comme ODM et j'essaie de faire une requête en utilisant populate et group by dans la même déclaration.

Voici mes modèles de documents simples:

var userSchema = new Schema({
    username: String
});

var messageSchema = new Schema({
    from: { type: Schema.ObjectId, ref: 'User' },
    to: { type: Schema.ObjectId, ref: 'User' },
    message: String,
    date: { type: Date, default: Date.now }
});

J'essaie simplement d'obtenir tous les messages d'un utilisateur, regroupés par chaque utilisateur avec qui il parle. J'ai essayé comme ça:

this.find({ 'to': user })
    .sort({ 'date': 1 })
    .group('from')
    .populate(['from', 'to'])
    .exec(callback);

Mais, malheureusement, mon modèle n'a pas de méthode group. Avez-vous une solution pour que cela fonctionne?

Je vous remercie.

19
Jeffrey Muller

Exemple utilisant $ lookup populate, lookup se remplit comme un tableau, d'où le $ unwind.

Message.aggregate(
    [
        { "$match": { "to": user } },
        { "$sort": { "date": 1 } },
        { "$group": { 
            "_id": "from",
            "to": { "$first": "$to" },
            "message": { "$first": "$message" },
            "date": { "$first": "$date" },
            "origId": { "$first": "$_id" }
        }},
        { "$lookup": {
             "from": "users",
             "localField": "from",
             "foreignField": "_id",
             "as": "from"
        }},
        { "$lookup": {
             "from": "users",
             "localField": "to",
             "foreignField": "_id",
             "as": "to"
        }},
        { "$unwind": { "path" : "$from" } },
        { "$unwind": { "path" : "$to" } }
    ],
    function(err,results) {
        if (err) throw err;
        return results;
    }
)
19
raubas

La meilleure option à utiliser ici est .aggregate() , qui est une implémentation de code natif contrairement à la méthode .group() de MongoDB qui utilise le Moteur JavaScript pour traiter les résultats.

Les méthodes comme .populate() ne sont pas directement prises en charge cependant, et cela est voulu par la conception car le pipeline d'agrégation et d'autres méthodes ne renvoient pas strictement une réponse basée sur le schéma du modèle actuel. Puisqu'il serait faux de "supposer" que c'est ce que vous faites, ce n'est qu'une réponse d'objet brut.

Rien ne vous empêche cependant de "transtyper" la réponse en tant que documents mangouste puis d'appeler la forme modèle de .populate() avec les chemins requis:

Message.aggregate(
    [
        { "$match": { "to": user } },
        { "$sort": { "date": 1 } },
        { "$group": { 
            "_id": "from",
            "to": { "$first": "$to" },
            "message": { "$first": "$message" },
            "date": { "$first": "$date" },
            "origId": { "$first": "$_id" }
        }}
    ],
    function(err,results) {
        if (err) throw err;
        results = result.map(function(doc) { 
            doc.from = doc._id
            doc._id = doc.origId;
            delete doc.origId;
            return new Message( doc ) 
        });
        User.populate( results, { "path": "from to" }, function(err,results) {
            if (err) throw err;
            console.log( JSON.stringify( results, undefined, 4 ) );
        });
    }
)

Bien sûr, cela renvoie vraiment le message $first de chaque "from" comme le laisse entendre le opérateur.

Peut-être que ce que vous entendez vraiment par "grouper par" est en fait de "trier":

Message.find({ "to": user })
    .sort({ "from": 1, "date": 1 })
    .populate("from to")
    .exec(function(err,messsages) {
        if (err) throw err;
        console.log( JSON.stringify( messages, undefined, 4 ) );
    });

Comme votre contexte dit "tous les messages" et non quelque chose qui serait autrement impliqué par un opérateur de regroupement comme avec la méthode de collecte .aggregate() ou .group(). Ainsi, les messages sont "regroupés" via le tri, plutôt que par un groupement particulier.

Ce dernier ressemble à ce que vous demandez vraiment, mais si vous vouliez réellement un véritable "groupement", il y a l'exemple d'agrégation avec la façon d'utiliser .populate() avec cela.

12
Neil Lunn