Come eseguire query di join utilizzando Sequelize su Node.js


104

Sto usando sequelize ORM; tutto è fantastico e pulito, ma ho avuto un problema quando lo uso con le joinquery. Ho due modelli: utenti e post.

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

Voglio una query che risponda con un post con le informazioni dell'utente che l'ha creata. Nella query grezza, ottengo questo:

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

La mia domanda è: come posso modificare il codice per utilizzare lo stile ORM invece della query SQL?

Risposte:


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

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

Che ti 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 query sopra potrebbe sembrare un po 'complicata rispetto a ciò che hai pubblicato, ma ciò che fa è fondamentalmente solo l'alias di tutte le colonne della tabella degli utenti per assicurarti che siano posizionate nel modello corretto quando restituite e non confuse con il modello dei post

Oltre a questo noterai che esegue un JOIN invece di selezionare da due tabelle, ma il risultato dovrebbe essere lo stesso

Ulteriore lettura:


7
E se volessi unirmi solo a Utenti nati nel 1984? In SQL farei:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Iwazaru

1
Tutti i link non sono morti :-(
Manwal

1
Quella domanda è mai stata inserita in una domanda con risposta?
theptrk

1
Abbiamo bisogno di entrambi o uno è sufficiente: User.hasMany (Post, {foreignKey: 'user_id'}) Post.belongsTo (User, {foreignKey: 'user_id'})
antew

1
@antew Quando chiami User.hasMany(Post)aggiungi metodi / attributi all'oggetto User e quando chiami Post.belongsTo(User)aggiungi metodi alla classe Post. Se chiami sempre da una direzione (es. User.getPosts ()), non è necessario aggiungere nulla all'oggetto Post. Ma è bello avere i metodi su entrambi i lati.
Ryan Shillington

170

Sebbene la risposta accettata non sia tecnicamente sbagliata, non risponde alla domanda originale né alla domanda successiva nei commenti, che era quello che cercavo qui. Ma l'ho capito, quindi ecco qua.

Se vuoi trovare tutti i post che hanno utenti (e solo quelli che hanno utenti) in cui l'SQL sarebbe simile a questo:

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

Che è semanticamente la stessa cosa dell'SQL originale dell'OP:

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

allora questo è quello che vuoi:

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

L'impostazione richiesta su true è la chiave per produrre un inner join. Se desideri un join esterno sinistro (in cui ottieni tutti i post, indipendentemente dal fatto che sia collegato un utente), cambia richiesto in false o lascialo disattivato poiché è l'impostazione predefinita:

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

Se desideri trovare tutti i post appartenenti agli utenti il ​​cui anno di nascita è 1984, ti consigliamo di:

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

Nota che required è vero per impostazione predefinita non appena aggiungi una clausola where.

Se vuoi tutti i post, indipendentemente dal fatto che ci sia un utente allegato ma se c'è un utente, allora solo quelli nati nel 1984, quindi aggiungi di nuovo il campo richiesto in:

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

Se vuoi tutti i post il cui nome è "Sunshine" e solo se appartiene a un utente nato nel 1984, dovresti farlo:

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

Se desideri tutti i post in cui il nome è "Sunshine" e solo se appartiene a un utente nato nello stesso anno che corrisponde all'attributo post_year del post, dovresti farlo:

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

Lo so, non ha senso che qualcuno faccia un post l'anno in cui sono nati, ma è solo un esempio, seguilo. :)

L'ho capito (principalmente) da questo documento:


9
Grazie mille, è stato utile
Neo

6
sì, ottima risposta
duxfox--

quindi cosa rappresenta posts.user_id = users.idnel tuo sequelize? grazie
hanzichi

5
Questa risposta è così esaustiva che dovrebbe essere collegata al documento di Sequelize
Giorgio Andretti

2
Ottimo tutorial che stavo cercando. Grazie mille
Andrew Rayan

6
Model1.belongsTo(Model2, { as: 'alias' })

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

2

Nel mio caso ho fatto la seguente cosa. In UserMaster userId è PK e in UserAccess userId è FK di UserMaster

UserAccess.belongsTo(UserMaster,{foreignKey: 'userId'});
UserMaster.hasMany(UserAccess,{foreignKey : 'userId'});
var userData = await UserMaster.findAll({include: [UserAccess]});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.