sql >> Database >  >> NoSQL >> MongoDB

MongoDB-queryopmerkingen samen met gebruikersinformatie

Het(de) probleem(en)

Zoals voorheen geschreven , zijn er verschillende problemen bij over-inbedding:

Probleem 1:BSON-groottelimiet

Op het moment van schrijven zijn BSON-documenten beperkt tot 16 MB . Als die limiet wordt bereikt, zou MongoDB een uitzondering maken en zou je gewoon geen opmerkingen meer kunnen toevoegen en in het ergste geval zelfs de (gebruikers-)naam of de afbeelding niet wijzigen als de wijziging de grootte van het document zou vergroten.

Probleem 2:Beperkingen en prestaties van zoekopdrachten

Onder bepaalde voorwaarden is het niet eenvoudig om de array met opmerkingen te doorzoeken of te sorteren. Sommige dingen zouden een nogal kostbare aggregatie vereisen, andere nogal ingewikkelde verklaringen.

Hoewel je zou kunnen stellen dat als de query's eenmaal op hun plaats zijn, dit niet zo'n probleem is, ben ik het daar niet mee eens. Ten eerste, hoe ingewikkelder een query is, hoe moeilijker het is om te optimaliseren, zowel voor de ontwikkelaar als voor de MongoDB's query-optimizer. Ik heb de beste resultaten behaald met het eenvoudig maken van gegevensmodellen en query's, waardoor reacties in één keer met een factor 100 werden versneld.

Bij het schalen kunnen de middelen die nodig zijn voor gecompliceerde en/of kostbare query's zelfs tot hele machines oplopen in vergelijking met een eenvoudiger gegevensmodel en bijbehorende query's.

Probleem 3:Onderhoudbaarheid

Last but not least kunt u problemen tegenkomen bij het onderhouden van uw code. Als een eenvoudige vuistregel

In deze context verwijst 'duur' zowel naar geld (voor professionele projecten) als tijd (voor hobbyprojecten).

(Mijn!) Oplossing

Het is vrij eenvoudig:vereenvoudig uw datamodel. Hierdoor worden uw zoekopdrachten minder ingewikkeld en (hopelijk) sneller.

Stap 1:Identificeer uw gebruiksscenario's

Dat wordt een wilde gok voor mij, maar het belangrijkste hier is om je de algemene methode te laten zien. Ik zou uw gebruiksscenario's als volgt definiëren:

  1. Voor een bepaald bericht moeten gebruikers kunnen reageren
  2. Toon voor een bepaald bericht de auteur en de opmerkingen, samen met de gebruikersnaam van de commentatoren en auteurs en hun foto
  3. Voor een bepaalde gebruiker moet het gemakkelijk mogelijk zijn om de naam, gebruikersnaam en afbeelding te wijzigen

Stap 2:Modelleer uw gegevens dienovereenkomstig

Gebruikers

Allereerst hebben we een eenvoudig gebruikersmodel

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

Niets nieuws hier, voor de volledigheid toegevoegd.

Berichten

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

En dat is het zowat voor een post. Er zijn hier twee dingen om op te merken:ten eerste slaan we de auteursgegevens op die we onmiddellijk nodig hebben bij het weergeven van een bericht, omdat dit ons een zoekopdracht bespaart voor een veel voorkomende, zo niet alomtegenwoordige gebruikssituatie. Waarom slaan we de gegevens van de opmerkingen en commentatoren niet dienovereenkomstig op? Vanwege de limiet van 16 MB , proberen we het opslaan van referenties in één document te voorkomen. In plaats daarvan slaan we de referenties op in commentaardocumenten:

Opmerkingen

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

Hetzelfde als bij berichten, we hebben alle benodigde gegevens om een ​​bericht weer te geven.

De vragen

Wat we nu hebben bereikt, is dat we de BSON-groottelimiet hebben omzeild en dat we niet naar de gebruikersgegevens hoeven te verwijzen om berichten en opmerkingen te kunnen weergeven, wat ons veel vragen zou moeten besparen. Maar laten we terugkomen op de gebruiksscenario's en nog wat vragen

Een opmerking toevoegen

Dat is nu helemaal duidelijk.

Alle of enkele opmerkingen krijgen voor een bepaald bericht

Voor alle reacties

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

Voor de 3 laatste reacties

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

Dus voor het weergeven van een bericht en alle (of sommige) opmerkingen, inclusief de gebruikersnamen en afbeeldingen, hebben we twee vragen. Meer dan je eerder nodig had, maar we hebben de maximale grootte omzeild en in principe kun je een onbepaald aantal reacties voor elk bericht hebben. Maar laten we tot iets echts komen

De laatste 5 berichten en hun laatste 3 reacties ontvangen

Dit is een proces in twee stappen. Met de juiste indexering (kom daar later op terug) zou dit echter nog steeds snel moeten zijn (en dus middelen besparen):

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

Alle berichten van een bepaalde gebruiker sorteren van nieuw naar oud en hun opmerkingen

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

Merk op dat we hier slechts twee vragen hebben. Hoewel je "handmatig" de verbinding tussen berichten en hun respectievelijke opmerkingen moet maken, zou dat vrij eenvoudig moeten zijn.

Een gebruikersnaam wijzigen

Dit is vermoedelijk een zeldzame use case uitgevoerd. Het is echter niet erg ingewikkeld met genoemd datamodel

Eerst wijzigen we het gebruikersdocument

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

We pushen de oude gebruikersnaam naar een overeenkomstige array. Dit is een veiligheidsmaatregel voor het geval er iets misgaat met de volgende handelingen. Bovendien hebben we de schrijfzorg op een vrij hoog niveau gezet om ervoor te zorgen dat de gegevens duurzaam zijn.

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

Niets bijzonders hier. De update-verklaring voor de opmerkingen ziet er vrijwel hetzelfde uit. Hoewel deze zoekopdrachten enige tijd in beslag nemen, worden ze zelden uitgevoerd.

De indices

Als vuistregel kan men zeggen dat MongoDB slechts één index per zoekopdracht kan gebruiken. Hoewel dit niet helemaal waar is, omdat er indexkruisingen zijn, is het gemakkelijk om mee om te gaan. Een ander ding is dat individuele velden in een samengestelde index onafhankelijk van elkaar kunnen worden gebruikt. Een gemakkelijke benadering voor indexoptimalisatie is dus om de query te vinden met de meeste velden die worden gebruikt in bewerkingen die gebruik maken van indices en er een samengestelde index van te maken. Houd er rekening mee dat de volgorde van voorkomen in de query van belang is. Dus laten we doorgaan.

Berichten

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

Opmerkingen

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

Conclusie

Een volledig ingesloten document per bericht is weliswaar de snelste manier om het en de opmerkingen te laden. Het schaalt echter niet goed en vanwege de aard van mogelijk complexe query's die nodig zijn om ermee om te gaan, kan dit prestatievoordeel worden benut of zelfs worden geëlimineerd.

Met de bovenstaande oplossing ruilt u wat snelheid (als!) tegen in principe onbeperkte schaalbaarheid en een veel eenvoudigere manier om met de gegevens om te gaan.

Hth.



  1. hoe kan ik mijn mangoest-query herschrijven nadat ik gegevens van het ene model in twee heb gesplitst?

  2. Spring boot mongoDB zoals query op numerieke velden-Integer / Double

  3. Query in genest document in mongodb

  4. Hoe project DBref op Spring MongoDB Aggregation?