So erstellen Sie Join-Abfragen mit Sequelize auf Node.js.

104

Ich benutze Sequelize ORM; Alles ist großartig und sauber, aber ich hatte ein Problem, wenn ich es mit joinAbfragen verwende. Ich habe zwei Modelle: Benutzer und Beiträge.

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

Ich möchte eine Abfrage, die mit einem Beitrag mit den Informationen des Benutzers antwortet, der sie erstellt hat. In der Rohabfrage erhalte ich Folgendes:

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

Meine Frage ist, wie ich den Code ändern kann, um den ORM-Stil anstelle der SQL-Abfrage zu verwenden.

Jose Sosa
quelle

Antworten:

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

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

Welches wird dir geben

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`;

Die obige Abfrage sieht im Vergleich zu dem, was Sie gepostet haben, möglicherweise etwas kompliziert aus. Im Grunde genommen werden jedoch nur alle Spalten der Benutzertabelle aliasisiert, um sicherzustellen, dass sie bei der Rückgabe im richtigen Modell platziert und nicht mit dem Post-Modell verwechselt werden

Ansonsten werden Sie feststellen, dass ein JOIN ausgeführt wird, anstatt aus zwei Tabellen auszuwählen, aber das Ergebnis sollte dasselbe sein

Weiterführende Literatur:

Jan Aagaard Meier
quelle
7
Was ist, wenn ich nur Benutzern beitreten möchte, die 1984 geboren wurden? In SQL würde ich tun:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Iwazaru
1
Alle Links sind nicht tot :-(
Manwal
1
Wurde diese Frage jemals in einer beantworteten Frage veröffentlicht?
Theptrk
1
Benötigen wir beide oder einer ist genug: User.hasMany (Post, {ForeignKey: 'user_id'}) Post.belongsTo (User, {ForeignKey: 'user_id'})
Uhr
1
@antew Wenn Sie aufrufen User.hasMany(Post), fügen Sie dem Benutzerobjekt Methoden / Attribute hinzu, und wenn Sie aufrufen Post.belongsTo(User), fügen Sie der Post-Klasse Methoden hinzu. Wenn Sie immer aus einer Richtung anrufen (z. B. User.getPosts ()), müssen Sie dem Post-Objekt nichts hinzufügen. Aber es ist schön, die Methoden auf beiden Seiten zu haben.
Ryan Shillington
170

Die akzeptierte Antwort ist zwar technisch nicht falsch, beantwortet aber weder die ursprüngliche Frage noch die Folgefrage in den Kommentaren, nach denen ich hier gesucht habe. Aber ich habe es herausgefunden, also geht es los.

Wenn Sie alle Posts mit Benutzern finden möchten (und nur diejenigen mit Benutzern), in denen SQL folgendermaßen aussehen würde:

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

Das ist semantisch dasselbe wie das ursprüngliche SQL des OP:

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

dann ist es was du willst:

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

Die Einstellung true ist der Schlüssel zum Erstellen einer inneren Verknüpfung. Wenn Sie einen linken äußeren Join möchten (bei dem Sie alle Posts erhalten, unabhängig davon, ob ein Benutzer verlinkt ist), ändern Sie die erforderliche Änderung in "false" oder lassen Sie sie aus, da dies die Standardeinstellung ist:

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

Wenn Sie alle Beiträge von Benutzern finden möchten, deren Geburtsjahr 1984 liegt, möchten Sie:

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

Beachten Sie, dass erforderlich standardmäßig true ist, sobald Sie eine where-Klausel hinzufügen.

Wenn Sie alle Beiträge möchten, unabhängig davon, ob ein Benutzer angehängt ist, aber wenn ein Benutzer vorhanden ist, nur die 1984 geborenen, fügen Sie das erforderliche Feld wieder hinzu in:

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

Wenn Sie alle Posts möchten, bei denen der Name "Sunshine" lautet und die nur einem Benutzer gehören, der 1984 geboren wurde, gehen Sie folgendermaßen vor:

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

Wenn Sie alle Posts mit dem Namen "Sunshine" möchten und nur dann, wenn er einem Benutzer gehört, der im selben Jahr geboren wurde und dem Attribut "post_year" des Posts entspricht, gehen Sie folgendermaßen vor:

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

Ich weiß, es macht keinen Sinn, dass jemand in dem Jahr, in dem er geboren wurde, einen Beitrag verfasst, aber es ist nur ein Beispiel - machen Sie mit. :) :)

Ich habe dies (meistens) aus diesem Dokument herausgefunden:

Ryan Shillington
quelle
9
Vielen Dank, das war nützlich
Neo
6
Ja, tolle Antwort
Duxfox
posts.user_id = users.idAlso , was repräsentiert in deiner Fortsetzung? danke
hanzichi
5
Diese Antwort ist so vollständig, dass sie in Sequelizes Dokument
Giorgio Andretti
2
Tolles Stück Tutorial, nach dem ich gesucht habe. Vielen Dank
Andrew Rayan
6
Model1.belongsTo(Model2, { as: 'alias' })

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

In meinem Fall habe ich folgendes getan. In UserMaster ist userId PK und in UserAccess ist userId FK von UserMaster

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