sql >> Databasteknik >  >> NoSQL >> MongoDB

MongoDB frågekommentarer tillsammans med användarinformation

Problemet/problemen

Som skrivet tidigare , det finns flera problem vid överinbäddning:

Problem 1:BSON-storleksgräns

När detta skrivs är BSON-dokument begränsade till 16 MB . Om den gränsen nås, skulle MongoDB göra ett undantag och du kunde helt enkelt inte lägga till fler kommentarer och i värsta fall inte ens ändra (användar-)namnet eller bilden om ändringen skulle öka storleken på dokumentet.

Problem 2:Frågabegränsningar och prestanda

Det är inte lätt möjligt att fråga eller sortera kommentarsfältet under vissa förutsättningar. Vissa saker skulle kräva en ganska kostsam sammanställning, andra ganska komplicerade påståenden.

Även om man skulle kunna hävda att när frågorna väl är på plats är detta inte ett stort problem, men jag ber att skilja. För det första, ju mer komplicerad en fråga är, desto svårare är den att optimera, både för utvecklaren och därefter MongoDBs frågeoptimerare. Jag har fått de bästa resultaten med att förenkla datamodeller och frågor, påskynda svaren med en faktor på 100 i en instans.

Vid skalning kan resurserna som behövs för komplicerade och/eller kostsamma frågor till och med summeras till hela maskiner jämfört med en enklare datamodell och enligt frågor.

Problem 3:Underhållbarhet

Sist men inte minst kan du mycket väl stöta på problem med att underhålla din kod. Som en enkel tumregel

I detta sammanhang avser "dyrt" både pengar (för professionella projekt) och tid (för hobbyprojekt).

(Min!) Lösning

Det är ganska enkelt:förenkla din datamodell. Följaktligen kommer dina frågor att bli mindre komplicerade och (förhoppningsvis) snabbare.

Steg 1:Identifiera dina användningsfall

Det kommer att vara en vild gissning för mig, men det viktiga här är att visa dig den allmänna metoden. Jag skulle definiera dina användningsfall enligt följande:

  1. För ett givet inlägg bör användare kunna kommentera
  2. För ett givet inlägg, visa författaren och kommentarerna, tillsammans med kommentatorernas och författarens användarnamn och deras bild
  3. För en given användare bör det vara lätt att ändra namn, användarnamn och bild

Steg 2:Modellera dina data i enlighet med detta

Användare

Först och främst har vi en enkel användarmodell

{
  _id: new ObjectId(),
  name: "Joe Average",
  username: "HotGrrrl96",
  picture: "some_link"
}

Inget nytt här, lagt till bara för fullständighetens skull.

Inlägg

{
  _id: new ObjectId()
  title: "A post",
  content: " Interesting stuff",
  picture: "some_link",
  created: new ISODate(),
  author: {
    username: "HotGrrrl96",
    picture: "some_link"
  }
}

Och det är ungefär det för ett inlägg. Det finns två saker att notera här:för det första lagrar vi författardata vi omedelbart behöver när vi visar ett inlägg, eftersom detta sparar oss en fråga för ett mycket vanligt, om inte allmänt förekommande användningsfall. Varför sparar vi inte kommentarer och kommentatordata i enlighet med detta? På grund av 16 MB storleksgräns , försöker vi förhindra lagring av referenser i ett enda dokument. Snarare lagrar vi referenserna i kommentarsdokument:

Kommentarer

{
  _id: new ObjectId(),
  post: someObjectId,
  created: new ISODate(),
  commenter: {
    username: "FooBar",
    picture: "some_link"
  },
  comment: "Awesome!"
}

På samma sätt som med inlägg har vi all nödvändig data för att visa ett inlägg.

Frågorna

Det vi har uppnått nu är att vi kringgick BSON-storleksgränsen och vi behöver inte hänvisa till användardata för att kunna visa inlägg och kommentarer, vilket borde spara oss en hel del frågor. Men låt oss återkomma till användningsfallen och några fler frågor

Lägga till en kommentar

Det är helt okomplicerat nu.

