sql >> Base de Datos >  >> NoSQL >> MongoDB

Aplicación similar a Twitter usando MongoDB

Tienes dos formas posibles en las que un usuario puede seguir a otro usuario; ya sea directamente o indirectamente a través de un grupo, en cuyo caso el usuario directamente sigue al grupo. Comencemos almacenando estos directos relaciones entre usuarios y grupos:

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

Ahora querrá poder rápidamente averiguar qué usuarios está siguiendo el usuario A, ya sea directa o indirectamente. Para lograr esto, puede desnormalizar los grupos que sigue el usuario A. Digamos que los grupos X e Y se definen de la siguiente manera:

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

En función de estos grupos y de las relaciones directas que tiene el usuario A, puede generar suscripciones entre usuarios. Los orígenes de una suscripción se almacenan con cada suscripción. Para los datos de ejemplo, las suscripciones se verían así:

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

Puede generar estas suscripciones con bastante facilidad, mediante una llamada map-reduce-finalize para un usuario individual. Si se actualiza un grupo, solo tiene que volver a ejecutar map-reduce para todos los usuarios que siguen al grupo y las suscripciones se actualizarán nuevamente.

Mapa-reducir

Las siguientes funciones de reducción de mapa generarán las suscripciones para un solo usuario.

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

Luego puede ejecutar map-reduce para un solo usuario, especificando una consulta, en este caso para userA .

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

Algunas notas:

  • Debe eliminar las suscripciones anteriores de un usuario antes de ejecutar map-reduce para ese usuario.
  • Si actualiza un grupo, debe ejecutar map-reduce para todos los usuarios que siguen al grupo.

Debo señalar que estas funciones de reducción de mapa resultaron ser más complejas de lo que tenía en mente , porque MongoDB no admite matrices como valores de retorno de funciones de reducción. En teoría, las funciones podrían sería mucho más simple, pero no sería compatible con MongoDB. Sin embargo, esta solución más compleja se puede usar para mapear y reducir todos los users colección en una sola llamada, si alguna vez tiene que hacerlo.