MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Comentários de consulta do MongoDB junto com informações do usuário

O(s) problema(s)


Como escrito antes , há vários problemas ao incorporar em excesso:

Problema 1:limite de tamanho BSON


No momento da redação deste artigo, documentos BSON são limitados a 16 MB . Se esse limite for atingido, o MongoDB lançará uma exceção e você simplesmente não poderá adicionar mais comentários e, nos piores cenários, nem mesmo alterar o nome (de usuário) ou a imagem se a alteração aumentar o tamanho do documento.

Problema 2:Limitações e desempenho da consulta


Não é fácil consultar ou classificar a matriz de comentários sob certas condições. Algumas coisas exigiriam uma agregação bastante cara, outras declarações bastante complicadas.

Embora se possa argumentar que, uma vez que as consultas estejam em vigor, isso não é um grande problema, discordo. Primeiro, quanto mais complicada for uma consulta, mais difícil será otimizar, tanto para o desenvolvedor quanto para o otimizador de consulta do MongoDBs. Tive os melhores resultados com modelos de dados e consultas simplificados, acelerando as respostas por um fator de 100 em uma instância.

Ao dimensionar, os recursos necessários para consultas complicadas e/ou caras podem até somar máquinas inteiras quando comparados a um modelo de dados mais simples e consultas correspondentes.

Problema 3:Manutenibilidade


Por último, mas não menos importante, você pode ter problemas para manter seu código. Como uma regra simples

Nesse contexto, "caro" refere-se tanto a dinheiro (para projetos profissionais) quanto a tempo (para projetos de hobby).

(Minha!) Solução


É muito fácil:simplifique seu modelo de dados. Consequentemente, suas consultas se tornarão menos complicadas e (espero) mais rápidas.

Etapa 1:identifique seus casos de uso


Isso vai ser um palpite para mim, mas o importante aqui é mostrar o método geral. Eu definiria seus casos de uso da seguinte forma:
  1. Para uma determinada postagem, os usuários devem poder comentar
  2. Para uma determinada postagem, mostre o autor e os comentários, juntamente com o nome de usuário dos comentadores e autores e sua foto
  3. Para um determinado usuário, deve ser fácil alterar o nome, nome de usuário e imagem

Etapa 2:modele seus dados adequadamente

Usuários


Em primeiro lugar, temos um modelo de usuário simples
{
  _id: new ObjectId(),
  name: "Joe Average",
  username: "HotGrrrl96",
  picture: "some_link"
}

Nada de novo aqui, adicionado apenas para completar.

Postagens

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

E é sobre isso para um post. Há duas coisas a serem observadas aqui:primeiro, armazenamos os dados do autor de que precisamos imediatamente ao exibir uma postagem, pois isso nos economiza uma consulta para um caso de uso muito comum, se não onipresente. Por que não salvamos os comentários e os dados dos comentadores de acordo? Devido ao limite de tamanho de 16 MB , estamos tentando impedir o armazenamento de referências em um único documento. Em vez disso, armazenamos as referências em documentos de comentários:

Comentários

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

Assim como nas postagens, temos todos os dados necessários para a exibição de uma postagem.

As consultas


O que conseguimos agora é que contornamos o limite de tamanho do BSON e não precisamos consultar os dados do usuário para poder exibir postagens e comentários, o que deve nos poupar muitas consultas. Mas vamos voltar aos casos de uso e mais algumas consultas

Adicionando um comentário


Isso é totalmente direto agora.

Receber todos ou alguns comentários para uma determinada postagem


Para todos os comentários
db.comments.find({post:objectIdOfPost})

Para os 3 últimos comentários
db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)

Portanto, para exibir uma postagem e todos (ou alguns) seus comentários, incluindo nomes de usuários e fotos, estamos em duas consultas. Mais do que você precisava antes, mas contornamos o limite de tamanho e basicamente você pode ter um número indefinido de comentários para cada postagem. Mas vamos a algo real

Receber as últimas 5 postagens e seus últimos 3 comentários


Este é um processo de duas etapas. No entanto, com a indexação adequada (voltará a isso mais tarde), isso ainda deve ser rápido (e, portanto, economizar recursos):
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);
  }
)

Obtenha todas as postagens de um determinado usuário classificadas da mais recente para a mais antiga e seus comentários

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

Observe que temos apenas duas consultas aqui. Embora você precise "manualmente" fazer a conexão entre as postagens e seus respectivos comentários, isso deve ser bastante simples.

Alterar um nome de usuário


Este é presumivelmente um caso de uso raro executado. No entanto, não é muito complicado com o referido modelo de dados

Primeiro, alteramos o documento do usuário
db.users.update(
  { username: "HotGrrrl96"},
  {
    $set: { username: "Joe Cool"},
    $push: {oldUsernames: "HotGrrrl96" }
  },
  {
    writeConcern: {w: "majority"}
  }
);

Empurramos o nome de usuário antigo para um array de acordo. Esta é uma medida de segurança caso algo dê errado com as seguintes operações. Além disso, definimos a preocupação de gravação em um nível bastante alto para garantir que os dados sejam duráveis.
db.posts.update(
  { "author.username": "HotGrrrl96"},
  { $set:{ "author.username": "Joe Cool"} },
  {
    multi:true,
    writeConcern: {w:"majority"}
  }
)

Nada de especial aqui. A declaração de atualização para os comentários parece praticamente a mesma. Embora essas consultas levem algum tempo, raramente são executadas.

Os índices


Como regra geral, pode-se dizer que o MongoDB pode usar apenas um índice por consulta. Embora isso não seja totalmente verdade, pois existem interseções de índice, é fácil lidar com isso. Outra coisa é que campos individuais em um índice composto podem ser usados ​​independentemente. Portanto, uma abordagem fácil para a otimização de índices é encontrar a consulta com mais campos usados ​​em operações que fazem uso de índices e criar um índice composto deles. Observe que a ordem de ocorrência na consulta é importante. Então, vamos em frente.

Postagens

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

Comentários

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

Conclusão


Um documento totalmente incorporado por postagem é reconhecidamente a maneira mais rápida de carregá-lo e seus comentários. No entanto, ele não escala bem e devido à natureza de consultas possivelmente complexas necessárias para lidar com ele, essa vantagem de desempenho pode ser aproveitada ou até mesmo eliminada.

Com a solução acima, você troca alguma velocidade (se!) contra escalabilidade basicamente ilimitada e uma maneira muito mais direta de lidar com os dados.

H.