简体   繁体   中英

How to get all children or parents in a many-to-many association if one model references itself in sequelize?

The association is defined as follows:

Person.hasMany(Person, {
   as: 'Parents',
   through: models.Person_Parent
});

It is clear how to get all parents of an instance:

person.getParents().success(..)

But how to access the child objects of a parent?

You need to set up the reverse association as well

Person.hasMany(Person, {
  as: 'Children',
  through: models.Person_Parent
});

Minimal working example on sequelize 6.14.0

In this example I have a user follows use relationship, and I show how to get both:

  • users followed by a user
  • users that follow a user

The key hard step is defining the many to many both ways at:

User.belongsToMany(User, { through: 'UserFollowUser', as: 'Follows', foreignKey: 'UserId' });
User.belongsToMany(User, { through: 'UserFollowUser', as: 'Followed', foreignKey: 'FollowId' });

main.js

const assert = require('assert')
const path = require('path')
const { DataTypes, Sequelize } = require('sequelize')
let sequelize
if (process.argv[2] === 'p') {
  sequelize = new Sequelize('tmp', undefined, undefined, {
    dialect: 'postgres',
    host: '/var/run/postgresql',
  })
} else {
  sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: 'tmp.sqlite',
  })
}
function assertEqual(rows, rowsExpect) {
  assert.strictEqual(rows.length, rowsExpect.length)
  for (let i = 0; i < rows.length; i++) {
    let row = rows[i]
    let rowExpect = rowsExpect[i]
    for (let key in rowExpect) {
      assert.strictEqual(row[key], rowExpect[key])
    }
  }
}
;(async () => {

// Create the tables.
const User = sequelize.define('User', {
  username: { type: DataTypes.STRING },
}, {});
User.belongsToMany(User, { through: 'UserFollowUser', as: 'Follows', foreignKey: 'UserId' });
// This is ony needed for the function followed. "foreignKey" could be removed otherwise.
User.belongsToMany(User, { through: 'UserFollowUser', as: 'Followed', foreignKey: 'FollowId' });
await sequelize.sync({ force: true });

// Create some users.

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

// Make user0 follow user1 and user2
await user0.addFollows([user1, user2])
// Make user2 and user3 follow user0
await user2.addFollow(user0)
await user3.addFollow(user0)

let rows

// Get users followed by an user by username.
async function followed(username, opts={}) {
  return User.findAll(Object.assign({
    include: [{
      model: User,
      as: 'Followed',
      attributes: [],
      through: { attributes: [] },
      where: { username },
    }],
    // Required for limit to work.
    subQuery: false,
    order: [['username', 'ASC']]
  }, opts))
}
rows = await followed('user0')
assertEqual(rows, [
  { username: 'user1'},
  { username: 'user2'},
])
rows = await followed('user0', { limit: 1 })
assertEqual(rows, [
  { username: 'user1'},
])
rows = await followed('user0', { order: [['username', 'DESC']] })
assertEqual(rows, [
  { username: 'user2'},
  { username: 'user1'},
])

// Now the inverse: find users that follow a given user by username.
async function following(username, opts={}) {
  return User.findAll(
    Object.assign({
      include: [{
        model: User,
        as: 'Follows',
        where: { username },
        attributes: [],
        through: { attributes: [] }
      }],
      // Required for limit to work.
      subQuery: false,
      order: [['username', 'ASC']],
    }, opts)
  )
}
assertEqual(await following('user0'), [
  { username: 'user2' },
  { username: 'user3' },
])
assertEqual(await following('user0', { order: [['username', 'DESC']] }), [
  { username: 'user3' },
  { username: 'user2' },
])
assertEqual(await following('user0', { limit: 1 }), [
  { username: 'user2' },
])
assertEqual(await following('user0', { limit: 1, order: [['username', 'DESC']] }), [
  { username: 'user3' },
])
assertEqual(await following('user1'), [
  { username: 'user0' },
])
assertEqual(await following('user2'), [
  { username: 'user0' },
])
assertEqual(await following('user3'), [])

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

package.json

{
  "name": "tmp",
  "private": true,
  "version": "1.0.0",
  "dependencies": {
    "pg": "8.5.1",
    "pg-hstore": "2.3.3",
    "sequelize": "6.14.0",
    "sql-formatter": "4.0.2",
    "sqlite3": "5.0.2"
  }
}

GitHub upstream .

Tested on Ubuntu 22.04, PostgreSQL 14.3.

Bibliography:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM