MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Application de type Twitter utilisant MongoDB

Vous avez deux façons possibles pour un utilisateur de suivre un autre utilisateur ; soit directement, soit indirectement via un groupe, auquel cas l'utilisateur directement suit le groupe. Commençons par stocker ces éléments directs relations entre utilisateurs et groupes :

{
  _id: "userA",
  followingUsers: [ "userB", "userC" ],
  followingGroups: [ "groupX", "groupY" ]
}

Maintenant, vous voudrez pouvoir rapidement savoir quels utilisateurs l'utilisateur A suit, directement ou indirectement. Pour ce faire, vous pouvez dénormaliser les groupes suivis par l'utilisateur A. Supposons que les groupes X et Y sont définis comme suit :

{
  _id: "groupX",
  members: [ "userC", "userD" ]
},
{
  _id: "groupY",
  members: [ "userD", "userE" ]
}

Sur la base de ces groupes et des relations directes de l'utilisateur A, vous pouvez générer des abonnements entre utilisateurs. L'origine ou les origines d'un abonnement sont stockées avec chaque abonnement. Pour les exemples de données, les abonnements ressembleraient à ceci :

// abusing exclamation mark to indicate a direct relation
{ ownerId: "userA", userId: "userB", origins: [ "!" ] },
{ ownerId: "userA", userId: "userC", origins: [ "!", "groupX" ] },
{ ownerId: "userA", userId: "userD", origins: [ "groupX", "groupY" ] },
{ ownerId: "userA", userId: "userE", origins: [ "groupY" ] }

Vous pouvez générer ces abonnements assez facilement, en utilisant un appel map-reduce-finalize pour un utilisateur individuel. Si un groupe est mis à jour, il vous suffit de relancer la réduction de carte pour tous les utilisateurs qui suivent le groupe et les abonnements seront à nouveau à jour.

Carte-réduire

Les fonctions de réduction de carte suivantes généreront les abonnements pour un seul utilisateur.

map = function () {
  ownerId = this._id;

  this.followingUsers.forEach(function (userId) {
    emit({ ownerId: ownerId, userId: userId } , { origins: [ "!" ] });
  });

  this.followingGroups.forEach(function (groupId) {
    group = db.groups.findOne({ _id: groupId });

    group.members.forEach(function (userId) {
      emit({ ownerId: ownerId, userId: userId } , { origins: [ group._id ] });
    });
  });
}

reduce = function (key, values) {
  origins = [];

  values.forEach(function (value) {
    origins = origins.concat(value.origins);
  });

  return { origins: origins };
}

finalize = function (key, value) {
  db.subscriptions.update(key, { $set: { origins: value.origins }}, true);
}

Vous pouvez ensuite exécuter le map-reduce pour un seul utilisateur, en spécifiant une requête, dans ce cas pour userA .

db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})

Quelques remarques :

  • Vous devez supprimer les abonnements précédents d'un utilisateur avant d'exécuter map-reduce pour cet utilisateur.
  • Si vous mettez à jour un groupe, vous devez exécuter map-reduce pour tous les utilisateurs qui suivent le groupe.

Je dois noter que ces fonctions de réduction de carte se sont avérées plus complexes que ce que j'avais en tête , car MongoDB ne prend pas en charge les tableaux comme valeurs de retour des fonctions de réduction. En théorie, les fonctions pourraient serait beaucoup plus simple, mais ne serait pas compatible avec MongoDB. Cependant, cette solution plus complexe peut être utilisée pour map-réduire l'ensemble des users collecte en un seul appel, si jamais vous en avez besoin.