2013-09-30 6 views
2

J'ai une collection de messsages avec les champs suivants: _id, senderId, receiverId, dateSubmittedMs, message, et pour un utilisateur donné je veux lui retourner le dernier message de tous les autres utilisateurs. Ainsi, par exemple, s'il y a des utilisateurs Alex, Barb, Chuck, Dora, je voudrais retourner le message le plus récent entre Alex et chacun de Barb, Chuck et Dora. Quelle est la meilleure façon de procéder? Puis-je le faire en une seule étape en utilisant l'agrégation? Les exemples d'agrégation dans la documentation en ligne officielle (http://docs.mongodb.org/manual/reference/aggregation/min/) montrent comment trouver l'âge le plus bas au sein d'une collection, mais ce dont j'ai besoin est quelque chose d'analogue à trouver le nom de la personne la plus jeune sur des groupes de personnes.

Voici mon approche actuelle:

Étape 1: Trouvez la valeur la plus élevée pour dateSubmitted sur tous les messages envoyés et reçus par Alex, le regroupement sur les autres utilisateurs:

var M = Messages.aggregate(
{$match: 
    {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]} 
}, 
{$group: {_id: "$receiverId", lastestSubmitted: {$max: "$submitted"} }}).fetch(); 

Étape 2: Créer un tableau de ces valeurs les plus élevées de DateSubmitted:

var MIds = _.pluck(M,'lastestSubmitted'); 

Etape 3: Trouver ces messages, par senderID, ReceiverID et latestSubmitted:

return Messages.find(
    {submitted: {$in: MIds}, $or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]}, 
    {$sort: {submitted: 1}} 
}); 

Il y a deux problèmes avec ceci:

  1. Peut-il être fait en une seule étape au lieu de trois? Peut-être à travers une commande mapReduce ou Aggregate?
  2. Au lieu de grouper uniquement sur le receiverId: 'Alex', est-il possible de regrouper quelque chose comme: $or [{receiverId: 'Alex', senderId: 'Barb'}, {senderId: 'Alex', receiverId: 'Barb'}]? (mais pour CHACUN des autres utilisateurs) Cela me permettrait d'obtenir le dernier message dans une conversation entre deux participants avec lesquels Alex a conversé. Ainsi, par exemple:

Des suggestions?

Répondre

1

La seule chose que vous devez changer est le group._id dans la phase de grugping. Bien que vous puissiez utiliser un document pour cela, pas seulement un champ, vous pouvez appliquer le tri dans le même pipeline. Cependant, cela ne change pas si vous n'utilisez que le receiverId pour le groupement.

var M = Messages.aggregate(
{$match: 
    {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]} 
}, 
{$group: 
    {_id: { 
      receiverId: "$receiverId", 
      senderId: "$senderId"}, 
     lastestSubmitted: {$max: "$submitted"} } 
}, 
{$sort: {submitted: -1} 
}, 
{$limit: 1} 
).fetch(); 

Cet exemple ci-dessus ajoute que la possibilité de vérifier la deuxième connexion la plus récente sondée ou le troisième, si vous ne est susceptible d'obtenir le dernier message que vous ne même pas besoin de groupe. Il suffit de lancer ceci:

var M = Messages.aggregate(
{$match: 
    {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]} 
}, 
{$sort: {submitted: -1} 
}, 
{$limit: 1} 
).fetch(); 

LA RÉPONSE Au-dessus, POUR FAIRE PASSER LE MESSAGE PLUS RÉCENTE ENTRE UN UTILISATEUR UNIQUE ET AUTRES, POUR OBTENIR LE MESSAGE PLUS RÉCENTE ENTRE PAIRS TOUS A L'UTILISATEUR LIRE IMPLIQUANT EN >>>

Basé sur les commentaires j'ai mal interprété un peu la question mais la partie ci-dessus est utile quand même. La résolution correcte pour votre problème est inférieure. La difficulté est d'obtenir une clé pour que la paire se groupe et identifie la paire commutée (bob -> tom == tom -> bob) dans ce cas. Vous pouvez utiliser la condition et la commande pour identifier les swaps.(Il est certainement une question beaucoup plus difficile) Le regard de code comme ceci:

var M = Messages.aggregate(
{$match: 
    {$or: [{senderId: 'Alex'}, {receiverId: 'Alex'}]} 
}, 
{$project: 
    {'part1':{$cond:[{$gt:['$senderId','$receiverId']},'$senderId','$receiverId']}, 
    'part2':{$cond:[{$gt:['$senderId','$receiverId']},'$receiverId','$senderId']}, 
    'message':1, 
    'submitted':1 
    } 
}, 
{$sort: {submitted: -1}}, 
{$group: 
    {_id: { 
      part1: "$part1", 
      part2: "$part2"}, 
     lastestSubmitted: {$first: "$submitted"}, 
     message: {$first: "$message"} } 
} 
).fetch(); 

Si vous n'êtes pas familier avec certains des opérateurs utilisés ci-dessus, comme $ ou cond premier $, vérifier this.

+0

Merci pour votre aide. J'ai essayé de l'implémenter, et cela fonctionnerait probablement si Meteor prenait en charge l'agrégation côté client, mais comme je viens de le découvrir (http://stackoverflow.com/questions/11266977/are-group-by-aggregation-queries-possible- in-météore-encore), météore ne l'est pas encore. Il y a un travail autour (https://github.com/meteor/meteor/pull/644), mais ce n'est pas réactif. Je vais essayer ça dans le shell mongodb et voir si je peux le faire fonctionner là-bas, juste pour être sûr. –

+0

J'ai essayé dans shell et copié collé le code que j'ai développé là à votre exemple. Honnêtement, je n'ai pas essayé dans Meteor pendant que je supposais que votre code dans la question fonctionne. Je suis curieux de vos tests, donc ce serait génial si vous pouviez fournir les résultats. Et j'espère que ça a aidé. – attish

+0

Bonjour @attish, merci pour le suivi. J'ai essayé d'implémenter vos deux solutions dans le shell mongodb, mais ils ne renvoient qu'un résultat global, alors qu'ils devraient retourner un résultat par conversation (si Alex avait des conversations avec Barb et Chuck, alors deux résultats devraient être retournés: le dernier message avec Barb et le dernier message avec Chuck). Avez-vous pu obtenir plus d'un résultat global? –

Questions connexes