Modelado One-to-Many en MongoDB

En el escenario del modelado one-to-many en MongoDB vamos a partir de dos entidades de datos en las que por cada instancia de la entidad Blog existen N elementos Comentario.

Así los documentos JSON serán:

{
  title: "El Amigo Informático",
  url: "http://elamigoinformatico.org",
  text: "El Amigo Informático que sabe Mogollón"
}
{
  name: "Carlos Que Carlos",
  created_on: ISODate("2015-12-01T10:01:22Z"),
  comment: "Me mola tu blog"
}

{
  name: "Pedro Picapiedra",
  created_on: ISODate("2015-12-01T14:15:10Z"),
  comment: "Increible trabajo"
}

Hay 3 estrategias para modelar la relación one-to-many. Por un lado podemos realizar, al igual que en el one-to-one, de la estrategia embedding y de la estrategia linking. En este caso además tenemos la estrategia bucketing.

Modelado One-to-Many Embedding en MongoDB

En este caso tendremos una sola colección de documentos que será la de los post. Por cada una de las entradas del blog vamos a tener anidados tantos comentarios como se vayan realizando. Los comentarios estarán modelados mediante un array de documentos.

{
  title: "El Amigo Informático",
  url: "http://elamigoinformatico.org",
  text: "El Amigo Informático que sabe Mogollón"
  comments: [
    {
      name: "Carlos Que Carlos",
      created_on: ISODate("2020-12-01T10:01:22Z"),
      comment: "Me mola tu blog"
    },
    {
      name: "Pedro Picapiedra",
      created_on: ISODate("2020-12-01T14:15:10Z"),
      comment: "Increible trabajo"
    }
  ]
}

Así para nuestro ejemplo vamos a crear una base de datos nueva:

use basedatosblog
var post = new Object();
post.title = 'El Amigo Informático'
post.url = 'http://elamigoinformatico.org'
post.text = 'El Amigo Informático que sabe Mogollón'

var comment1 = new Object();
comment1.name = 'Carlos Que Carlos'
comment1.created_on = ISODate('2020-12-01T10:01:22Z')
comment1.comment = 'Me mola tu blog'

var comment2 = new Object();
comment2.name = 'Pedro Picapiedra'
comment2.created_on = ISODate('2020-12-01T14:15:10Z')
comment2.comment = 'Increible trabajo'
post.comments = [comment1, comment2]

db.posts.insert(post);

La parte positiva de un modelado one-to-many en MongoDB mediante una estrategia de embedding es que una sola consulta nos va a recuperar todos los comentarios asociados a la entrada del blog.

Así si hacemos la proyección y sólo nos traemos los comentarios:

db.posts.find({title: 'El Amigo Informático'}, { _id: 0, comments: 1});

La parte negativa es que:

  • No debemos superar el máximo tamaño del documento que es de 16 Mb.
  • El rendimiento de la escritura. Al ir añadiendo subdocumentos al array MongoDB tiene que ir calculando el padding del documento y puede impactar en el rendimiento de la escritura.
  • Si tenemos muchos comentarios no podemos pedirlos de forma paginada ya que la consulta nos devolverá todos los documentos. Así que la paginación quedará de mano de la aplicación.

Modelado One-to-Many Linking en MongoDB

En este escenario vamos a crear una clave en la entidad del blog y usaremos esta clave como foreign key de los comentarios.

Así que tendremos los datos de la siguiente manera para el blog:

{
  _id:1,
  title: "El Amigo Informático",
  url: "http://elamigoinformatico.org",
  text: "El Amigo Informático que sabe Mogollón"
}

Y por otro lado cada uno de los comentarios con el _id como foreign key:

{
  blog_entry: 1,
  name: "Carlos Que Carlos",
  created_on: ISODate("2015-12-01T10:01:22Z"),
  comment: "Me mola tu blog"
}

{
  blog_entry: 1,
  name: "Pedro Picapiedra",
  created_on: ISODate("2015-12-01T14:15:10Z"),
  comment: "Increible trabajo"
}

Así para nuestro ejemplo vamos a limpiar la colección que habíamos creado previamente y crearla otra vez con la nueva estructura:

db.posts.remove({});
var post = new Object();
post._id = 1
post.title = 'El Amigo Informático'
post.url = 'http://elamigoinformatico.org'
post.text = 'El Amigo Informático que sabe Mogollón'

