Jak tworzyć zapytania łączące za pomocą Sequelize na Node.js.

104

Używam sequelize ORM; wszystko jest świetne i czyste, ale miałem problem, gdy używam go do joinzapytań. Mam dwa modele: użytkowników i postów.

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

Potrzebuję zapytania, które odpowiada postem z informacjami o użytkowniku, który go utworzył. W surowym zapytaniu otrzymuję to:

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

Moje pytanie brzmi: jak mogę zmienić kod, aby używał stylu ORM zamiast zapytania SQL?

Jose Sosa
źródło

Odpowiedzi:

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

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

Co ci da

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

Powyższe zapytanie może wyglądać na nieco skomplikowane w porównaniu z tym, co opublikowałeś, ale to, co robi, polega po prostu na aliasowaniu wszystkich kolumn tabeli użytkowników, aby upewnić się, że są one umieszczone we właściwym modelu po zwróceniu i nie są pomieszane z modelem postów

Poza tym zauważysz, że wykonuje JOIN zamiast wybierania z dwóch tabel, ale wynik powinien być taki sam

Dalsze czytanie:

Jan Aagaard Meier
źródło
7
A jeśli chcę dołączyć tylko do Użytkowników urodzonych w 1984 roku? W SQL zrobiłbym:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Iwazaru
1
Wszystkie linki są martwe, nie :-(
Manwal
1
Czy to pytanie zostało kiedykolwiek opublikowane w odpowiedzi na pytanie?
theptrk
1
Czy potrzebujemy obu, czy wystarczy jedno: User.hasMany (Post, {ForeignKey: 'user_id'}) Post.belongsTo (User, {ForeignKey: 'user_id'})
antew
1
@antew Kiedy wywołujesz User.hasMany(Post), dodajesz metody / atrybuty do obiektu User, a kiedy wywołujesz Post.belongsTo(User), dodajesz metody do klasy Post. Jeśli zawsze dzwonisz z jednego kierunku (np. User.getPosts ()), nie musisz niczego dodawać do obiektu Post. Ale miło jest mieć metody po obu stronach.
Ryan Shillington,
170

Chociaż zaakceptowana odpowiedź nie jest technicznie zła, nie odpowiada na pierwotne pytanie ani na pytanie uzupełniające w komentarzach, czego tutaj szukałem. Ale wymyśliłem to, więc proszę.

Jeśli chcesz znaleźć wszystkie posty, które mają użytkowników (i tylko te, które mają użytkowników), w których SQL wyglądałby tak:

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

Co jest semantycznie tym samym, co oryginalny SQL OP:

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

to jest to, czego chcesz:

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

Ustawienie wymagane do true jest kluczem do utworzenia sprzężenia wewnętrznego. Jeśli chcesz, aby lewe sprzężenie zewnętrzne (gdzie otrzymujesz wszystkie posty, niezależnie od tego, czy jest połączony użytkownik), zmień wymagane na false lub zostaw je wyłączone, ponieważ jest to ustawienie domyślne:

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

Jeśli chcesz znaleźć wszystkie posty należące do użytkowników, których rok urodzenia przypada na 1984, możesz:

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

Zauważ, że required jest domyślnie prawdziwe, gdy tylko dodasz klauzulę where w.

Jeśli chcesz mieć wszystkie posty, niezależnie od tego, czy jest dołączony użytkownik, ale jeśli jest użytkownik, to tylko te urodzone w 1984 r., Dodaj wymagane pole z powrotem:

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

Jeśli chcesz mieć wszystkie posty, które mają na imię „Sunshine” i tylko jeśli należą do użytkownika, który urodził się w 1984 roku, zrób to:

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

Jeśli chcesz mieć wszystkie posty, których nazwa brzmi „Sunshine” i tylko jeśli należą do użytkownika, który urodził się w tym samym roku, który jest zgodny z atrybutem post_year w poście, wykonaj następujące czynności:

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

Wiem, to nie ma sensu, żeby ktoś zrobił wpis w roku urodzenia, ale to tylko przykład - idź z nim. :)

Odkryłem to (głównie) z tego dokumentu:

Ryan Shillington
źródło
9
Dziękuję bardzo, to było przydatne
Neo
6
tak, świetna odpowiedź
duxfox
więc co reprezentuje posts.user_id = users.idtwój sequelize? dzięki
hanzichi
5
Ta odpowiedź jest tak wyczerpująca, że ​​powinna być połączona w dokumencie Sequelize
Giorgio Andretti
2
Świetny tutorial, którego szukałem. Wielkie dzięki
Andrew Rayan
6
Model1.belongsTo(Model2, { as: 'alias' })

Model1.findAll({include: [{model: Model2  , as: 'alias'  }]},{raw: true}).success(onSuccess).error(onError);
Raja JLIDI
źródło
2

W moim przypadku zrobiłem następującą rzecz. W UserMaster userid jest PK w UserAccess userId jest FK z UserMaster

UserAccess.belongsTo(UserMaster,{foreignKey: 'userId'});
UserMaster.hasMany(UserAccess,{foreignKey : 'userId'});
var userData = await UserMaster.findAll({include: [UserAccess]});
Renish Gotecha
źródło