How do I create many-many association in feathers-sequelize? - javascript

I have a feathers api set up using feathers-sequelize to persist to a MySQL database.
I've got the datamodel set up and can see the relevant tables are created.
const Sequelize = require('sequelize');
module.exports = function (app) {
const sequelizeClient = app.get('sequelizeClient');
const orders = sequelizeClient.define('orders', {
id: {
type: Sequelize.UUID,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
}, {
classMethods: {
associate (models) {
this.belongsToMany(models.users, {through: "offers", foreignKey: "orderId", otherKey: "userId"});
}
}
});
return orders;
};
How do I actually create an association of offers? I've tried something like this:
const orders = hook.app.service("orders");
order.offers.push(user.id);
orders.patch(order.id, order);
but it doesn't seem to have any effect

You shouldn't need to patch with a many-to-many. Simply use the Sequelize association method:
order.addUser(user.id)
To return the association you might need to do an order.reaload() in your after hook.

Related

How to modify virtual fields from sequelize findAll result?

I have looked everywhere and couldn't find any clear answers for this.
I have a complex findAll() with many inclusions and each with their own virtual fields.
What I want is to modify the virtual fields of the result, however as it is returning the model instance trying to access the virtual fields returns undefined as they are not in the result yet.
I have tried 'raw: true' but this removes all virtual fields and as my data has nested tables which also have their own virtual fields which I need, I cannot do that.
Example models
var Book = sequelize.define('Book', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
title: {
type: DataTypes.STRING
},
author: {
type: DataTypes.STRING
}
//....other columns,
myField: {
type: DataTypes.Virtual,
get() {
return this.getDataValue('title:') + this.getDataValue('author');
})
Getting the data
model.Book.findAll({
limit: 100
})
.then((result) => {
const newBook = result.map(row => {
return {...row, myField: 'setMyOwnValueHere'}
}
return newBook
}
Get model data first : get
model.Book.findAll({
limit: 100
}).then(result => {
const books = result.map(row => {
//this returns all values of the instance,
//also invoking virtual getters
const book = row.get();
book.myField = 'setMyOwnValueHere';
return book;
});
return books;
});

Write sequlize migration to update table onDelete attribute

I'm using sequlize ORM, I have migration file to create associations between tables:
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('testSessions', 'patientId', {
type: Sequelize.UUID,
references: {
model: 'patients',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
});
},
down: async queryInterface => {
await queryInterface.removeColumn('testSessions', 'patientId');
},
};
I want to change onDelete key to 'CASCADE'. Is it possible to write a migration that would change the value of the onDelete attribute?
I don't want to lose the data I have in the table so undoing migration and running is not a solution for me.
Do you know if you can write a migration that will override the onDelete value, how would it look like?
Just write SQL query that removes constraint and then adds constraint using sequelize.query something like below:
var table = 'YOURTABLENAME';
var constraint = 'YOURCONSTRAINTNAME';
var constraintFkey = 'CONSTRAINTFKEY';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.query(`alter table "${table}" drop constraint "${constraint}"`)
.then(() => queryInterface.sequelize.query(
`alter table "${table}"
add constraint "${constraint}" foreign key("${constraintFkey}") references "property_service" ("id")
on delete cascade`
));
},
down: (queryInterface, Sequelize) => {
return queryInterface.sequelize.query(`alter table "${tableName}" drop constraint "${constraintName}"`)
.then(() => queryInterface.sequelize.query(
`alter table "${tableName}"\
add constraint "${constraintName}" foreign key("${constraintFkey}") references "patients" ("id")
on delete no action`
));
},
};

How to insert data into bridging/mapping table using Sequelize.js

I am trying to insert data into the bridging/mapping table using sequelize.js
I defined the Articles as-
const Articles = db.define('article', {
"slug": {
type: Sequelize.STRING,
primaryKey: true,
allowNull:false
},
"title": {
type: Sequelize.STRING(50),
allowNull: false
},
"description": {
type: Sequelize.STRING(100),
},
"body": Sequelize.STRING,
})
and Tags as-
const Tags = db.define('tag', {
name: {
type: Sequelize.STRING,
primaryKey: true
}
})
I created a many-to-many association between them as
Articles.belongsToMany(Tags, {through:'article_tags'})
Tags.belongsToMany(Articles, {through:'article_tags'})
This created a table article_tags in my Database along with articles and tags. But now how can I insert data into this article_tags bridging table using sequelize?
You have to model that mapping table as well.... then when you are in the resolver for say something like "tagArticle" you would just call the create function on the mapping table with the id of the article and the id of the tag... here is an example of a resolver for a join table i have where i am joining Dealers to Accounts
createUserAccountDealer: async (_, args, { models }) => {
let returnValue = null;
const { username, input } = args;
try {
returnValue = await models.ProfileDealerInfo.create(input);
if (returnValue && returnValue.dataValues.id) {
const tempJoin = { username, dealer_id: returnValue.dataValues.id };
await models.ProfileAccountDealer.create(tempJoin);
}
} catch (err) {
logger.error(err);
}
return returnValue;
}
The difference for me is that dealer is a child of account, so i do the join record right after creating the child dealer record... Your example is a little diff where tags and articles may already exist and you may just only be associating them meaning the id's would just be passed in the args from the client and you would not have the extra step of waiting to get the id back from the child record create

Sequelize.js: get association of many-to-many junction model

I have some application. There is an Character, which can belong to many group. A group can have many characters and also many ranks. Each Character have specified rank. Ranks differs across the groups.
Character model:
module.exports = (sequelize, DataTypes) => {
let Character = sequelize.define('Character', {
/* attributes */
}, {});
Character.associate = (models) => {
Character.hasMany(models.outfit, { foreignKey: 'owner' });
Character.belongsToMany(models.group, { through: models.groupmember });
};
return Character;
};
Group model:
module.exports = (sequelize, DataTypes) => {
let Group = sequelize.define('Group', {
/* attributes */
}, {});
Group.associate = function (models) {
Group.belongsToMany(models.character, { through: models.groupmember, as: 'members' });
Group.hasMany(models.grouprank);
};
return Group;
};
GroupMember (junction table) model:
module.exports = (sequelize, DataTypes) => {
let GroupMember = sequelize.define('GroupMember', {
/* groupId, characterId are generated by sequelize */
rankId: DataTypes.INTEGER
}, {});
GroupMember.associate = function (models) {
GroupMember.belongsTo(models.grouprank, { foreignKey: 'rankId', targetKey: 'id' });
};
return GroupMember;
};
Group rank model: (doesn't matter much in my question)
module.exports = (sequelize, DataTypes) => {
let GroupRank = sequelize.define('GroupRank', {
// atributes
}, {});
GroupRank.associate = function (models) {
GroupRank.belongsTo(models.group);
GroupRank.hasMany(models.groupmember, { foreignKey: 'rankId' });
};
return GroupRank;
};
I have added rankId column to junction table and I have problem to retrieve Character with all its groups included and also the rank which it have.
My current code, which returns the Character, the groups to which he belongst to, and also ranks, but the ALL ranks which belongs to the group. And I want just the rank with ID which is specified by rankId.
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
/* where: { id: '$GroupMember.rankId$' } */
}
}]
}).then(result => {
});
Yep. I know it looks weird little, it lacks CamelCase but however my problem is about something else. I tried to query with where attribute, but Sequelize is parsing the string, so I can't put there column name. I hope the question is understable enough. Thank you for your time!
I think below code finally worked for me. http://docs.sequelizejs.com/class/lib/sequelize.js~Sequelize.html#static-method-literal
database.character.findById(characterId, {
include: [{
model: database.group,
through: database.groupmember,
include: {
model: database.grouprank,
where: database.Sequelize.literal('`Groups->GroupRanks`.`id` = `Groups->GroupMember`.`rankId`')
}
}]
})
edit: Finally I decided to rewrite models as stated here -> FindAll with includes involving a complicated many-to-(many-to-many) relationship (sequelizejs)

