Comment créer des requêtes de jointure à l'aide de Sequelize sur Node.js

104

J'utilise sequelize ORM; tout est super et propre, mais j'ai eu un problème lorsque je l'utilise avec des joinrequêtes. J'ai deux modèles: les utilisateurs et les messages.

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

Je veux une requête qui répond avec un message avec les informations de l'utilisateur qui l'a créé. Dans la requête brute, j'obtiens ceci:

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

Ma question est de savoir comment modifier le code pour utiliser le style ORM au lieu de la requête SQL?

José Sosa
la source

Réponses:

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

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

Ce qui vous donnera

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 requête ci-dessus peut sembler un peu compliquée par rapport à ce que vous avez publié, mais ce qu'elle fait, c'est simplement aliaser toutes les colonnes de la table des utilisateurs pour s'assurer qu'elles sont placées dans le bon modèle lorsqu'elles sont renvoyées et ne sont pas mélangées avec le modèle des articles.

En dehors de cela, vous remarquerez qu'il fait un JOIN au lieu de sélectionner parmi deux tables, mais le résultat devrait être le même

Lectures complémentaires:

Jan Aagaard Meier
la source
7
Que faire si je souhaite rejoindre uniquement les utilisateurs nés en 1984? En SQL je ferais:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Iwazaru
1
Tous les liens ne sont pas morts :-(
Manwal
1
Cette question a-t-elle déjà été publiée dans une question avec réponse?
theptrk
1
Avons-nous besoin des deux ou un est suffisant: User.hasMany (Post, {ForeignKey: 'user_id'}) Post.belongsTo (User, {ForeignKey: 'user_id'})
antew
1
@antew Lorsque vous appelez, User.hasMany(Post)vous ajoutez des méthodes / attributs à l'objet User, et lorsque vous appelez, Post.belongsTo(User)vous ajoutez des méthodes à la classe Post. Si vous appelez toujours d'une direction (ex. User.getPosts ()), vous n'avez rien à ajouter à l'objet Post. Mais c'est bien d'avoir les méthodes des deux côtés.
Ryan Shillington le
170

Bien que la réponse acceptée ne soit pas techniquement fausse, elle ne répond pas à la question initiale ni à la question de suivi dans les commentaires, ce que je recherchais ici. Mais je l'ai compris, alors voilà.

Si vous voulez trouver tous les articles qui ont des utilisateurs (et seulement ceux qui ont des utilisateurs) où le SQL ressemblerait à ceci:

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

Ce qui est sémantiquement la même chose que le SQL original de l'OP:

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

alors c'est ce que vous voulez:

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

La définition requise sur true est la clé pour produire une jointure interne. Si vous voulez une jointure externe gauche (où vous obtenez toutes les publications, qu'il y ait ou non un utilisateur lié), remplacez-la obligatoire par false ou laissez-la désactivée car c'est la valeur par défaut:

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

Si vous voulez trouver tous les messages appartenant aux utilisateurs dont l'année de naissance est 1984, vous voudrez:

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

Notez que obligatoire est true par défaut dès que vous ajoutez une clause where dans.

Si vous voulez tous les messages, qu'il y ait ou non un utilisateur attaché, mais s'il y a un utilisateur, seuls ceux nés en 1984, puis rajoutez le champ requis dans:

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

Si vous voulez tous les articles dont le nom est "Sunshine" et seulement s'ils appartiennent à un utilisateur né en 1984, vous feriez ceci:

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

Si vous voulez que toutes les publications dont le nom est "Sunshine" et uniquement si elles appartiennent à un utilisateur né la même année et qui correspondent à l'attribut post_year sur la publication, procédez comme suit:

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

Je sais, cela n'a pas de sens que quelqu'un publie un message l'année de sa naissance, mais ce n'est qu'un exemple - allez-y. :)

J'ai compris cela (principalement) à partir de ce document:

Ryan Shillington
la source
9
Merci beaucoup, cela a été utile
Neo
6
oui, bonne réponse
duxfox--
alors qu'est-ce que cela représente posts.user_id = users.iddans votre séquelle? merci
hanzichi
5
Cette réponse est si exhaustive qu'elle devrait être liée dans le document de Sequelize
Giorgio Andretti
2
Super morceau de tutoriel que je cherchais.Merci beaucoup
Andrew Rayan
6
Model1.belongsTo(Model2, { as: 'alias' })

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

Dans mon cas, j'ai fait la chose suivante. Dans UserMaster userId est PK et dans UserAccess userId est FK de UserMaster

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