2013-12-08 7 views
35

Sto usando sequelize ORM; tutto è bello e pulito, ma ho avuto un problema quando lo uso con le query join. Ho due modelli: utenti e post.Come rendere le query di join utilizzando sequelize in nodejs

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

Desidero una query che risponda a un post con le informazioni dell'utente che l'ha effettuata. Nella query grezzo, ottengo questo:

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

mia domanda è come posso cambiare il codice per utilizzare lo stile ORM anziché la query SQL?

risposta

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

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

che vi 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 'complicato rispetto a quello che hai postato, ma ciò che fa è fondamentalmente solo aliasing tutte le colonne della tabella utenti per assicurarsi che essi vengono inseriti nel modello corretto quando vengono restituiti e non sono confusi con i post modello

Oltre a ciò, noterete che fa un JOIN invece di selezionare da due tabelle, ma il risultato dovrebbe essere lo stesso

Ulteriori approfondimenti:

+0

Grazie uomo, che le grandi opere. –

+6

Cosa succede se desidero unire solo utenti nati nel 1984? In SQL vorrei fare: 'SELECT * FROM posts UNISCITI utenti ON users.id = posts.user_id WHERE users.year_birth = 1984' – Iwazaru

+0

@Iwazaru La risposta sarebbe troppo lunga per rientrare in un commento, si prega di aprire una nuova domanda –

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

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

Mentre la risposta accettata non è tecnicamente sbagliato, non risponde alla domanda originale né la follow-up domanda nei commenti, che è stato quello che sono venuto qui cercando. Ma l'ho capito, quindi ecco qui.

Se si desidera trovare tutti i messaggi che hanno gli utenti (e solo quelli che hanno gli utenti) in cui l'SQL sarebbe simile a questa:

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

Che è semanticamente la stessa cosa di SQL originale del PO:

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

allora questo è ciò che si vuole:

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

impostazione desiderata tR È la chiave per produrre un'unione interiore. Se si desidera un join esterno sinistro (dove si ottiene tutti i messaggi, indipendentemente dal fatto che c'è un utente collegato), quindi cambiamento necessario per falso, o lasciare fuori dato che è il default:

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

Se si desidera trovare tutte Messaggi appartenenti a utenti il ​​cui anno di nascita è nel 1984, che ci si vuole:

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

Nota che ha richiesto è true per impostazione predefinita, non appena si aggiunge una clausola WHERE.

Se si desidera che tutti i messaggi, indipendentemente dal fatto che c'è un utente collegato, ma se c'è un utente quindi solo quelli nati nel 1984, quindi aggiungere il campo richiesto nel:

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

Se si desidera che tutti Messaggi in cui il nome è "Sunshine" e solo se appartiene a un utente che è nata nel 1984, si farebbero questo:

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

Se si desidera che tutti i messaggi in cui il nome è "Sunshine" e solo se appartiene ad un utente nato nello stesso anno che corrisponde all'attributo post_year sul post, avrebbe fatto questo:

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

Lo so, non ha senso che qualcuno potesse fare un post l'anno in cui sono nati, ma è solo un esempio - andare con lui. :)

ho capito questo (soprattutto) da questo documento: