Cómo realizar consultas de unión usando Sequelize en Node.js

104

Estoy usando sequelize ORM; todo está genial y limpio, pero tuve un problema cuando lo uso con joinconsultas. Tengo dos modelos: usuarios y publicaciones.

var User = db.seq.define('User',{
    username: { type: db.Sequelize.STRING},
    email: { type: db.Sequelize.STRING},
    password: { type: db.Sequelize.STRING},
    sex : { type: db.Sequelize.INTEGER},
    day_birth: { type: db.Sequelize.INTEGER},
    month_birth: { type: db.Sequelize.INTEGER},
    year_birth: { type: db.Sequelize.INTEGER}

});

User.sync().success(function(){
    console.log("table created")
}).error(function(error){
    console.log(err);
})


var Post = db.seq.define("Post",{
    body: { type: db.Sequelize.TEXT },
    user_id: { type: db.Sequelize.INTEGER},
    likes: { type: db.Sequelize.INTEGER, defaultValue: 0 },

});

Post.sync().success(function(){
    console.log("table created")
}).error(function(error){
    console.log(err);
})

Quiero una consulta que responda con una publicación con la información del usuario que la realizó. En la consulta sin procesar, obtengo esto:

db.seq.query('SELECT * FROM posts, users WHERE posts.user_id = users.id ').success(function(rows){
            res.json(rows);
        });

Mi pregunta es ¿cómo puedo cambiar el código para usar el estilo ORM en lugar de la consulta SQL?

Jose Sosa
fuente

Respuestas:

129
User.hasMany(Post, {foreignKey: 'user_id'})
Post.belongsTo(User, {foreignKey: 'user_id'})

Post.find({ where: { ...}, include: [User]})

Que te dará

SELECT
  `posts`.*,
  `users`.`username` AS `users.username`, `users`.`email` AS `users.email`,
  `users`.`password` AS `users.password`, `users`.`sex` AS `users.sex`,
  `users`.`day_birth` AS `users.day_birth`,
  `users`.`month_birth` AS `users.month_birth`,
  `users`.`year_birth` AS `users.year_birth`, `users`.`id` AS `users.id`,
  `users`.`createdAt` AS `users.createdAt`,
  `users`.`updatedAt` AS `users.updatedAt`
FROM `posts`
  LEFT OUTER JOIN `users` AS `users` ON `users`.`id` = `posts`.`user_id`;

La consulta anterior puede parecer un poco complicada en comparación con lo que publicó, pero lo que hace básicamente es simplemente crear un alias en todas las columnas de la tabla de usuarios para asegurarse de que se coloquen en el modelo correcto cuando se devuelvan y no se mezclen con el modelo de publicaciones.

Aparte de eso, notará que hace un JOIN en lugar de seleccionar de dos tablas, pero el resultado debería ser el mismo

Otras lecturas:

Jan Aagaard Meier
fuente
7
¿Qué pasa si quiero unirme solo a los usuarios que nacieron en 1984? En SQL haría:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Iwazaru
1
Todos los enlaces están muertos no :-(
Manwal
1
¿Esa pregunta se publicó alguna vez en una pregunta respondida?
theptrk
1
¿Necesitamos los dos o uno es suficiente? User.hasMany (Post, {ForeignKey: 'user_id'}) Post.belongsTo (User, {ForeignKey: 'user_id'})
antew
1
@antew Cuando llama User.hasMany(Post), agrega métodos / atributos al objeto Usuario, y cuando llama Post.belongsTo(User), agrega métodos a la clase Post. Si siempre llama desde una dirección (por ejemplo, User.getPosts ()), no necesita agregar nada al objeto Post. Pero es bueno tener los métodos en ambos lados.
Ryan Shillington
170

Si bien la respuesta aceptada no es técnicamente incorrecta, no responde a la pregunta original ni a la pregunta de seguimiento en los comentarios, que es lo que vine aquí buscando. Pero lo descubrí, así que aquí va.

Si desea encontrar todas las publicaciones que tienen usuarios (y solo las que tienen usuarios) donde el SQL se vería así:

SELECT * FROM posts INNER JOIN users ON posts.user_id = users.id

Que es semánticamente lo mismo que el SQL original del OP:

SELECT * FROM posts, users WHERE posts.user_id = users.id

entonces esto es lo que quieres:

Posts.findAll({
  include: [{
    model: User,
    required: true
   }]
}).then(posts => {
  /* ... */
});

La configuración requerida en verdadero es la clave para producir una combinación interna. Si desea una combinación externa izquierda (donde obtiene todas las publicaciones, independientemente de si hay un usuario vinculado), cambie required a false, o déjelo desactivado, ya que es el valor predeterminado:

Posts.findAll({
  include: [{
    model: User,
//  required: false
   }]
}).then(posts => {
  /* ... */
});

Si desea encontrar todas las publicaciones que pertenecen a usuarios cuyo año de nacimiento es 1984, querrá:

Posts.findAll({
  include: [{
    model: User,
    where: {year_birth: 1984}
   }]
}).then(posts => {
  /* ... */
});

Tenga en cuenta que required es verdadero de forma predeterminada tan pronto como agregue una cláusula where en.

Si desea todas las publicaciones, independientemente de si hay un usuario adjunto, pero si hay un usuario, solo los nacidos en 1984, agregue el campo requerido nuevamente en:

Posts.findAll({
  include: [{
    model: User,
    where: {year_birth: 1984}
    required: false,
   }]
}).then(posts => {
  /* ... */
});

Si desea todas las publicaciones cuyo nombre es "Sunshine" y solo si pertenece a un usuario que nació en 1984, debe hacer lo siguiente:

Posts.findAll({
  where: {name: "Sunshine"},
  include: [{
    model: User,
    where: {year_birth: 1984}
   }]
}).then(posts => {
  /* ... */
});

Si desea todas las publicaciones cuyo nombre es "Sunshine" y solo si pertenece a un usuario que nació en el mismo año y que coincide con el atributo post_year en la publicación, debe hacer lo siguiente:

Posts.findAll({
  where: {name: "Sunshine"},
  include: [{
    model: User,
    where: ["year_birth = post_year"]
   }]
}).then(posts => {
  /* ... */
});

Lo sé, no tiene sentido que alguien haga una publicación el año en que nació, pero es solo un ejemplo, hazlo. :)

Descubrí esto (principalmente) de este documento:

Ryan Shillington
fuente
9
Muchas gracias, esto fue útil
Neo
6
sí, gran respuesta
duxfox--
entonces, ¿qué representa posts.user_id = users.iden su secuela? gracias
Hanzichi
5
Esta respuesta es tan exhaustiva que debería estar vinculada en el documento de Sequelize
Giorgio Andretti
2
Gran parte del tutorial que he estado buscando.Muchas gracias
Andrew Rayan
6
Model1.belongsTo(Model2, { as: 'alias' })

Model1.findAll({include: [{model: Model2  , as: 'alias'  }]},{raw: true}).success(onSuccess).error(onError);
Raja JLIDI
fuente
2

En mi caso hice lo siguiente. En UserMaster userId es PK y en UserAccess userId es FK de UserMaster

UserAccess.belongsTo(UserMaster,{foreignKey: 'userId'});
UserMaster.hasMany(UserAccess,{foreignKey : 'userId'});
var userData = await UserMaster.findAll({include: [UserAccess]});
Renish Gotecha
fuente