ID photo of Ciro Santilli taken in 2013 right eyeCiro Santilli OurBigBook logoOurBigBook.com  Sponsor 中国独裁统治 China Dictatorship 新疆改造中心、六四事件、法轮功、郝海东、709大抓捕、2015巴拿马文件 邓家贵、低端人口、西藏骚乱
nodejs/sequelize/many_to_many_double.js
#!/usr/bin/env node

// https://cirosantilli.com/sequelize-example

const assert = require('assert');
const path = require('path');
const { DataTypes } = require('sequelize');
const common = require('./common')
const sequelize = common.sequelize(__filename, process.argv[2], { define: { timestamps: false } })
;(async () => {

// Create the tables.
const User = sequelize.define('User', {
  name: { type: DataTypes.STRING },
}, {});
const Post = sequelize.define('Post', {
  body: { type: DataTypes.STRING },
}, {});

User.belongsToMany(Post, {through: 'UserLikesPost', as: 'likedPosts'});
Post.belongsToMany(User, {through: 'UserLikesPost', as: 'likers'});

User.belongsToMany(Post, {through: 'UserFollowsPost', as: 'followedPosts'});
Post.belongsToMany(User, {through: 'UserFollowsPost', as: 'followers'});

await sequelize.sync({force: true});

// Create some users and likes.

const user0 = await User.create({name: 'user0'})
const user1 = await User.create({name: 'user1'})
const user2 = await User.create({name: 'user2'})

const post0 = await Post.create({body: 'post0'});
const post1 = await Post.create({body: 'post1'});
const post2 = await Post.create({body: 'post2'});

// Autogenerated add* methods

// Setup likes and follows.
await user0.addLikedPost(post0)
await post1.addLikers([user0, user2])
await user1.addFollowedPosts([post0, post1])
await post1.addFollower(user2)

// Autogenerated get* methods

// Get likes by a user.

const user0Likes = await user0.getLikedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user0Likes[0].body, 'post0');
assert.strictEqual(user0Likes[1].body, 'post1');
assert.strictEqual(user0Likes.length, 2);

const user1Likes = await user1.getLikedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user1Likes.length, 0);

const user2Likes = await user2.getLikedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user2Likes[0].body, 'post1');
assert.strictEqual(user2Likes.length, 1);

// Get users that liked a given post.

const post0Likers = await post0.getLikers({order: [['name', 'ASC']]})
assert.strictEqual(post0Likers[0].name, 'user0');
assert.strictEqual(post0Likers.length, 1);

const post1Likers = await post1.getLikers({order: [['name', 'ASC']]})
assert.strictEqual(post1Likers[0].name, 'user0');
assert.strictEqual(post1Likers[1].name, 'user2');
assert.strictEqual(post1Likers.length, 2);

const post2Likers = await post2.getLikers({order: [['name', 'ASC']]})
assert.strictEqual(post2Likers.length, 0);

// Get follows by a user.

const user0Follows = await user0.getFollowedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user0Follows.length, 0);

const user1Follows = await user1.getFollowedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user1Follows[0].body, 'post0');
assert.strictEqual(user1Follows[1].body, 'post1');
assert.strictEqual(user1Follows.length, 2);

const user2Follows = await user2.getFollowedPosts({order: [['body', 'ASC']]})
assert.strictEqual(user2Follows[0].body, 'post1');
assert.strictEqual(user2Follows.length, 1);

// Get users that followed a given post.

const post0Followers = await post0.getFollowers({order: [['name', 'ASC']]})
assert.strictEqual(post0Followers[0].name, 'user1');
assert.strictEqual(post0Followers.length, 1);

const post1Followers = await post1.getFollowers({order: [['name', 'ASC']]})
assert.strictEqual(post1Followers[0].name, 'user1');
assert.strictEqual(post1Followers[1].name, 'user2');
assert.strictEqual(post1Followers.length, 2);

const post2Followers = await post2.getFollowers({order: [['name', 'ASC']]})
assert.strictEqual(post2Followers.length, 0);

// Same as getLikedPosts but with the user ID instead of the model object.
// as is mandatory to disambiguate which one we want to get.
{
  const user0Likes = await Post.findAll({
    include: [{
      model: User,
      as: 'likers',
      where: {id: user0.id},
    }],
    order: [['body', 'ASC']],
  })
  assert.strictEqual(user0Likes[0].body, 'post0');
  assert.strictEqual(user0Likes[1].body, 'post1');
  assert.strictEqual(user0Likes.length, 2);
}

// Alternatively, we can also pass the association object instead of model + as.
// This is actually nicer!
{
  const user0Likes = await Post.findAll({
    include: [{
      association: Post.associations.likers,
      where: {id: user0.id},
    }],
    order: [['body', 'ASC']],
  })
  assert.strictEqual(user0Likes[0].body, 'post0');
  assert.strictEqual(user0Likes[1].body, 'post1');
  assert.strictEqual(user0Likes.length, 2);
}

// Yet another way that can be more useful in nested includes.
{
  const user0Likes = (await User.findOne({
    where: {id: user0.id},
    include: [{
      model: Post,
      as: 'likedPosts',
    }],
    order: [[{model: Post, as: 'likedPosts'}, 'body', 'ASC']],
  })).likedPosts
  assert.strictEqual(user0Likes[0].body, 'post0');
  assert.strictEqual(user0Likes[1].body, 'post1');
  assert.strictEqual(user0Likes.length, 2);
}

})().finally(() => { return sequelize.close() });