MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Aplicativo semelhante ao Twitter usando MongoDB


Você tem duas maneiras possíveis de um usuário seguir outro usuário; direta ou indiretamente por meio de um grupo, caso em que o usuário diretamente segue o grupo. Vamos começar armazenando esses diretos relações entre usuários e grupos:
{
  _id: "userA",
  followingUsers: [ "userB", "userC" ],
  followingGroups: [ "groupX", "groupY" ]
}

Agora, você poderá rapidamente descobrir quais usuários o usuário A está seguindo, direta ou indiretamente. Para conseguir isso, você pode desnormalizar os grupos que o usuário A está seguindo. Digamos que os grupos X e Y sejam definidos da seguinte forma:
{
  _id: "groupX",
  members: [ "userC", "userD" ]
},
{
  _id: "groupY",
  members: [ "userD", "userE" ]
}

Com base nesses grupos e nas relações diretas que o usuário A tem, você pode gerar assinaturas entre usuários. As origens de uma assinatura são armazenadas com cada assinatura. Para os dados de exemplo, as assinaturas ficariam assim:
// 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" ] }

Você pode gerar essas assinaturas com bastante facilidade, usando uma chamada map-reduce-finalize para um usuário individual. Se um grupo for atualizado, você só precisará executar novamente o map-reduce para todos os usuários que estão seguindo o grupo e as assinaturas serão atualizadas novamente.

Redução de mapa


As seguintes funções de redução de mapa gerarão as assinaturas para um único usuário.
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);
}

Você pode então executar o map-reduce para um único usuário, especificando uma consulta, neste caso para userA .
db.users.mapReduce(map, reduce, { finalize: finalize, query: { _id: "userA" }})

Algumas notas:
  • Você deve excluir as assinaturas anteriores de um usuário antes de executar o map-reduce para esse usuário.
  • Se você atualizar um grupo, deverá executar map-reduce para todos os usuários que seguem o grupo.

Devo observar que essas funções de redução de mapa acabaram sendo mais complexas do que eu tinha em mente , porque o MongoDB não suporta arrays como valores de retorno de funções de redução. Em teoria, as funções poderiam seria muito mais simples, mas não seria compatível com o MongoDB. No entanto, esta solução mais complexa pode ser usada para mapear-reduzir todos os users coleta em uma única chamada, se você precisar.