sql >> Base de Datos >  >> NoSQL >> MongoDB

Comentarios de consulta de MongoDB junto con información del usuario

El(los) problema(s)

Como escrito antes , hay varios problemas al sobreincrustar:

Problema 1:límite de tamaño de BSON

Al momento de escribir este artículo, los documentos BSON están limitados a 16 MB . Si se alcanza ese límite, MongoDB generaría una excepción y simplemente no podría agregar más comentarios y, en el peor de los casos, ni siquiera cambiaría el nombre (de usuario) o la imagen si el cambio aumentara el tamaño del documento.

Problema 2:limitaciones y rendimiento de las consultas

No es posible consultar u ordenar fácilmente la matriz de comentarios bajo ciertas condiciones. Algunas cosas requerirían una agregación bastante costosa, otras declaraciones bastante complicadas.

Si bien se podría argumentar que una vez que se realizan las consultas, esto no es un gran problema, discrepo. En primer lugar, cuanto más complicada es una consulta, más difícil es optimizarla, tanto para el desarrollador como, posteriormente, para el optimizador de consultas de MongoDB. Obtuve los mejores resultados con la simplificación de modelos de datos y consultas, acelerando las respuestas en un factor de 100 en una instancia.

Al escalar, los recursos necesarios para consultas complicadas y/o costosas pueden incluso sumar máquinas completas en comparación con un modelo de datos más simple y consultas correspondientes.

Problema 3:Mantenibilidad

Por último, pero no menos importante, es posible que tenga problemas para mantener su código. Como regla general simple

En este contexto, "caro" se refiere tanto al dinero (para proyectos profesionales) como al tiempo (para proyectos de pasatiempos).

(¡Mi!) Solución

Es bastante fácil:simplifica tu modelo de datos. En consecuencia, sus consultas serán menos complicadas y (con suerte) más rápidas.

Paso 1:Identifique sus casos de uso

Eso va a ser una suposición descabellada para mí, pero lo importante aquí es mostrarte el método general. Definiría sus casos de uso de la siguiente manera:

  1. Para una publicación determinada, los usuarios deberían poder comentar
  2. Para una publicación determinada, muestra el autor y los comentarios, junto con el nombre de usuario de los comentaristas y autores y su imagen
  3. Para un usuario determinado, debería ser posible cambiar fácilmente el nombre, el nombre de usuario y la imagen

Paso 2:modele sus datos en consecuencia

Usuarios

En primer lugar, tenemos un modelo de usuario sencillo

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

Nada nuevo aquí, agregado solo para completar.

Publicaciones

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

Y eso es todo por un post. Hay dos cosas a tener en cuenta aquí:primero, almacenamos los datos del autor que necesitamos inmediatamente cuando mostramos una publicación, ya que esto nos ahorra una consulta para un caso de uso muy común, si no ubicuo. ¿Por qué no guardamos los comentarios y los datos de los comentaristas en consecuencia? Debido al límite de tamaño de 16 MB , estamos tratando de evitar el almacenamiento de referencias en un solo documento. Más bien, almacenamos las referencias en documentos de comentarios:

Comentarios

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

Al igual que con las publicaciones, tenemos todos los datos necesarios para mostrar una publicación.

Las consultas

Lo que hemos logrado ahora es que sorteamos el límite de tamaño de BSON y no necesitamos referirnos a los datos del usuario para poder mostrar publicaciones y comentarios, lo que debería ahorrarnos muchas consultas. Pero volvamos a los casos de uso y algunas consultas más

Agregar un comentario

Eso es totalmente sencillo ahora.

Obtener todos o algunos comentarios de una publicación determinada

Para todos los comentarios

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

Para los 3 últimos comentarios

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

Entonces, para mostrar una publicación y todos (o algunos) de sus comentarios, incluidos los nombres de usuario y las imágenes, tenemos dos consultas. Más de lo que necesitaba antes, pero sorteamos el límite de tamaño y, básicamente, puede tener un número indefinido de comentarios para cada publicación. Pero vayamos a algo real

Obtener las últimas 5 publicaciones y sus últimos 3 comentarios

Este es un proceso de dos pasos. Sin embargo, con una indexación adecuada (volveremos a eso más adelante), esto aún debería ser rápido (y, por lo tanto, ahorrar 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);
  }
)

Obtenga todas las publicaciones de un usuario determinado ordenadas de la más reciente a la más antigua y sus comentarios

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

Tenga en cuenta que solo tenemos dos consultas aquí. Aunque debe hacer la conexión "manualmente" entre las publicaciones y sus respectivos comentarios, eso debería ser bastante sencillo.

Cambiar un nombre de usuario

Presumiblemente, este es un caso de uso raro ejecutado. Sin embargo, no es muy complicado con dicho modelo de datos

Primero, cambiamos el documento de usuario

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

Empujamos el antiguo nombre de usuario a una matriz correspondiente. Esta es una medida de seguridad en caso de que algo salga mal con las siguientes operaciones. Además, establecemos la preocupación de escritura en un nivel bastante alto para asegurarnos de que los datos sean duraderos.

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

Nada especial aquí. La declaración de actualización para los comentarios se ve más o menos igual. Si bien esas consultas toman algún tiempo, rara vez se ejecutan.

Los índices

Como regla general, se puede decir que MongoDB solo puede usar un índice por consulta. Si bien esto no es del todo cierto ya que hay intersecciones de índice, es fácil de manejar. Otra cosa es que los campos individuales en un índice compuesto se pueden usar de forma independiente. Por lo tanto, un enfoque fácil para la optimización de índices es encontrar la consulta con la mayoría de los campos utilizados en operaciones que utilizan índices y crear un índice compuesto de ellos. Tenga en cuenta que el orden de aparición en la consulta es importante. Entonces, sigamos adelante.

Publicaciones

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

Comentarios

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

Conclusión

Es cierto que un documento completamente incrustado por publicación es la forma más rápida de cargarlo y sus comentarios. Sin embargo, no escala bien y debido a la naturaleza de las consultas posiblemente complejas necesarias para manejarlo, esta ventaja de rendimiento puede aprovecharse o incluso eliminarse.

Con la solución anterior, intercambia algo de velocidad (¡si!) contra escalabilidad básicamente ilimitada y una forma mucho más sencilla de manejar los datos.

Hth.