for sync reasons I would like to create a hash of certain fields of a row as a virtual field.
My sequelize model looks like this:
var crypto = require('crypto');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('transactions', {
id: {
type: DataTypes.INTEGER,
primaryKey: true
},
randomfieldone: {
type: DataTypes.BIGINT,
allowNull: true,
},
randomfieldtwo: {
type: 'NUMERIC',
allowNull: false,
},
hash: {
type: DataTypes.VIRTUAL,
set: function (val) {
var string = this.randomfieldone+''+this.randomfieldtwo;
var hash = crypto.createHash('md5');
hash.update(string);
hash.digest('hex');
this.setDataValue('hash', hash);
}
}
},{
timestamps: false
});
};
When I try to output that, I get 'undefined'.
I would like to be able to access it like any other 'real' field.
console.log(row.hash)
What am I doing wrong here?
I am use
hash: {
type: DataTypes.VIRTUAL,
set: function (val) {
var string = this.get("randomfieldone")+''+this.get("randomfieldtwo");
var hash = crypto.createHash('md5');
hash.update(string);
hash.digest('hex');
this.setDataValue('hash', hash);
}
}
Ok I solved it:
var md5 = require('MD5');
getterMethods: {
hash: function () {
var string = this.id+''+this.randomfieldone +''+this.randomfieldtwo;
var hash = md5(string);
return hash;
}
}
if you set getter function for a property in schema, the property will be included when the instance is converted to object or json.
Related
I am trying to fetch data from Table_A and Table_B using node and sequelize
Table Structure
Table_A:
id PK
name Text
Table_B:
id PK
a_id FK_tableA_id
name Text
Model
TableA.js
'use strict';
const DataTypes = require('sequelize').DataTypes;
module.exports = (sequelize) => {
const Table_A = sequelize.define('Table_A', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
}
});
Table_A.associate = models => {
Table_A.belongsTo(models.Table_B, { as: 'tb' });
}
return Table_A;
};
TableB.js
'use strict';
const DataTypes = require('sequelize').DataTypes;
module.exports = (sequelize) => {
const Table_B = sequelize.define('Table_B', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
a_id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: null
},
name: {
type: DataTypes.TEXT,
allowNull: true
}
});
return Table_B;
};
I am getting below error while I am trying to run the query using sequelize, Can you please guide me where I am making the mistake?
Error
EagerLoadingError [SequelizeEagerLoadingError]: Table_B is not associated to Table_A!
at Function._getIncludedAssociation (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:545:13)
at Function._validateIncludedElement (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:482:53)
at C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:401:37
at Array.map (<anonymous>)
at Function._validateIncludedElements (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:397:39)
at Function.aggregate (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1204:12)
at Function.count (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1252:31)
at async Promise.all (index 0)
at async Function.findAndCountAll (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1268:27)
index.js
'use strict';
const { Op } = require('sequelize');
const {sequelize, connect } = require('./db');
const uninitModels = require('./models');
let initModels = uninitModels(sequelize);
initModels = { connection: sequelize, ...initModels }
const {
Table_A, Table_B
} = initModels;
function dbCall(final) {
Table_A.findAndCountAll(final).then((result)=>{
console.log(result)
}).catch((err)=>{
console.log(err)
})
}
function data() {
let final = {
include: [
{
model: Table_B,
attributes: ['id', 'name', 'a_id'],
as: 'tb'
}
]
}
dbCall(final);
}
data();
I suppose you didn't register associations that should be registered by calling associate methods of models.
Also you confused how models are linked. If a model1 has a foreign key field pointing to a model2 then an association should be model1.belongsTo(model2).
In your case it should be:
Table_A.associate = models => {
Table_A.hasMany(models.Table_B, { as: 'tb', foreginKey: 'a_id' });
}
and in the model Table_B:
Table_B.associate = models => {
Table_B.belongsTo(models.Table_A, { as: 'ta', foreginKey: 'a_id' });
}
Pay attention to foreignKey option, you need to indicate it explicitly because your foreign key field is named other than Table_A+id.
I got stuck on this same error (for what seemed like an eternity) and finally realized that there was a big flaw in the way I was declaring my associations.
Incorrect:
Account.associate = function (models) {
Account.hasMany(models.History, {
onDelete: "cascade"
});
};
Account.associate = function (models) {
Account.hasMany(models.User, {
onDelete: "cascade"
});
};
In hindsight, this was a really silly oversight doing two declarations here. tl;dr the 2nd declaration was canceling out the 1st one.
Correct:
Account.associate = function (models) {
Account.hasMany(models.History, {
onDelete: "cascade"
});
Account.hasMany(models.User, {
onDelete: "cascade"
});
};
One declaration with multiple function calls for the win.
I have a mongoose schema for stories that looks like this:
{
id: {
type: Number,
default: 0
},
title: {
type: String,
maxLength: 60
},
author: {
userid: {
type: Number
},
username: {
type: String
}
}
chapters: [chapter],
numchapters: {
type: Number,
default: 1
},
favs: {
type: Number,
default: 0
},
completed: {
type: Boolean,
default: false
}
}
What I'm trying to do is reference a document in a separate collection (users), and use the values of its userid and username fields in the author field.
how do I do this?
current code:
storyobj.populate('author', {path: 'author', model: 'users', select: 'userid username'}, (err) => {
if (err) {
console.log(err)
}
})
just in case it's relevant, the structure of the users collection looks like this:
{
username: {
type: String,
},
email: {
type: String,
},
password: {
type: String,
},
userid: {
type: Number
},
isAdmin: {
type: Boolean,
default: false
},
banned: {
type: Boolean,
default: false
}
}
EDIT:
I've changed the author field in the Stories model to look like this:
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
This is so I tell Mongoose, "Hey, I want this field to reference a user in the User collection".
Here are some more details that I hope will be of help.
Full code:
var storydb = require('../models/stories/story');
var chapterdb = require('../models/stories/chapter');
var userdb = require('../models/user');
const file = JSON.parse(fs.readFileSync('test.json')); // this is a file with the data for the stories I am trying to insert into my database
for (const d in file) {
var storyobj = new storydb({
id: d,
chapters: []
});
for (let e = 0; e < file[d].length; e++) {
var abc = file[d][e];
var updatey = {
chaptertitle: abc.chapter,
chapterid: parseInt(abc.recid),
words: abc.wordcount,
notes: abc.notes,
genre: abc.gid.split(', '),
summary: abc.summary,
hidden: undefined,
loggedinOnly: undefined,
posted: new Date(Date.parse(abc.date)),
bands: abc.bandnames.split(', ')
};
var kbv = getKeyByValue(userlookup, abc.uid);
storyobj.title = abc.title;
storyobj.numchapters = file[d].length;
storyobj.favs = file[d][0].numfavs;
updatey.characters = abc.charid.split(/, |,/g);
storyobj.chapters.push(updatey)
}
storyobj.save();
}
In file, there's a unique ID representing the author of each story. kbv returns the userid associated with that unique ID (note that they're NOT the same).
Now, here's where I'm having trouble:
What I want to do is find a user matching the userid in kbv, and make that the author property in the story model.
The code I'm currently using to try and achieve that:
storydb.findOne({storyobj}, 'author').populate("author", (f) => console.log(f));
const Stories = require("./path/to/model");
Stories
.find({ /* query */ }, { /* projection */ })
.populate("author.username", ["userid", "username"])
.then(/* handle resolve */)
.catch(/* handle rejection */)
For this to work, you have to add a ref key to the userid key in your model, where the ref value is the name of the model it's referencing.
Story.model.js
const StorySchema = new Schema({
author: {
userid: { type: Schema.Types.ObjectId, ref: "users", required: true },
/* other props */
}
/* other props */
});
I have a mongoose model that represents a player and want to be able to fetch the player and when selecting the player, want to call isReady like a getter.
The model looks like so:
const PlayerSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User" },
famousPerson: { type: String }
})
PlayerSchema.methods.isReady = function (cb) {
return Boolean(this.famousPerson)
}
And I want to be able to call it like so:
const player = await PlayerModel
.findOne({_id: playerId})
.select(["_id", "username", "isReady"])
Am I able to set the method on the class as a getter?
You can use mongoose virtuals for this, but to work as expected you need to configure your schema so that it can return virtuals, because by default virtuals will not be included.
const PlayerSchema = new Schema(
{
famousPerson: { type: String },
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
PlayerSchema.virtual("isReady").get(function () {
return Boolean(this.famousPerson);
});
You Can Follow This Code
const player = await PlayerModel
.findOne({_id: playerId})
.select(" +_id +username +isReady)
This is very basic, and should work.. but doesn't. So first my models:
const Conversation = sequelize.define('Conversation', {
name: {
type: DataTypes.STRING,
allowNull: false
},
...
})
Conversation.associate = (models, options) => {
Conversation.hasOne(models.Audio, options)
}
and:
module.exports = (sequelize /*: sequelize */ , DataTypes /*: DataTypes */ ) => {
const Audio = sequelize.define("Audio", {
name: {
type: DataTypes.STRING,
unique: true,
allowNull: true
},
})
Audio.associate = (models, options) => {
Audio.belongsTo(models.Conversation, options)
}
I have a model loader that does:
fs.readdirSync(`${__dirname}`)
.filter((modelFile) => {
return path.extname(modelFile) === '.js' && modelFile !== 'index.js'
})
.map((modelFile) => {
let model = sequelize.import(`./${modelFile}`)
models[model.name] = model
return model
})
.filter((model) => models[model.name].associate)
.forEach((model) => {
models[model.name].associate(models, {
hooks: true,
onDelete: 'CASCADE'
});
})
So it calls the associate method for all models that have it defined. This works, in that, when I .sync it, it creates a ConversationId field in my Conversations table.
When I try to execute:
let updAudio = {
ConversationId,
name: 'myname'
}
await global.db.models.Audio.create(updAudio, { logging: console.log })
ConversationId is not null, but when it saves in the DB, it's null. I've inspected it a hundred times. The raw query looks like:
INSERT INTO "Audios" ("id","name","createdAt","updatedAt") VALUES (DEFAULT,'myname','2019-10-20 19:59:18.139 +00:00','2019-10-20 19:59:18.139 +00:00') RETURNING *;
So what happened to ConversationId?
You might need to add the foreign key in the model definition:
(One of the two may work)
const Audio = sequelize.define("Audio", {
name: {
type: DataTypes.STRING,
unique: true,
allowNull: true
},
conversationId : {
type: Sequelize.INTEGER,
references: 'Conversations' // or "conversations"? This is a table name
referencesKey: 'id' // the PK column name
}
})
or
const Audio = sequelize.define("Audio", {
name: {
type: DataTypes.STRING,
unique: true,
allowNull: true
},
conversationId : {
type: Sequelize.INTEGER,
references: {
model: Conversation
key: 'id'
}
}
})
You might also need to define your belongsTo association this way:
Audio.belongsTo(Conversation, foreignKey: 'conversationId');
Try to look for the generated raw query on creation of the tables and start from there.
Recently, I have been working with mongodb with one single model. When I tried to add a second model, I noticed that I might face some issues.
First, here's the code with one single model:
riskRating.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
let riskRatingRow = new Schema({
securitycause: {
type: String
},
operationalrisk: {
type: String
},
riskid: {
type: String
},
riskstatements: {
type: String
},
businessline: {
type: String
},
supportingasset: {
type: String
},
category: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
inherentriskrating: {
type: String
},
controleffectiveness: {
type: String
},
residualrisk: {
type: String
}
});
module.exports = mongoose.model('riskRating', riskRatingRow);
Here's how I use it in the server code:
server.js
const RiskRatingRow= require('./models/riskRating');
router.route('/table').get((req, res) => {
RiskRatingRow.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
});
});
router.route('/table/add').post((req, res) => {
console.log('REQ.body is ', req.body);
const riskRatingRow = new RiskRatingRow(req.body);
riskRatingRow.save()
.then(issue => {
res.status(200).json({
'tableRow': 'Added successfully'
});
})
.catch(err => {
res.status(400).send('Failed to create new record');
});
});
First question: Is there anything wrong so far?
Now, when I add the second model:
twoModels.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
let riskRatingRow = new Schema({
//1st model defintion
});
const businessLineDashboardRow = new Schema({
//2nd model defintion
});
module.exports = mongoose.model('businessLineDashboard', businessLineDashboardRow);
module.exports = mongoose.model('riskRating', riskRatingRow);
I have noticed that in server.js, when I am using the first model, I am not referencing it directly. I'm rather referecing the singleModel.js file. Particularly in these two lines:
const RiskRatingRow = require('./models/riskRating');
// Here I am using directly the file reference RiskRatingRow
const riskRatingRow = new RiskRatingRow(req.body);
// Same thing here
RiskRatingRow.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
});
So, when I was about to make use of the second model, I found myself blocked since as I explained when I used the first model, I didn't reference it directly. I just referenced the file.
Thing is, that actually works fine.
But, I don't know if the model file contains two models, how am I supposed to make use of them both in the server file.
So here's my two other questions:
1/ How come that code works even though I am just referecing the model defintion file?
2/ Should I define the second model in a separate file, and reference it in order to be able to use it?
Thank you, I hope I was clear enough.
module.exports can be an object containing multiple things as properties:
module.exports = {
RiskRatingRow: mongoose.model('businessLineDashboard', businessLineDashboardRow),
BusinessLineDashboardRow: mongoose.model('riskRating', riskRatingRow),
}
Since it's an empty object ({}) by default you can also assign the exports individually:
module.exports.RiskRatingRow = mongoose.model('businessLineDashboard', businessLineDashboardRow)
module.exports.BusinessLineDashboardRow = mongoose.model('riskRating', riskRatingRow)
You can now destructure the models out of the object inside server.js:
const { RiskRatingRow, BusinessLineDashboardRow } = require('./models/twoModels')
Or, if you want to do it the old-school way:
const models = require('./models/twoModels')
const RiskRatingRow = models.RiskRatingRow
const BusinessLineDashboardRow = models.BusinessLineDashboardRow
Niklas's answer is on point. However, I have found a more complete solution:
riskRating.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
module.exports = function(mongoose) {
let riskRatingRow = new Schema({
securitycause: {
type: String
},
operationalrisk: {
type: String
},
riskid: {
type: String
},
riskstatements: {
type: String
},
businessline: {
type: String
},
supportingasset: {
type: String
},
category: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
inherentriskrating: {
type: String
},
controleffectiveness: {
type: String
},
residualrisk: {
type: String
}
});
let businessLineDashboardRow = new Schema({
ref: {
type: String
},
riskstatements: {
type: String
},
maximpact: {
type: String
},
controleffectiveness: {
type: String
},
recommendedriskrating: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
validatedreviewriskrating: {
type: String
},
rationalforriskadjustment: {
type: String
}
});
var models = {
BusinessLineDashboard : mongoose.model('BusinessLineDashboard', businessLineDashboardRow),
RiskRating : mongoose.model('RiskRating', riskRatingRow)
};
return models;
}
server.js
router.route('/riskRating2').get((req, res) => {
models.RiskRating.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
analyseDeRisqueService.determineMaxImpactForEachRisk(tableData)
});
});