Få alla eller några kommentarer för ett givet inlägg

För alla kommentarer

db.comments.find({post:objectIdOfPost})

För de 3 senaste kommentarerna

db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)

Så för att visa ett inlägg och alla (eller några) av dess kommentarer inklusive användarnamn och bilder har vi två frågor. Mer än du behövde tidigare, men vi kringgick storleksgränsen och i princip kan du ha ett obestämt antal kommentarer för varje inlägg. Men låt oss komma till något verkligt

Hämta de senaste 5 inläggen och deras senaste 3 kommentarer

Detta är en tvåstegsprocess. Men med korrekt indexering (återkommer till det senare) bör detta fortfarande vara snabbt (och därmed resursbesparing):

var posts = db.posts.find().sort({created:-1}).limit(5)
posts.forEach(
  function(post) {
    doSomethingWith(post);
    var comments = db.comments.find({"post":post._id}).sort("created":-1).limit(3);
    doSomethingElseWith(comments);
  }
)

Få alla inlägg från en given användare sorterade från senaste till äldsta och deras kommentarer

var posts = db.posts.find({"author.username": "HotGrrrl96"},{_id:1}).sort({"created":-1});
var postIds = [];
posts.forEach(
  function(post){
    postIds.push(post._id);
  }
)
var comments = db.comments.find({post: {$in: postIds}}).sort({post:1, created:-1});

Observera att vi bara har två frågor här. Även om du måste "manuellt" göra kopplingen mellan inlägg och deras respektive kommentarer, borde det vara ganska enkelt.

Ändra ett användarnamn

Detta är förmodligen ett sällsynt användningsfall. Det är dock inte särskilt komplicerat med nämnda datamodell

Först ändrar vi användardokumentet

db.users.update(
  { username: "HotGrrrl96"},
  {
    $set: { username: "Joe Cool"},
    $push: {oldUsernames: "HotGrrrl96" }
  },
  {
    writeConcern: {w: "majority"}
  }
);

Vi skickar det gamla användarnamnet till en motsvarande array. Detta är en säkerhetsåtgärd om något går fel med följande operationer. Dessutom sätter vi skrivproblemet på en ganska hög nivå för att säkerställa att data är hållbara.

db.posts.update(
  { "author.username": "HotGrrrl96"},
  { $set:{ "author.username": "Joe Cool"} },
  {
    multi:true,
    writeConcern: {w:"majority"}
  }
)

Inget speciellt här. Uppdateringsförklaringen för kommentarerna ser ungefär likadan ut. Även om dessa frågor tar lite tid, körs de sällan.

Indeksen

Som en tumregel kan man säga att MongoDB bara kan använda ett index per fråga. Även om detta inte är helt sant eftersom det finns indexkorsningar, är det lätt att hantera. En annan sak är att enskilda fält i ett sammansatt index kan användas oberoende. Så ett enkelt tillvägagångssätt för indexoptimering är att hitta frågan med flest fält som används i operationer som använder index och skapa ett sammansatt index av dem. Observera att förekomstordningen i frågan har betydelse. Så, låt oss gå vidare.

Inlägg

db.posts.createIndex({"author.username":1,"created":-1})

Kommentarer

db.comments.createIndex({"post":1, "created":-1})

Slutsats

Ett helt inbäddat dokument per inlägg är visserligen det snabbaste sättet att ladda det och dess kommentarer. Den skalas dock inte bra och på grund av möjliga komplexa frågor som krävs för att hantera den, kan denna prestandafördel utnyttjas eller till och med elimineras.

Med ovanstående lösning byter du lite hastighet (om!) mot i princip obegränsad skalbarhet och ett mycket enklare sätt att hantera data.

Hth.



  1. Hur kan jag redigera/byta namn på nycklar under json.load i python?

  2. Varför får jag ett InvalidDocument-undantag när jag sparar ett objekt i MongoDB i Django för första gången?

  3. Kan inte ansluta till MongoDB atlas databas

  4. Hur kan jag lägga till bilder i mongoDB?