sql >> Databáze >  >> NoSQL >> MongoDB

Komentáře dotazů MongoDB spolu s informacemi o uživateli

Problém(y)

Jak již bylo napsáno , při nadměrném vkládání dochází k několika problémům:

Problém 1:Limit velikosti BSON

V době psaní tohoto článku jsou dokumenty BSON omezeny na 16 MB . Pokud je tohoto limitu dosaženo, MongoDB vyvolá výjimku a vy byste jednoduše nemohli přidávat další komentáře a v nejhorším případě ani nezměnit (uživatelské) jméno nebo obrázek, pokud by změna zvětšila velikost dokumentu.

Problém 2:Omezení dotazu a výkon

Za určitých podmínek není snadné dotazovat se nebo třídit pole komentářů. Některé věci by vyžadovaly poměrně nákladnou agregaci, jiné dosti komplikované výpisy.

I když by se dalo namítnout, že jakmile jsou dotazy na místě, není to velký problém, musím se lišit. Za prvé, čím je dotaz složitější, tím hůře se optimalizuje, a to jak pro vývojáře, tak pro optimalizátor dotazů MongoDBs. Nejlepších výsledků jsem dosáhl se zjednodušením datových modelů a dotazů, zrychlením odpovědí o faktor 100 v jednom případě.

Při škálování mohou zdroje potřebné pro komplikované a/nebo nákladné dotazy dokonce sčítat celé stroje ve srovnání s jednodušším datovým modelem a podle dotazů.

Problém 3:Údržba

V neposlední řadě můžete narazit na problémy s údržbou kódu. Jako jednoduché pravidlo

V tomto kontextu se „drahý“ týká peněz (pro profesionální projekty) a času (pro hobby projekty).

(Moje!) řešení

Je to docela snadné:zjednodušte svůj datový model. V důsledku toho budou vaše dotazy méně komplikované a (doufejme) rychlejší.

Krok 1:Identifikujte své případy použití

Bude to pro mě divoký odhad, ale důležité je ukázat vám obecnou metodu. Vaše případy použití bych definoval následovně:

  1. U daného příspěvku by uživatelé měli mít možnost komentovat
  2. U daného příspěvku zobrazit autora a komentáře spolu s uživatelským jménem komentujících a autorů a jejich obrázkem
  3. U daného uživatele by mělo být snadné změnit jméno, uživatelské jméno a obrázek

Krok 2:Podle toho modelujte svá data

Uživatelé

Za prvé, máme přímočarý uživatelský model

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

Není zde nic nového, přidáno jen pro úplnost.

Příspěvky

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

A to je k příspěvku asi vše. Zde je třeba poznamenat dvě věci:za prvé ukládáme data autora, která okamžitě potřebujeme při zobrazení příspěvku, protože nám to ušetří dotaz na velmi běžný, ne-li všudypřítomný případ použití. Proč neukládáme komentáře a údaje o komentářích podle toho? Kvůli limitu velikosti 16 MB , snažíme se zabránit ukládání referencí do jednoho dokumentu. Spíše ukládáme odkazy v dokumentech komentářů:

Komentáře

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

Stejně jako u příspěvků máme všechny potřebné údaje pro zobrazení příspěvku.

Dotazy

Nyní jsme dosáhli toho, že jsme obešli limit velikosti BSON a nepotřebujeme se odvolávat na uživatelská data, abychom mohli zobrazovat příspěvky a komentáře, což by nám mělo ušetřit spoustu dotazů. Ale vraťme se k případům použití a několika dalším dotazům

Přidání komentáře

To je nyní zcela jednoduché.

Získání všech nebo některých komentářů k danému příspěvku

Za všechny komentáře

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

Za 3 poslední komentáře

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

Takže pro zobrazení příspěvku a všech (nebo některých) jeho komentářů včetně uživatelských jmen a obrázků jsme u dvou dotazů. Více, než jste dříve potřebovali, ale obešli jsme limit velikosti a v podstatě můžete mít ke každému příspěvku neomezený počet komentářů. Ale pojďme k něčemu skutečnému

Získání posledních 5 příspěvků a jejich posledních 3 komentářů

Toto je dvoukrokový proces. Při správném indexování (k tomu se vrátíme později) by to však mělo být rychlé (a tedy úspora zdrojů):

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

Seřadit všechny příspěvky daného uživatele od nejnovějších po nejstarší a jejich komentáře

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

Všimněte si, že zde máme pouze dva dotazy. Ačkoli musíte „ručně“ propojit příspěvky a jejich příslušné komentáře, mělo by to být docela jednoduché.

Změna uživatelského jména

Toto je pravděpodobně ojedinělý případ použití. S uvedeným datovým modelem to však není příliš složité

Nejprve změníme uživatelský dokument

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

Staré uživatelské jméno přesuneme do příslušného pole. Toto je bezpečnostní opatření pro případ, že by se s následujícími operacemi něco pokazilo. Kromě toho jsme nastavili starost o zápis na poměrně vysokou úroveň, abychom zajistili trvanlivost dat.

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

Tady nic zvláštního. Aktualizační prohlášení pro komentáře vypadá v podstatě stejně. I když tyto dotazy nějakou dobu trvají, jsou málokdy provedeny.

Indexy

Obecně lze říci, že MongoDB může použít pouze jeden index na dotaz. I když to není úplně pravda, protože existují průsečíky indexů, je snadné se s tím vypořádat. Další věcí je, že jednotlivá pole ve složeném indexu lze používat nezávisle. Jednoduchým přístupem k optimalizaci indexu je tedy najít dotaz s největším počtem polí používaných v operacích využívajících indexy a vytvořit z nich složený index. Všimněte si, že na pořadí výskytu v dotazu záleží. Tak pojďme dál.

Příspěvky

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

Komentáře

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

Závěr

Plně vložený dokument na příspěvek je nepochybně nejrychlejší způsob, jak jej načíst a jeho komentáře. Neškáluje se však dobře a vzhledem k povaze možná složitých dotazů nutných k jejímu zpracování může být tato výkonnostní výhoda využita nebo dokonce eliminována.

S výše uvedeným řešením vyměníte určitou rychlost (pokud!) za v podstatě neomezenou škálovatelnost a mnohem přímočařejší způsob nakládání s daty.

Hth.



  1. Hromadné vkládání Mongo do více sbírek

  2. Jak vizualizovat použití Resque pomocí Node.js, WebSockets a Redis

  3. ReferenceField s odkazem na existující dokument

  4. MongoDB $setIntersection