var comment1 = new Object();
comment1.blog_entry = 1
comment1.name = 'Carlos Que Carlos'
comment1.created_on = ISODate('2020-12-01T10:01:22Z')
comment1.comment = 'Me mola tu blog'

var comment2 = new Object();
comment2.blog_entry = 1
comment2.name = 'Pedro Picapiedra'
comment2.created_on = ISODate('2020-12-01T14:15:10Z')
comment2.comment = 'Increible trabajo' 

db.posts.insert(post);
db.comments.insert(comment1);
db.comments.insert(comment2);

Vamos a tener que realizar una lectura por cada uno de los comentarios que tenga asociada la entrada al blog. Así, si hay 1000 comentarios en el blog deberemos realizar 1000 lecturas. Si bien es verdad que permite realizar otras estrategias como la paginación de comentarios, recuperando únicamente los necesarios.

var post_id = db.posts.findOne({ title: 'El Amigo Informático'}, { _id : 1});

db.comments.find({ blog_entry: post_id._id}).forEach( function(doc) { print (doc.name + doc.comment) });

Modelado One-to-Many Bucketing en MongoDB

La estrategia de Bucketing es una estrategia intermedia entre la de embedding y la de linking. La idea es crear una colección donde vayamos insertando los documentos, si bien, esta colección tendrá un tamaño máximo y reducido.

De esta manera evitamos los problemas de padding y reposicionamiento. La colección de bucketing estará enlazada a la entidad blog mediante una estrategia de linking.

Así quedaría la colección para el post:

{
  _id:1,
  title: "El Amigo Informático",
  url: "http://elamigoinformatico.org",
  text: "El Amigo Informático que sabe Mogollón"
}

Y luego cada uno de los buckets:

{
  blog_entry: 1,
  page: 1,
  comments: [
    {
      name: "Carlos Que Carlos",
      created_on: ISODate("2015-12-01T10:01:22Z"),
      comment: "Me mola tu blog"
    },
    {}
  ]
}

{
  blog_entry: 1,
  page: 2,
  comments: [
    {
      name: "Pedro Picapiedra",
      created_on: ISODate("2015-12-01T14:15:10Z"),
      comment: "Increible trabajo"
    },
    {}
  ]
}

Así para nuestro ejemplo vamos a limpiar las colecciones que habíamos creado previamente y crearla otra vez con la nueva estructura:

db.posts.remove({});
db.comments.remove({});

var post = new Object();
post._id = 1
post.title = 'El Amigo Informático'
post.url = 'http://elamigoinformatico.org'
post.text = 'El Amigo Informático que sabe Mogollón'

var comment1 = new Object();
comment1.name = 'Carlos Que Carlos'
comment1.created_on = ISODate('2020-12-01T10:01:22Z')
comment1.comment = 'Me mola tu blog'

var bucket1 = new Object();
bucket1.blog_entry = 1
bucket1.page = 1
bucket1.comments = [comment1]

var comment2 = new Object();
comment2.name = 'Pedro Picapiedra'
comment2.created_on = ISODate('2020-12-01T14:15:10Z')
comment2.comment = 'Increible trabajo' 

var bucket2 = new Object();
bucket2.blog_entry = 1
bucket2.page = 2
bucket2.comments = [comment2]

db.posts.insert(post);
db.comments.insert(bucket1);
db.comments.insert(bucket2);

Una de las cosas que hay que hacer es identificar el indicador a utilizar en la estrategia de bucketing. Éste puede ser el número de página de comentarios (utilizado en el ejemplo) o cualquier otro indicador, por ejemplo la fecha. Siempre hay que buscar un indicador que los distribuya lo más uniformemente. A la hora de realizar la consulta habrá que usar el indicador que hayamos puesto en el bucket.

var post_id = db.posts.findOne({title: 'El Amigo Informático'}, {_id : 1});
var pageid = 2;
db.comments.find({ blog_entry: post_id._id, page : pageid}).forEach( function(doc) {
   doc.comments.forEach(function(comm)
                        {
                            print(comm.name + ' ' + comm.comment)
                        })
});

También te podría gustar...

Deja un comentario

Tu dirección de correo electrónico no será publicada.