When using findAndCountAll with a limit and offset, I get only (for example) 8 rows per page instead of 10.
Here's what I'm using to paginate results (10 per page):
async function allPlayers(req, res) {
const page = parseInt(req.query.page);
const perPage = parseInt(req.query.perPage);
const options = {
where: {
[Op.and]: [
{
type: "player",
},
{
"$teams.team.type$": "club",
},
],
},
include: [
{
model: UserTeam,
duplicating: false,
required: true,
include: [
{
model: Team,
include: [{ model: Club }, { model: School }],
},
],
},
],
};
const { rows, count } = await User.findAndCountAll({
...options,
limit: perPage,
offset: perPage * (page - 1),
});
res.json({ data: { rows, count } });
}
The issue seems to be Sequelize filtering out the rows when returned from SQL, instead of in the query. This happens because of this segment in the find options query:
{
model: UserTeam,
duplicating: false,
required: true,
include: [...],
}
Because of that, instead of returning 10 per paginated page, it's returning 10 or less (depending if any rows were filtered out).
Is there a fix for this behaviour or a different way to re-structure my data so I don't need this nested query?
I need this because I have a database/model structure like this:
User (players, coaches, admins, etc.)
|
|_ UserTeam (pivot table containing userId and teamId)
|
|_ Team
I don't think you need to reference the pivot/join tables in the query itself. There are too many nested levels of entities in your query with School and Club etc.
If you, instead of including the nested data when you perform the query, include the User/Player model and Team model in the UserTeam model with the reference keyword, Sequelize in my experience will handle this type of operation better. The same goes for School and Club, they could be included in their join tables' model definitions instead.
Note that I use the define method and not the class based models. What if you try something like this:
const { DataTypes } = require('sequelize')
const sequelize = require('./../config/db')
const User = require('./User')
const Team = require('./Team')
const UserTeam = sequelize.define('userTeam', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
teamId: {
type: DataTypes.UUID,
allowNull: false,
unique: false,
onDelete: 'CASCADE',
references: {
model: Team,
key: 'id'
},
validate: {
isUUID: {
args: 4,
msg: 'Team ID must be a UUID4 string.'
}
}
},
userId: {
type: DataTypes.UUID,
allowNull: false,
unique: false,
onDelete: 'CASCADE',
references: {
model: User,
key: 'id'
},
validate: {
isUUID: {
args: 4,
msg: 'User ID must be a UUID4 string.'
}
}
},
deletedAt: {
type: DataTypes.DATE,
allowNull: true,
defaultValue: null
}
},
{
paranoid: true,
tableName: 'userTeam'
})
Then you include the Team like this:
const users = await User.findAll({
include: Team
})
Associations (it would probably be more logical to call the join table TeamUser and not UserTeam):
User.belongsToMany(Team, { through: UserTeam, foreignKey: 'userId', onDelete: 'CASCADE' })
Team.belongsToMany(User, { through: UserTeam, foreignKey: 'teamId', onDelete: 'CASCADE' })
A json response based on this many-to-many relationship and the query above should result in a list of all users with every team that user is connected to. Here is one of those users:
{
"fullName": "Player One",
"id": "6e8ca258-9daa-4d52-b033-a077d98c29ef",
"firstName": "Player",
"lastName": "One",
"email": "playerone#gmail.com",
"password": "$2a$14$tApwpX9Ld9a1cjZMFzTGZeVEUC01M7n/tSVlldG7OEbm9sEh/k8kW",
"verified": 1,
"verifyCode": "4f49b5ca-12ed5a2d-1608191096291",
"resetPasswordToken": "bd15eda5097e030988eed5d2d20b3bbb6a06439f6b9907783fc0ea19083d8410",
"resetPasswordExpire": 1620266644543,
"passwordChangedAt": 1608337842942,
"createdFromIp": "122.222.10.33",
"createdAt": "2020-12-14T06:33:13.000Z",
"updatedAt": "2021-05-06T01:54:04.000Z",
"deletedAt": null,
"teams": [
{
"id": "427e9de4-9318-4406-aed9-fcbb3b8a3282",
"name": "Texas Rangers",
"type": "MLB",
"slug": "texas-rangers",
"createdByUserId": "6e8ca258-9daa-4d52-b033-a077d98c29ef",
"createdAt": "2020-12-14T06:33:15.000Z",
"updatedAt": "2020-12-29T06:07:54.000Z",
"deletedAt": null,
"userTeams": {
"id": 54,
"teamId": "427e9de4-9318-4406-aed9-fcbb3b8a3282",
"userId": "6e8ca258-9daa-4d52-b033-a077d98c29ef",
"deletedAt": null,
"createdAt": "2020-12-14T06:33:15.000Z",
"updatedAt": "2020-12-14T06:33:15.000Z"
}
},
{
"id": "cbff6df7-0e0c-4906-9e1c-54b569079d83",
"name": "New York Yankees",
"type": "MLB",
"slug": "yankees",
"createdByUserId": "16f38fc5-63d1-4285-8773-526720f9a506",
"createdAt": "2020-12-14T23:57:11.000Z",
"updatedAt": "2021-07-06T06:08:35.000Z",
"deletedAt": null,
"userTeams": {
"id": 55,
"teamId": "cbff6df7-0e0c-4906-9e1c-54b569079d83",
"userId": "6e8ca258-9daa-4d52-b033-a077d98c29ef",
"deletedAt": null,
"createdAt": "2020-12-14T23:57:11.000Z",
"updatedAt": "2020-12-14T23:57:11.000Z"
}
}
]
}
Let try this function bro
paginate: ({
currentPage,
pageSize
}) => {
const offset = parseInt((currentPage - 1) * pageSize, 10);
const limit = parseInt(pageSize, 10);
return {
offset,
limit,
};
},
// import function here
const result = await city.findAndCountAll({
where: conditions,
order: [
['createdAt', 'DESC']
],
...paginate({
currentPage: page,
pageSize: limit
}),
})
Related
I have two models, a posts model and a category model where I have an array that stores posts by objectId
Category Model
const mongoose = require('mongoose');
const CategorySchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
color: {
type: String,
required: true,
},
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
required: false,
}],
createdAt: {
type: Date,
default: Date.now,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", CategorySchema);
Post model
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
},
img: {
type: String,
required: true,
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
desc: {
type: String,
required: false,
},
createdAt: {
type: Date,
default: Date.now,
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Post", PostSchema);
So I created a get by id category controller
const Category = require('../../models/Category');
class FindCategory {
async find(req, res) {
const { id } = req.params;
try {
const category = await Category.findById(id);
return res.status(200).json(category);
} catch (err) {
return res.status(500).json(err);
}
}
}
module.exports = new FindCategory();
The problem is that when I make this request in my postman, it returns me for example
{
"_id": "63ac925d872445065a588f61",
"name": "Games",
"color": "#ff914d",
"posts": [
"63ac9cbccec4d9f35c4f4d1f"
],
"createdAt": "2022-12-28T19:00:45.847Z",
"updatedAt": "2022-12-28T19:49:47.119Z",
"__v": 0
}
But I would like to render the information of each post inside the "posts" array, something like that for example
{
"_id": "63ac925d872445065a588f61",
"name": "Games",
"color": "#ff914d",
"posts": [
"name": "red dead",
"img": "example.png",
"category": "games",
"desc": "test,
],
"createdAt": "2022-12-28T19:00:45.847Z",
"updatedAt": "2022-12-28T19:49:47.119Z",
"__v": 0
}
You should use the populate function provided by mongoose: https://mongoosejs.com/docs/populate.html#setting-populated-fields
Category.
findById(...).
populate('posts')
So i basically trying to get string value from database into an Array using Sequelize:
{
"status": "success",
"data": [
{
"id": 15,
"title": "The Godfather",
"year": 1972,
"director": "Francis Ford Coppola",
"categoryFilm": "R",
"Url": "https://m.media-amazon.com/images/M/MV5BMTU4MTgxOTQ0Nl5BMl5BanBnXkFtZTgwNDI0Mjk1NDM#._V1_UY100_CR19,0,100,100_AL_.jpg,https://m.media-amazon.com/images/M/MV5BMTczMTk5MjkwOF5BMl5BanBnXkFtZTgwMDI0Mjk1NDM#._V1_UY100_CR12,0,100,100_AL_.jpg,https://m.media-amazon.com/images/M/MV5BZTFiODA5NWEtM2FhNC00MWEzLTlkYjgtMWMwNzBhYzlkY2U3XkEyXkFqcGdeQXVyMDM2NDM2MQ##._V1_UX100_CR0,0,100,100_AL_.jpg",
"genre": [
{
"genre": "Crime,Drama"
}
]
}
]
}
with my code showed as below, i try sequelize literal to change it but i cannot resolve how to turn it from string into array and split it by "," (comma):
const allFilm = async (req, res) => {
await Film.findAll({
attributes: [
"id",
"title",
"year",
"director",
[sequelize.literal(`"category"."category"`), "categoryFilm"],
[sequelize.literal(`"photo"."photoUrl"`), "Url"],
],
subQuery: false,
include: [
{
model: Genre,
as: "genre",
attributes: ["genre"],
},
{
model: Category,
as: "category",
attributes: [],
},
{
model: Photo,
as: "photo",
attributes: [],
},
],
})
.then((data) => {
res.status(200).json({
status: "success",
data: data,
});
})
.catch((err) => {
res.status(400).json({
status: err,
});
});
};
what i want is like this is there is somthing wrong with my code since i find no error but i can not resolved it into array:
"Url": ["https://m.media-amazon.com/images/M/MV5BMTU4MTgxOTQ0Nl5BMl5BanBnXkFtZTgwNDI0Mjk1NDM#._V1_UY100_CR19,0,100,100_AL_.jpg","https://m.media-amazon.com/images/M/MV5BMTczMTk5MjkwOF5BMl5BanBnXkFtZTgwMDI0Mjk1NDM#._V1_UY100_CR12,0,100,100_AL_.jpg","https://m.media-amazon.com/images/M/MV5BZTFiODA5NWEtM2FhNC00MWEzLTlkYjgtMWMwNzBhYzlkY2U3XkEyXkFqcGdeQXVyMDM2NDM2MQ##._V1_UX100_CR0,0,100,100_AL_.jpg"],
I can't answer your question, but you can maybe try that :
From Load attributes from associated model in sequelize.js
await Film.findAll({
attributes: [
"id",
"title",
"year",
"director",
[sequelize.literal(`"category"."category"`), "categoryFilm"],
[Sequelize.col('photo.photoUrl'), 'Url'] // here
],
subQuery: false,
raw:true, // here
include: [
{
model: Genre,
as: "genre",
attributes: ["genre"],
},
{
model: Category,
as: "category",
attributes: [],
},
{
model: Photo,
as: "photo",
attributes: [],
required: false, // here
},
],
})
There are two tables which are Transaction and Transaction Details. The recordId is a foreign key of Transaction Details.
I am trying to find a Transaction by recordId and then include the Transaction Details. A Transaction can have many Transaction Details. Here are my codes:
db.models.Transaction.hasMany(db.models.TransactionDetails, {
foreignKey: 'recordId'
});
And then I'm querying a findOne and it looks like this:
db.models.Transaction.findOne({
where: {
recordId: req.query.recordid
} ,
include: [{
model: db.models.TransactionDetails
}]
})
But when I sent the request on Postman, the JSON data looked like this:
{
"error": false,
"message": {
"id": "8151",
"recordId": "6688",
"transactionNo": "1563804469415",
"cashierId": "4",
"payType": "cash",
"terminalNo": "0012346",
"amount": 40,
"discount": 0,
"cardNo": "none",
"transDate": "2019-07-22T14:23:26.000Z",
"createdAt": "2019-07-22T14:20:19.679Z",
"updatedAt": "2019-07-22T14:20:19.679Z",
"pt-transactions-details": [
{
"id": "38048",
"recordId": "8151", //this is wrong, the recordId must be the same as above which is 6688
"transId": "3731",
"productId": "539",
"quantity": "1",
"amount": 60,
"terminal": "002789",
"createdAt": "2019-09-13T01:22:48.349Z",
"updatedAt": "2019-09-13T01:22:48.349Z"
},
{
"id": "9921",
"recordId": "8151", //this is wrong, the recordId must be the same as above which is 6688
"transId": "3985",
"productId": "1061",
"quantity": "2",
"amount": 100,
"terminal": "0012346",
"createdAt": "2019-07-05T03:44:49.406Z",
"updatedAt": "2019-07-05T03:44:49.406Z"
},
{
"id": "68848",
"recordId": "8151", //this is wrong, the recordId must be the same as above which is 6688
"transId": "5358",
"productId": "1128",
"quantity": "1",
"amount": 160,
"terminal": "171412",
"createdAt": "2019-10-15T13:00:03.864Z",
"updatedAt": "2019-10-15T13:00:03.864Z"
}
]
}
}
Can someone help me regarding this? I already spent a day trying to figure this out.
Short answer, you need to pass sourceKey into hasMany method.
Transaction.hasMany(TransactionDetail, { foreignKey: 'recordId', sourceKey: 'recordId' });
Long answer, here is an example:
index.ts:
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
import assert from 'assert';
class Transaction extends Model {}
Transaction.init(
{
recordId: {
unique: true,
type: DataTypes.STRING,
},
},
{ sequelize, modelName: 'transactions' },
);
class TransactionDetail extends Model {}
TransactionDetail.init(
{
amount: DataTypes.INTEGER,
},
{ sequelize, modelName: 'transaction_details' },
);
Transaction.hasMany(TransactionDetail, { foreignKey: 'recordId', sourceKey: 'recordId' });
(async function test() {
try {
await sequelize.sync({ force: true });
await Transaction.create(
{ recordId: '6688', transaction_details: [{ amount: 60 }, { amount: 100 }, { amount: 160 }] },
{ include: [TransactionDetail] },
);
const rval = await Transaction.findOne({ where: { recordId: '6688' }, include: [TransactionDetail] });
console.log(rval.dataValues);
assert.equal(rval.transaction_details.length, 3, 'transaction details count should equal 3');
const transactionDetailsDataValues = rval.transaction_details.map((d) => d.dataValues);
console.log('transactionDetailsDataValues: ', transactionDetailsDataValues);
} catch (error) {
console.log(error);
} finally {
await sequelize.close();
}
})();
The execution result of above code:
{ id: 1,
recordId: '6688',
transaction_details:
[ transaction_details {
dataValues: [Object],
_previousDataValues: [Object],
_changed: {},
_modelOptions: [Object],
_options: [Object],
isNewRecord: false },
transaction_details {
dataValues: [Object],
_previousDataValues: [Object],
_changed: {},
_modelOptions: [Object],
_options: [Object],
isNewRecord: false },
transaction_details {
dataValues: [Object],
_previousDataValues: [Object],
_changed: {},
_modelOptions: [Object],
_options: [Object],
isNewRecord: false } ] }
transactionDetailsDataValues: [ { id: 1, amount: 60, recordId: '6688' },
{ id: 2, amount: 100, recordId: '6688' },
{ id: 3, amount: 160, recordId: '6688' } ]
Check the data record in the database:
node-sequelize-examples=# select * from "transactions";
id | recordId
----+----------
1 | 6688
(1 row)
node-sequelize-examples=# select * from "transaction_details";
id | amount | recordId
----+--------+----------
1 | 60 | 6688
2 | 100 | 6688
3 | 160 | 6688
(3 rows)
Sequelize version: "sequelize": "^5.21.3"
source code: https://github.com/mrdulin/node-sequelize-examples/tree/master/src/examples/stackoverflow/60446814
You need to define Sequelize association as mentioned below -
db.models.Transaction.belongsTo(db.models.TransactionDetails, {
foreignKey: 'recordId', targetKey: 'recordId'
});
I hope it helps!
I have struggled with the mongoose.model.populate function for hours now. I have even tried directly copying and pasting several solutions without luck.
I have a User model which is supposed to contain an array of 'Dilemmas' which he/she has created, but I have been unable to populate it.
Here are the models as well as the implementation of populate().
User.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
dilemmas: [
{
type: Schema.Types.ObjectId,
ref: "Dilemma"
}
]
});
module.exports = User = mongoose.model("User", UserSchema, "users");
Dilemma.js
const mongoose = require("mongoose");
const slug = require("mongoose-slug-generator");
const Schema = mongoose.Schema;
mongoose.plugin(slug);
// Create Schema
const DilemmaSchema = new Schema({
creator: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
title: {
type: String
},
slug: {
type: String,
slug: "title"
},
red: {
type: String,
required: true
},
blue: {
type: String,
required: true
},
red_votes: {
type: Number,
default: 0,
required: true
},
blue_votes: {
type: Number,
default: 0,
required: true
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
}
],
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
text: {
type: String,
required: true
},
author: {
type: String
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Dilemma = mongoose.model("Dilemma", DilemmaSchema, "dilemmas");
Routes.js
// #route GET api/users/profile
// #desc Gets logged in user's profile
// #access Private
router.get(
"/profile",
passport.authenticate("jwt", { session: false }),
(req, res) => {
User.find({ username: req.user.username })
.populate("dilemmas")
.then(user => {
if (!user) {
errors.nouser = "There is no user";
return res.status(404).json(errors);
}
res.json(user);
})
.catch(err => res.status(400).json(err));
}
);
JSON Response
[
{
"_id": "5b807beef770e7c7e6bf7ce0",
"dilemmas": [],
"username": "Jonas",
"email": "Mohrdevelopment#gmail.com",
"password": "$2a$10$QaqljS9x08YQ9N9EuCBTpO114ZJUFuVxAV80xMzImNi8eW2frPg0C",
"date": "2018-08-24T21:43:10.411Z",
"__v": 0
}
]
JSON Dilemmas response
[
{
"red_votes": 0,
"blue_votes": 0,
"_id": "5b80975f6e47fecba621f295",
"user": "5b807beef770e7c7e6bf7ce0",
"title": "Am i the real author asdsdasd?",
"red": "This is the red dilemma",
"blue": "This is the blue dilemma",
"likes": [],
"comments": [],
"date": "2018-08-24T23:40:15.381Z",
"slug": "am-i-the-real-author-asdsdasd",
"__v": 0
},
{
"red_votes": 0,
"blue_votes": 0,
"_id": "5b808e789bc36bcae8c6c3ad",
"creator": "5b807beef770e7c7e6bf7ce0",
"title": "Am i the real author?",
"red": "This is the red dilemma",
"blue": "This is the blue dilemma",
"likes": [],
"comments": [],
"date": "2018-08-24T23:02:16.565Z",
"slug": "am-i-the-real-author",
"__v": 0
}
]
JSON Users response
{
"_id": {
"$oid": "5b807beef770e7c7e6bf7ce0"
},
"dilemmas": [],
"username": "Jonas",
"email": "Mohrdevelopment#gmail.com",
"password": "$2a$10$QaqljS9x08YQ9N9EuCBTpO114ZJUFuVxAV80xMzImNi8eW2frPg0C",
"date": {
"$date": "2018-08-24T21:43:10.411Z"
},
"__v": 0
}
I just encountered a similar issue myself. Populating a ref worked, but populating an array of refs did not. I was able to get the array populate to work by explicitly specifying the model name in the populate call, e.g.:
User.find({ ... }).populate({
path: 'dilemmas',
model: 'Dilemma',
});
I don't know why this makes a difference, when the name of the referenced model is already specified in the schema.
Have you tried this?
User.find({ username: req.user.username })
.populate("dilemmas")
.exec() // <-- add exec() to perform the search
.then(user => {
...
})
Did you check the documentation here?
https://mongoosejs.com/docs/populate.html#refs-to-children
It shows a similar setup (with Authors and Stories.) It mentions 'pushing' stories to be able to use a find / populate combo.
I am attempting to output a nested relation where
Cat.hasMany(legs)
Leg.belongsTo(cat)
Leg.hasOne(paw)
paw.hasMany(leg)
Here is my Cat Model:
module.exports = (sequelize, DataTypes) => {
const Cat = sequelize.define('Cat', {
userId: {
type: DataTypes.STRING,
},
}, {});
Cat.associate = function (models) {
Cat.hasMany(models.Leg, {
foreignKey: 'catId',
as: 'legs',
});
};
return Cat;
};
My Legs Model:
module.exports = (sequelize, DataTypes) => {
const Leg = sequelize.define('Leg', {
originalValue: DataTypes.JSON,
newValue: DataTypes.JSON,
legId: DataTypes.INTEGER,
objectId: DataTypes.INTEGER,
pawId: DataTypes.INTEGER,
}, {});
Leg.associate = function (models) {
Leg.belongsTo(models.Cat, {
foreignKey: 'LegId',
onDelete: 'CASCADE',
});
Leg.hasOne(models.Paw, {
foreignKey: 'pawId',
});
};
return Leg;
};
Here is my Paw model
module.exports = (sequelize, DataTypes) => {
const Paw = sequelize.define('Paw', {
pawType: DataTypes.STRING,
}, {});
Paw.associate = function (models) {
Paw.hasMany(models.Leg, {
foreignKey: 'pawId',
as: 'paws',
});
};
return Paw;
};
Currently My code is outputting this when i query the Cat Table
[
{
"id": 1,
"userId": "2wdfs",
"createdAt": "2018-04-14T20:12:47.112Z",
"updatedAt": "2018-04-14T20:12:47.112Z",
"legs": [
{
"id": 1,
"catId": 1,
"pawId": 1,
"createdAt": "2018-04-14T20:12:54.500Z",
"updatedAt": "2018-04-14T20:12:54.500Z"
}
]
}
]
However I would like the pawType from the paws table to also be present when listing everything from the cat table. Something more along the lines of this:
[
{
"id": 1,
"userId": "2wdfs",
"createdAt": "2018-04-14T20:12:47.112Z",
"updatedAt": "2018-04-14T20:12:47.112Z",
"legs": [
{
"id": 1,
"catId": 1,
"paws" : [
{
"id": 1,
"pawType": "cute"
}
]
"createdAt": "2018-04-14T20:12:54.500Z",
"updatedAt": "2018-04-14T20:12:54.500Z"
}
]
}
]
Additionally, Here is the query I am using to retrieve the Cats.
return Cat.findAll({ include: [{ model: Leg, as: 'legs',include [{model: Paw,}], }], })
This is the error that is returning,
{ SequelizeDatabaseError: column legs->Paw.pawId does not exist
{ error: column legs->Paw.pawId does not exist
And the full SQL command
sql: 'SELECT "Cat"."id", "Cat"."userId", "Cat"."createdAt", "Cat"."updatedAt", "legs"."id" AS "legs.id", "legs"."originalValue" AS "legs.originalValue", "legs"."newValue" AS "legs.newValue", "legs"."catId" AS "legs.catId", "legs"."objectId" AS "legs.objectId", "legs"."pawId" AS "legs.pawId", "legs"."createdAt" AS "legs.createdAt", "legs"."updatedAt" AS "legs.updatedAt", "legs->Paw"."id" AS "legs.Paw.id", "legs->Paw"."paw" AS "legs.Paw.paw", "legs->Paw"."pawId" AS "legs.Paw.pawId", "legs->Paw"."createdAt" AS "legs.Paw.createdAt", "legs->Paw"."updatedAt" AS "legs.Paw.updatedAt" FROM "Cats" AS "Cat" LEFT OUTER JOIN "Legs" AS "legs" ON "Cat"."id" = "legs"."catId" LEFT OUTER JOIN "Paws" AS "legs->Paw" ON "legs"."id" = "legs->Paw"."pawId";' },
There are many issues. I'll try to address them incrementally.
1) Models By default, if you do not declare a primaryKey, then sequelize automatically adds an id column for you. Thus legId isn't a useful column.
Furthermore, if you associate a model, the foreignKey reference is added for you, thus pawId shouldn't be declared.
Thus Legs.js should be modified to:
module.exports = (sequelize, DataTypes) => {
var Leg = sequelize.define('Leg', {
originalValue: DataTypes.JSON,
newValue: DataTypes.JSON,
objectId: DataTypes.INTEGER // not entirely sure what this is
})
Leg.associate = function (models) {
// associations
}
return Leg
}
The above gives me the following columns in pgAdmin:
2) Associations
The following association doesn't make sense, and should cause an error:
Leg.hasOne(Paw)
Paw.hasMany(Leg)
Unhandled rejection Error: Cyclic dependency found. Legs is dependent of itself.
Dependency chain: Legs -> Paws => Legs
Each Leg should have one Paw, and thus I suggest the following:
Leg.associate = function (models) {
// Leg.belongsTo(models.Cat)
Leg.hasOne(models.Paw, {
foreignKey: 'pawId',
as: 'paw'
})
}
Paw.associate = function (models) {
Paw.belongsTo(models.Leg, {
as: 'leg' // note this changed to make more sense
foreignKey: 'pawId'
})
}
3) Foreign Keys
Leg.belongsTo(models.Cat, {
foreignKey: 'catId', // this should match
onDelete: 'CASCADE'
})
Cat.hasMany(models.Leg, {
foreignKey: 'catId', // this should match
as: 'legs'
})
4) Eager Loading
When eager loading nested associations, you have to include them. You should also use as alias that matches your model associations:
Cat.findAll({
include: [{
model: Leg,
as: 'legs', // Cat.legs
include: [{
model: Paw,
as: 'paw' // Leg.paw instead of Leg.pawId
}]
}]
})
Using this entire setup and the above query, I obtain:
[
{
"id": 1,
"userId": "1",
"createdAt": "2018-04-15T11:22:59.888Z",
"updatedAt": "2018-04-15T11:22:59.888Z",
"legs": [
{
"id": 1,
"originalValue": null,
"newValue": null,
"objectId": null,
"createdAt": "2018-04-15T11:22:59.901Z",
"updatedAt": "2018-04-15T11:22:59.901Z",
"catId": 1,
"paw": {
"id": 1,
"pawType": null,
"createdAt": "2018-04-15T11:22:59.906Z",
"updatedAt": "2018-04-15T11:22:59.906Z",
"pawId": 1
}
}
]
}
]
Extra
Because this is obviously a practice setup, you could modify Paw to be a belongsToMany relation (perhaps you have conjoined cats by the paw?) as follows:
Paw.associate = function (models) {
Paw.belongsToMany(models.Leg, {
foreignKey: 'pawId',
through: 'PawLegs // a through join table MUST be defined
})
}
This would be the correct way to implement what you initially tried to with
Leg.hasOne(paw)
paw.hasMany(leg)