sql >> Databasteknik >  >> NoSQL >> MongoDB

Twitter-liknande app som använder MongoDB

Du har två möjliga sätt på vilka en användare kan följa en annan användare; antingen direkt eller indirekt genom en grupp, i vilket fall användaren direkt följer gruppen. Låt oss börja med att lagra dessa direkta relationer mellan användare och grupper:

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

Nu vill du kunna snabbt ta reda på vilka användare som användare A följer, antingen direkt eller indirekt. För att uppnå detta kan du avnormalisera grupperna som användare A följer. Låt oss säga att grupp X och Y definieras enligt följande:

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

Baserat på dessa grupper, och de direkta relationer som användare A har, kan du skapa prenumerationer mellan användare. Ursprunget till ett abonnemang lagras med varje abonnemang. För exempeldata skulle prenumerationerna se ut så här:

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

Du kan generera dessa prenumerationer ganska enkelt genom att använda ett map-reduce-finalize-samtal för en enskild användare. Om en grupp uppdateras behöver du bara köra map-reduce igen för alla användare som följer gruppen och prenumerationerna kommer att vara uppdaterade igen.

Karta-minska

Följande map-reduce-funktioner genererar prenumerationerna för en enskild användare.

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

Du kan sedan köra map-reduce för en enskild användare, genom att ange en fråga, i det här fallet för userA .

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

Några anteckningar:

  • Du bör ta bort en användares tidigare prenumerationer innan du kör map-reduce för den användaren.
  • Om du uppdaterar en grupp bör du köra map-reduce för alla användare som följer gruppen.

Jag bör notera att dessa kartreducerande funktioner visade sig vara mer komplexa än vad jag hade i åtanke , eftersom MongoDB inte stöder arrayer som returvärden för reduceringsfunktioner. I teorin kunde funktionerna vara mycket enklare, men skulle inte vara kompatibel med MongoDB. Denna mer komplexa lösning kan dock användas för att kartreducera hela users insamling i ett enda samtal, om du någonsin måste.




  1. MongoDB:Kan inte kanonisera frågan:BadValue Projection kan inte ha en blandning av inkludering och exkludering

  2. Automatisera databashälsokontroll

  3. MongoDB $currentDate

  4. Stackexchange.Redis varför upprättar ConnectionMultiplexer.Connect två klientanslutningar?