MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

App simile a Twitter che utilizza MongoDB

Hai due possibili modi in cui un utente può seguire un altro utente; direttamente o indirettamente attraverso un gruppo, nel qual caso l'utente direttamente segue il gruppo. Iniziamo con la memorizzazione di questi diretti relazioni tra utenti e gruppi:

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

Ora vorrai essere in grado di rapidamente scoprire quali utenti l'utente A sta seguendo, direttamente o indirettamente. Per ottenere ciò, puoi denormalizzare i gruppi che l'utente A sta seguendo. Diciamo che i gruppi X e Y sono definiti come segue:

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

Sulla base di questi gruppi e delle relazioni dirette dell'utente A, puoi generare abbonamenti tra utenti. L'origine o le origini di un abbonamento vengono memorizzate con ogni abbonamento. Per i dati di esempio gli abbonamenti sarebbero così:

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

Puoi generare questi abbonamenti abbastanza facilmente, utilizzando una chiamata map-reduce-finalize per un singolo utente. Se un gruppo viene aggiornato, devi solo rieseguire la riduzione della mappa per tutti gli utenti che stanno seguendo il gruppo e le iscrizioni saranno nuovamente aggiornate.

Riduci mappa

Le seguenti funzioni di riduzione della mappa genereranno gli abbonamenti per un singolo utente.

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

È quindi possibile eseguire la riduzione della mappa per un singolo utente, specificando una query, in questo caso per userA .

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

Alcune note:

  • Dovresti eliminare le sottoscrizioni precedenti di un utente, prima di eseguire map-reduce per quell'utente.
  • Se aggiorni un gruppo, dovresti eseguire map-reduce per tutti gli utenti che seguono il gruppo.

Devo notare che queste funzioni di riduzione della mappa si sono rivelate più complesse di quanto avessi in mente , perché MongoDB non supporta gli array come valori di ritorno delle funzioni di riduzione. In teoria, le funzioni potrebbero essere molto più semplice, ma non sarebbe compatibile con MongoDB. Tuttavia, questa soluzione più complessa può essere utilizzata per ridurre la mappatura degli interi users ritiro in una sola chiamata, se necessario.