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

MongoDB-forespørgselskommentarer sammen med brugeroplysninger

Problem(erne)

Som skrevet før , der er flere problemer ved overindlejring:

Problem 1:BSON-størrelsesgrænse

I skrivende stund er BSON-dokumenter begrænset til 16 MB . Hvis denne grænse er nået, ville MongoDB give en undtagelse, og du kunne simpelthen ikke tilføje flere kommentarer og i værste fald ikke engang ændre (bruger-)navnet eller billedet, hvis ændringen ville øge størrelsen af ​​dokumentet.

Problem 2:Forespørgselsbegrænsninger og ydeevne

Det er ikke let muligt at forespørge eller sortere kommentararrayet under visse betingelser. Nogle ting ville kræve en ret kostbar sammenlægning, andre ret komplicerede udsagn.

Mens man kan argumentere for, at når først forespørgslerne er på plads, er dette ikke noget af et problem, men jeg beder om at være anderledes. For det første, jo mere kompliceret en forespørgsel er, jo sværere er den at optimere, både for udvikleren og efterfølgende MongoDBs forespørgselsoptimering. Jeg har haft de bedste resultater med at forenkle datamodeller og forespørgsler og fremskynde svarene med en faktor på 100 i ét tilfælde.

Ved skalering kan de nødvendige ressourcer til komplicerede og/eller dyre forespørgsler endda opsummere til hele maskiner sammenlignet med en enklere datamodel og i overensstemmelse med forespørgsler.

Problem 3:Vedligeholdelse

Sidst men ikke mindst kan du meget vel løbe ind i problemer med at vedligeholde din kode. Som en simpel tommelfingerregel

I denne sammenhæng refererer "dyrt" både til penge (til professionelle projekter) og tid (til hobbyprojekter).

(Min!) Løsning

Det er ret nemt:forenkle din datamodel. Dine forespørgsler bliver derfor mindre komplicerede og (forhåbentlig) hurtigere.

Trin 1:Identificer dine use cases

Det vil være et vildt gæt for mig, men det vigtige her er at vise dig den generelle metode. Jeg vil definere dine use cases som følger:

  1. For et givet indlæg bør brugere kunne kommentere
  2. For et givet indlæg skal du vise forfatteren og kommentarerne sammen med kommentatorernes og forfatterens brugernavn og deres billede
  3. For en given bruger bør det være nemt at ændre navn, brugernavn og billede

Trin 2:Modellér dine data i overensstemmelse hermed

Brugere

Først og fremmest har vi en ligetil brugermodel

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

Intet nyt her, tilføjet blot for fuldstændighedens skyld.

Indlæg

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

Og det er om det for et indlæg. Der er to ting at bemærke her:For det første gemmer vi de forfatterdata, vi umiddelbart har brug for, når vi viser et indlæg, da dette sparer os for en forespørgsel efter en meget almindelig, hvis ikke allestedsnærværende brugssag. Hvorfor gemmer vi ikke kommentarer og kommentatordata i overensstemmelse hermed? På grund af 16 MB størrelsesgrænse , forsøger vi at forhindre lagring af referencer i et enkelt dokument. I stedet gemmer vi referencerne i kommentardokumenter:

Kommentarer

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

På samme måde som med indlæg har vi alle de nødvendige data til at vise et indlæg.

Forespørgslerne

Det, vi har opnået nu, er, at vi har omgået BSON-størrelsesgrænsen, og vi behøver ikke at referere til brugerdataene for at kunne vise indlæg og kommentarer, hvilket burde spare os for mange forespørgsler. Men lad os vende tilbage til use cases og nogle flere forespørgsler

Tilføjelse af en kommentar

Det er fuldstændig ligetil nu.

Få alle eller nogle kommentarer til et givet indlæg

For alle kommentarer

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

For de 3 seneste kommentarer

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

Så for at vise et indlæg og alle (eller nogle) af dets kommentarer, inklusive brugernavne og billeder, har vi to spørgsmål. Mere end du havde brug for før, men vi omgik størrelsesgrænsen, og som udgangspunkt kan du have et ubestemt antal kommentarer for hvert indlæg. Men lad os komme til noget rigtigt

Få de seneste 5 indlæg og deres seneste 3 kommentarer

Dette er en to-trins proces. Men med korrekt indeksering (vil komme tilbage til det senere) burde dette stadig være hurtigt (og dermed ressourcebesparelse):

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å alle indlæg fra en given bruger sorteret fra nyeste til ældste og deres 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});

Bemærk, at vi kun har to forespørgsler her. Selvom du skal "manuelt" oprette forbindelsen mellem indlæg og deres respektive kommentarer, burde det være ret ligetil.

Skift et brugernavn

Dette er formentlig en sjælden use case, der er udført. Det er dog ikke særlig kompliceret med nævnte datamodel

Først ændrer vi brugerdokumentet

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

Vi skubber det gamle brugernavn til et tilsvarende array. Dette er en sikkerhedsforanstaltning, hvis noget går galt med følgende handlinger. Desuden sætter vi skrivebekymringen til et ret højt niveau for at sikre, at dataene er holdbare.

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

Ikke noget særligt her. Opdateringserklæringen for kommentarerne ser stort set ens ud. Selvom disse forespørgsler tager noget tid, udføres de sjældent.

Indeksen

Som en tommelfingerregel kan man sige, at MongoDB kun kan bruge ét indeks pr. forespørgsel. Selvom dette ikke er helt sandt, da der er indekskryds, er det let at håndtere. En anden ting er, at individuelle felter i et sammensat indeks kan bruges uafhængigt. Så en nem tilgang til indeksoptimering er at finde den forespørgsel med flest felter, der bruges i operationer, der gør brug af indekser og oprette et sammensat indeks af dem. Bemærk, at rækkefølgen af ​​forekomst i forespørgslen har betydning. Så lad os gå videre.

Indlæg

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

Kommentarer

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

Konklusion

Et fuldt indlejret dokument pr. indlæg er ganske vist den hurtigste måde at indlæse det og dets kommentarer på. Det skalerer imidlertid ikke godt, og på grund af arten af ​​muligvis komplekse forespørgsler, der er nødvendige for at håndtere det, kan denne ydeevnefordel blive udnyttet eller endda elimineret.

Med ovenstående løsning bytter du en vis hastighed (hvis!) mod stort set ubegrænset skalerbarhed og en meget mere ligetil måde at håndtere dataene på.

Hth.



  1. Sådan konfigureres MongoDb-samlingsnavn til en klasse i Spring Data

  2. Hvornår skal man bruge Singleton vs Transient vs Request ved hjælp af Ninject og MongoDB

  3. Fjernelse af en-en og en-mange referencer - Mongoose

  4. HBase:5 tips til at køre på EC2 med lav hukommelse