Как делать запросы на соединение с помощью Sequelize на Node.js

106

Я использую sequelize ORM; все отлично и чисто, но у меня возникла проблема, когда я использую его с joinзапросами. У меня две модели: пользователи и посты.

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

Мне нужен запрос, который отвечает сообщением с информацией о пользователе, который его сделал. В необработанном запросе я получаю следующее:

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

У меня вопрос: как изменить код, чтобы использовать стиль ORM вместо SQL-запроса?

Хосе Соса
источник

Ответы:

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

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

Что даст вам

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

Вышеупомянутый запрос может показаться немного сложным по сравнению с тем, что вы опубликовали, но в основном он просто накладывает псевдонимы всех столбцов таблицы пользователей, чтобы убедиться, что они помещены в правильную модель при возврате, а не смешиваются с моделью сообщений.

Кроме того, вы заметите, что он выполняет JOIN вместо выбора из двух таблиц, но результат должен быть таким же

Дальнейшее чтение:

Ян Аагаард Мейер
источник
8
Что, если я хочу присоединиться только к пользователям 1984 года рождения? В SQL я бы сделал:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984
Ивазару
1
Все ссылки не мертвы :-(
Manwal
1
Был ли этот вопрос когда-либо размещен в ответе на вопрос?
theptrk
1
Нужны ли они оба или одного достаточно: User.hasMany (Post, {foreignKey: 'user_id'}) Post.belongsTo (User, {foreignKey: 'user_id'})
antew
2
@antew Когда вы вызываете, User.hasMany(Post)вы добавляете методы / атрибуты к объекту User, а когда вы вызываете, Post.belongsTo(User)вы добавляете методы в класс Post. Если вы всегда звоните с одного направления (например, User.getPosts ()), вам не нужно ничего добавлять к объекту Post. Но приятно иметь методы с обеих сторон.
Райан Шиллингтон
178

Хотя принятый ответ технически не является неправильным, он не отвечает ни на исходный вопрос, ни на последующий вопрос в комментариях, что я и искал. Но я понял это, так что поехали.

Если вы хотите найти все сообщения, в которых есть пользователи (и только те, у которых есть пользователи), SQL будет выглядеть так:

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

Это семантически то же самое, что и исходный SQL OP:

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

тогда это то, что вы хотите:

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

Требуемая установка значения true является ключом к созданию внутреннего соединения. Если вам нужно левое внешнее соединение (где вы получаете все сообщения, независимо от того, есть ли связанный пользователь), измените required на false или оставьте его отключенным, поскольку это значение по умолчанию:

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

Если вы хотите найти все сообщения, принадлежащие пользователям, чей год рождения - 1984, вам нужно:

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

Обратите внимание, что required по умолчанию истинно, как только вы добавляете предложение where в.

Если вам нужны все сообщения, независимо от того, прикреплен ли к ним пользователь, но если есть пользователь, то только те, которые родились в 1984 году, добавьте необходимое поле обратно:

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

Если вам нужны все сообщения с именем «Sunshine» и только если они принадлежат пользователю, родившемуся в 1984 году, вы должны сделать следующее:

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

Если вам нужны все сообщения с именем «Sunshine» и только если они принадлежат пользователю, родившемуся в том же году, который соответствует атрибуту post_year в сообщении, вы должны сделать следующее:

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

Я знаю, что это не имеет смысла, чтобы кто-то делал пост в год своего рождения, но это просто пример - продолжайте. :)

Я понял это (в основном) из этого документа:

Райан Шиллингтон
источник
9
Большое спасибо, это было полезно
Neo
6
да, отличный ответ
duxfox
Итак, что представляет собой posts.user_id = users.idваш сиквелиз? спасибо
hanzichi
6
Этот ответ настолько исчерпывающий, что его следует указать в документе Sequelize
Джорджио Андретти,
2
Отличный урок, который я так долго искал. Большое спасибо
Эндрю Райан
6
Model1.belongsTo(Model2, { as: 'alias' })

Model1.findAll({include: [{model: Model2  , as: 'alias'  }]},{raw: true}).success(onSuccess).error(onError);
Раджа Джлиди
источник
2

В моем случае я сделал следующее. В UserMaster userId - PK, а в UserAccess userId - FK UserMaster.

UserAccess.belongsTo(UserMaster,{foreignKey: 'userId'});
UserMaster.hasMany(UserAccess,{foreignKey : 'userId'});
var userData = await UserMaster.findAll({include: [UserAccess]});
Рениш Готеча
источник