0

Interested in creating a new feed type app. I am having trouble conceptualizing the one to many relationship in sequelize. Ultimately, I am working with a News Api.

A user would visit the site, select many different categories of new (e.g. sports, tech). The category, thus has many associated articles. The application should return a feed of articles for each user.

The model, conceptually, is one user to many categories, and further, one category to many articles. I am unsure how to map this schema in sequelize (node.js.). Thoughts?

Andrew C
  • 1
  • 2

1 Answers1

0

Without model definitions I can just show a basic concept of association definitions.

User.hasMany(Category)
Category.hasMany(Article)
Category.belongsTo(User)
Article.belongsTo(Category)

So getting all articles related to a certain user can be like this:

// let's assume a user is stored in `user` var
const articles = await Article.findAll({
  include: [{
    required: true,
    model: Category,
    where: {
      userId: user.id
    }
  }]
});
Anatoly
  • 20,799
  • 3
  • 28
  • 42
  • I believe I understand, I am new to SO, so unfamiliar with how I would include models code. It is how you would think but keeping it simple... User model (full name STRING), category (category STRING), and article (title STRING, url STRING). – Andrew C Nov 27 '20 at 05:51
  • I would like to set up a route, to get all articles associated with a user, based on their category selection. I believe your method would work. Just a bit unsure, how it works. – Andrew C Nov 27 '20 at 05:53
  • At first please read how to define models here https://sequelize.org/master/manual/model-basics.html#model-definition – Anatoly Nov 27 '20 at 05:54
  • Yes, I do have my models set up correctly. It is the associations I am having difficulty understanding when using 'include'. I guess the key here is the 'where' attribute within the include attribute. – Andrew C Nov 27 '20 at 05:58
  • Exactly. You just need to put `where` option where you wish to add conditions on a certain model. – Anatoly Nov 27 '20 at 06:00
  • Ah, so the findAll code would be appropriate on the client side route (e.g. ajax route). For example, when a user selects a 'render feed' button, an event could fire to findAll articles (...) and return the articles to the client to display or use a template engine to render on client side. – Andrew C Nov 27 '20 at 06:03
  • All sequelize code should be on a backend side. A client only requests some backend API endpoints to get what it wants. Something like `/articles` – Anatoly Nov 27 '20 at 06:06
  • thank you, I will give it a try, appreciate your help – Andrew C Nov 27 '20 at 06:07
  • would you suggest to define the associations on the server.js file? Once the /models folder (holding the User.js Article.js Category.js model files) are importing into that file? Or is it best practice to define associations on model files / within the models folder of a Node Express Mysql app? – Andrew C Nov 27 '20 at 16:42
  • It's better to define associations along with models BUT register them only after all models are registered. See my answer here https://stackoverflow.com/questions/61709290/sequelize-model-association-wont-create-a-new-column/61710568#61710568 – Anatoly Nov 27 '20 at 17:21
  • User.hasMany(Category, { as: 'All_Categories', foreignKey: 'userId' }); – Andrew C Nov 30 '20 at 02:20
  • Category.belongsTo(User, { as: 'UserRef', foreignKey: 'userId' }); – Andrew C Nov 30 '20 at 02:21
  • wouldn't the two above lines of code do the same thing, place foreignKey of userId in the Categories table? – Andrew C Nov 30 '20 at 02:21
  • In term of `sync` method - yes, in terms of Sequelize requests - no, it's for requesting Users and their associated Categories (1:N) and requesting Categories with one user per a category (N:1) – Anatoly Nov 30 '20 at 06:29
  • I believe I understand. The User.hasMany definition is appropriate for a single user route. Ex. User.findById('1', { include: [{ model: Category, as: 'All_Categories', attributes: ['category'] }] }); – Andrew C Nov 30 '20 at 16:01
  • Whereas, the Category.belongsTo definition is appropriate for a single category route/query. Ex. Category.findById('1', { include: [{ model: User, as: 'UserRef', attributes: ['full_nm'] }] }); – Andrew C Nov 30 '20 at 16:04
  • You're almost right. You also can request several users `User.findAll({ where: { name: 'user' }, include: [{ model: Category, as: 'All_Categories', attributes: ['category'] }] })` as well as several categories `Category.findAll({ where: { name: 'category'}, include: [{ model: User, as: 'UserRef', attributes: ['full_nm'] }] })` – Anatoly Nov 30 '20 at 17:21
  • Hm. Knowing that you do not have access to my models. Are you assuming I have a 'name' attribute on the User and Category Model? I have a 'first_nm' & 'last_nm' attribute on the User model; and a beforeCreate Hook to create 'full_nm'. Whereas, I have only a 'category' attr on my Category model. In my example, I was just using the userId (PK) and categoryId (PK) for my filtering method. I am wondering why that would not work. – Andrew C Nov 30 '20 at 19:08
  • It's just an example. Obviously you should replace `name` with whatever field you need to use in this condition – Anatoly Nov 30 '20 at 19:09
  • I understand, just wanted to clarify! As I work through this project, I am thinking that the User > Category relationship should really be a BelongsToMany relationship. As a single category (e.g. Sports) could be selected by many different Users. And further, a User could have more than one Category. Thoughts? – Andrew C Nov 30 '20 at 19:12
  • For that you need a new table like `UserCategory` and its model to describe `belongsToMany` associations and able to query user along with linked categories through this table – Anatoly Nov 30 '20 at 19:14
  • I suppose this should be a new question on SO ) – Anatoly Nov 30 '20 at 19:15