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

// Like association_many_to_many_same_model but with a super many to many,
// i.e. explicit through table relations).

const assert = require('assert');
const path = require('path');
const { DataTypes, Op } = 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 UserFollowUser = sequelize.define('UserFollowUser', {
    UserId: {
      type: DataTypes.INTEGER,
      references: {
        model: User,
        key: 'id'
      }
    },
    FollowId: {
      type: DataTypes.INTEGER,
      references: {
        model: User,
        key: 'id'
      }
    },
  }
);

// Super many to many. Only works with explicit table for some reason.
User.belongsToMany(User, {through: UserFollowUser, as: 'Follows'});
UserFollowUser.belongsTo(User)
User.hasMany(UserFollowUser)

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

// Create some users.

const user0 = await User.create({name: 'user0'})
const user1 = await User.create({name: 'user1'})
const user2 = await User.create({name: 'user2'})
const user3 = await User.create({name: 'user3'})
await user0.addFollows([user1, user2])
await user2.addFollow(user0)
await user3.addFollow(user0)

// Find all users that a user follows.
const user0Follows = await user0.getFollows({order: [['name', 'ASC']]})
assert.strictEqual(user0Follows[0].name, 'user1');
assert.strictEqual(user0Follows[1].name, 'user2');
assert.strictEqual(user0Follows.length, 2);

const user1Follows = await user1.getFollows({order: [['name', 'ASC']]})
assert.strictEqual(user1Follows.length, 0);

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

const user3Follows = await user3.getFollows({order: [['name', 'ASC']]})
assert.strictEqual(user3Follows[0].name, 'user0');
assert.strictEqual(user3Follows.length, 1);

// Find all users that a user follows with explicit id.
{
  const user0Follows = (await User.findOne({
    where: {id: user0.id},
    attributes: [],
    include: [{
      model: User,
      as: 'Follows',
      through: {attributes: []},
    }],
  })).Follows
  assert.strictEqual(user0Follows[0].name, 'user1');
  assert.strictEqual(user0Follows[1].name, 'user2');
  assert.strictEqual(user0Follows.length, 2);
}

// Another method with the many-to-many reversed.
// Using the super many to many is the only way I know of doing this so far.
// which is a pain.
{
  const user0Follows = await User.findAll({
    include: [{
      model: UserFollowUser,
      attributes: [],
      on: {
        FollowId: { [Op.col]: 'User.id' },
      },
      where: {UserId: user0.id}
    }],
    order: [['name', 'ASC']],
  })
  assert.strictEqual(user0Follows[0].name, 'user1');
  assert.strictEqual(user0Follows[1].name, 'user2');
  assert.strictEqual(user0Follows.length, 2);
}

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