简体   繁体   中英

Include child records count in the parent record attributes list without fetching all child records

I have setup a very basic minified playground for sequelize experiements to learn how it behaves on association records fetch. But i am not able to achieve the desired output.

Reproduceable Code

const Sequelize = require('sequelize');

const { fn } = Sequelize;
const sequelize = new Sequelize('playground', 'postgres', 'postgres', {
  host: 'localhost',
  dialect: 'postgres',
  pool: {
    max: 9,
    min: 0,
    idle: 10000
  }
});

const Posts = sequelize.define('posts', {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true
    },
    title: {
      type: Sequelize.STRING
    },
    content: {
      type: Sequelize.STRING
    }
  }, 
  {
    freezeTableName: true,
    timestamps: false
  }
);

const Comments = sequelize.define('comments', {
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true
  },
  postId: {
    type: Sequelize.INTEGER,
    references: {
       model: 'posts',
       key: 'id'
    }
  },
  comment: {
    type: Sequelize.STRING
  }
}, 
{
  freezeTableName: true,
  timestamps: false
}
);

Posts.hasMany(Comments, {
   foreignKey: 'postId'
})


let criteria = {
  group: ['posts.id', 'comments.id'],
  attributes: [
    [fn('count', 'comments.id'), 'TotalComments']
  ],
  include: [
    {
      model: Comments,
    }
  ]
}


Posts.findAll(criteria).then(posts =>{
    console.dir(JSON.parse(JSON.stringify(posts)), {depth: null, colors: true});
});

Problem Encountering

The problem is straight forward i want to add the child records count in the parent attributes eg the count of all comments in the every post returned, but the output i am getting is returning a fix count which is 1. I can simply iterate all posts and set TotalComments by reading the total length of comments included, but i want to do this using sequelize query, because that way i can apply some filter eg posts with comments between 6 to 10

Output Shown

[
  {
    id: 101,
    title: 'The revolution in brain',
    TotalComments: '1', //this count is not matched with below included comments count
    comments: [
      { id: 15, postId: 101, comment: 'It helped me a lot' },
      { id: 13, postId: 101, comment: 'very disapointing' },
      { id: 12, postId: 101, comment: 'welldone' },
      { id: 14, postId: 101, comment: 'This post sucks' }
    ]
  },
  {
    id: 102,
    title: 'Making your hands dirty on Java',
    TotalComments: '1',
    comments: []
  },
  {
    id: 100,
    title: 'Earlier models of medical surgery',
    TotalComments: '1',
    comments: [
      { id: 10, postId: 100, comment: 'Appreciated your work' },
      { id: 11, postId: 100, comment: 'good' }
    ]
  }
]

Output Desired

[
  {
    id: 101,
    title: 'The revolution in brain',
    TotalComments: 4,
    comments: [
      { id: 15, postId: 101, comment: 'It helped me a lot' },
      { id: 13, postId: 101, comment: 'very disapointing' },
      { id: 12, postId: 101, comment: 'welldone' },
      { id: 14, postId: 101, comment: 'This post sucks' }
    ]
  },
  {
    id: 102,
    title: 'Making your hands dirty on Java',
    TotalComments: 0,
    comments: []
  },
  {
    id: 100,
    title: 'Earlier models of medical surgery',
    TotalComments: 2,
    comments: [
      { id: 10, postId: 100, comment: 'Appreciated your work' },
      { id: 11, postId: 100, comment: 'good' }
    ]
  }
]

Generated SQL

SELECT "posts"."id", "posts"."title", count('comments.id') AS "TotalComments", "comments"."id" AS "comments.id", "comments"."postId" AS "comments.postId", "comments"."comment" AS "comments.comment" FROM "posts" AS "posts" LEFT OUTER JOIN "comments" AS "comments" ON "posts"."id" = "comments"."postId" GROUP BY "posts"."id", "comments"."id";

I solved the issue my self after a lot of searching, i hope it will help every body else if they encounter similar issue, basically i used the sequelize literal to insert some raw query to address the problem, the modified query criteria example is following

let criteria = {
  group: ['posts.id', 'comments.id'],
  attributes: [
    'id',
    'title',
    [literal('(select count(comments.id) from comments where "comments"."postId" = "posts"."id")'), 'TotalComments']
  ],
  include: [
    {
      model: Comments,
    }
  ]
}


Posts.findAll(criteria).then(posts =>{
    console.dir(JSON.parse(JSON.stringify(posts)), {depth: null, colors: true});
});

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