Seeding data across Sequelize associations

I am using Sequelize (new to ORM's) and currently have three tables: card and tags, that have a many-to-many relationship that is established with the third table, card_tags. My question is two fold:
How do I seed data across associations?
How do I create new data (i.e. an API function) that establishes new data across associated data bases?
Please see the below for my models and let me know if I'm leaving anything out. Thanks!
cards model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Card = sequelize.define('card', {
title: {
type: DataTypes.STRING,
allowNull: false
},
link: {
type: DataTypes.STRING,
allowNull: false
}
});
Card.associate = (models) => {
Card.belongsToMany(models.tag, { through: 'card_tag', as: 'tag' });
};
return Card;
};
tags model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Tag = sequelize.define('tag', {
title: {
type: DataTypes.STRING,
allowNull: false
}
});
Tag.associate = (models) => {
Tag.belongsToMany(models.card, { through: 'card_tag', as: 'card'});
};
return Tag;
};
card_tags model:
'use strict';
module.exports = function(sequelize, DataTypes) {
const CardTag = sequelize.define('card_tag', {
cardId: DataTypes.INTEGER,
tagId: DataTypes.INTEGER
});
return CardTag;
};
The simplest and quickest way to do is to to insert bulk data in each relation.
You can write sequelize queries to bulkInsert keeping in mind that primary key for each foriegn key is created first.
You can use faker npm module to generate random data.

Categories