如何在Node.js上使用Sequelize进行联接查询

168

我正在使用 Sequelize ORM,一切都很好和干净,但当我使用联接查询时遇到了问题。 我有两个模型:用户和帖子。

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查询?


相关:https://dev59.com/7GAg5IYBdhLWcg3w9fDm - Ciro Santilli OurBigBook.com
5个回答

284
尽管被接受的答案在技术上没有错误,但它并没有回答原始问题或评论中的后续问题,而我来这里是寻找这些答案的。但我已经想出来了,下面是解决方法。
如果您想查找所有具有用户(仅具有用户)的帖子,则 SQL 将如下所示:
SELECT * FROM posts INNER JOIN users ON posts.user_id = users.id

这与原作者的 SQL 在语义上是相同的:

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

那么这就是你想要的:

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

将required设置为true是生成内连接的关键。如果您想要一个左外连接(其中您会获得所有帖子,而不管是否有链接的用户),则将required更改为false,或者将其省略,因为那是默认设置:

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

如果您想查找所有出生年份为1984年的用户所发布的所有帖子,您需要:

Posts.findAll({
  include: [{
    model: User,
    where: {year_birth: 1984}
   }]
}).then(posts => {
  /* ... */
});
请注意,只要在where子句中添加了条件,required默认为true。
如果您想获取所有帖子,而不管是否有用户附加,但如果有用户,则仅获取1984年出生的帖子,则需要将required字段添加回去。
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 => {
  /* ... */
});

我知道,某人在出生年份时发帖子似乎没有意义,但这只是一个例子 - 请接受它。 :)

我主要从这个文档中找到了答案:


10
非常感谢,这很有用。 - Neo
6
是的,非常好的回答。 - Abdul Ahmad
8
你好,posts.user_id = users.id 在 Sequelize 中代表什么意思?谢谢。 - hanzichi
13
这个答案非常详尽,应该被链接到 Sequelize 的文档中。 - Giorgio Andretti
2
@JasonG 你需要 "attributes" 键。在这个文档中搜索 "attributes": https://sequelize.org/master/manual/model-querying-basics.html - Ryan Shillington
显示剩余7条评论

172
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而不是从两个表中选择,但结果应该是相同的。

进一步阅读:


16
如果我只想加入1984年出生的用户,那该怎么办?在SQL中,我会这样写:SELECT * FROM posts JOIN users ON users.id = posts.user_id WHERE users.year_birth = 1984 - clemlatz
1
所有链接都已失效,不开心 :-( - Manwal
1
那个问题是否曾经被发布在已回答的问题中? - theptrk
1
我们需要两个还是一个就足够了: User.hasMany(Post, {foreignKey: 'user_id'}) Post.belongsTo(User, {foreignKey: 'user_id'}) - antew
2
当您调用User.hasMany(Post)时,您会向User对象添加方法/属性,而当您调用Post.belongsTo(User)时,您会向Post类添加方法。如果您总是从一个方向调用(例如User.getPosts()),则不需要向Post对象添加任何内容。但是在两侧都拥有方法很好。 - Ryan Shillington
显示剩余10条评论

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

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

3
在我的情况下,我做了以下的事情。在UserMaster中,userId 是主键,在UserAccess中,userId 是UserMaster的外键。
UserAccess.belongsTo(UserMaster,{foreignKey: 'userId'});
UserMaster.hasMany(UserAccess,{foreignKey : 'userId'});
var userData = await UserMaster.findAll({include: [UserAccess]});

0
在模型文件中创建关联,然后使用 include 进行连接,你也可以使用内部 include 来连接表。

----model.js

blog1.hasMany(blog2, {foreignKey: 'blog_id'})
blog2.belongsTo(blog1, {foreignKey: 'blog_id'})

-----controller.js

blog2.find({ where: {blog_id:1}, include: [blog1]})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接