node Sequelize how to write static functions - javascript

I have experience in writing statics functions in moongose like
var mongoose =require('mongoose');
var Schema = mongoose.Schema;
var adminSchema = new Schema({
fullname : String,
number : Number,
email: String,
auth : {
username: String,
password : String,
salt: String
}
});
adminSchema.statics.usernameInUse = function (username, callback) {
this.findOne({ 'auth.username' : username }, function (err, doc) {
if (err) callback(err);
else if (doc) callback(null, true);
else callback(null, false);
});
};
here usernameInUse is the function I wana write but using sequelize for mysql database
my model
/*
This module is attendant_user table model.
It will store attendants accounts details.
*/
"use strict";
module.exports = function(sequelize, DataTypes) {
var AttendantUser = sequelize.define('AttendantUser', {
username : {
type : DataTypes.STRING,
allowNull : false,
validate : {
isAlpha : true
}
},{
freezeTableName : true,
paranoid : true
});
return AttendantUser;
};
How to add statics function here..??

Well, you can easily use Expansion of models
var User = sequelize.define('user', { firstname: Sequelize.STRING });
// Adding a class level method
User.classLevelMethod = function() {
return 'foo';
};
// Adding an instance level method
User.prototype.instanceLevelMethod = function() {
return 'bar';
};
OR in some cases you may use getter and setter on your models. See the docs:
A) Defining as part of a property:
var Employee = sequelize.define('employee', {
name: {
type : Sequelize.STRING,
allowNull: false,
get : function() {
var title = this.getDataValue('title');
// 'this' allows you to access attributes of the instance
return this.getDataValue('name') + ' (' + title + ')';
},
},
title: {
type : Sequelize.STRING,
allowNull: false,
set : function(val) {
this.setDataValue('title', val.toUpperCase());
}
}
});
Employee
.create({ name: 'John Doe', title: 'senior engineer' })
.then(function(employee) {
console.log(employee.get('name')); // John Doe (SENIOR ENGINEER)
console.log(employee.get('title')); // SENIOR ENGINEER
})
B) Defining as part of the model:
var Foo = sequelize.define('foo', {
firstname: Sequelize.STRING,
lastname: Sequelize.STRING
}, {
getterMethods : {
fullName : function() { return this.firstname + ' ' + this.lastname }
},
setterMethods : {
fullName : function(value) {
var names = value.split(' ');
this.setDataValue('firstname', names.slice(0, -1).join(' '));
this.setDataValue('lastname', names.slice(-1).join(' '));
},
}
});
Hope it helps.

AttendantUser.usernameInUse = function (username, callback) {
...
};
return AttendantUser;

Related

How to update existing object with additional data

The project is created with nodejs and mongoose. What I am trying to do is to update the existing model with addition data (which is a comment, in that case).
This is the model and its methods:
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
date: {
type: String,
required: true
},
time: {
type: String,
required: true
},
assignedTo: {
type: String,
required: true
},
assignedBy: {
type: String,
required: true
},
status: {
type: String,
required: true
},
priority: {
type: String,
required: true
},
comments: {
comment:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
}
});
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
The controller, which is passing the information from the form:
exports.postComment = (req,res,next) =>{
const bugId = req.body.bugID;
const name = req.session.user.fullName;
const content = req.body.content;
const prod = {name, content};
Bug.findById(bugId).then(bug =>{
return bug.addComment(prod);
})
.then(result =>{
console.log(result);
});
};
I am getting a following error:
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
(node:3508) UnhandledPromiseRejectionWarning: TypeError: this.comments is not iterable
The error indicate you're trying to iterable a type of data which does NOT has that capability.
You can check that printing the type:
console.log(typeof this.comments)
Or even, priting the whole object:
console.log(this.comments)
as you can see, in both cases you're getting an object, not a list (how you spect)
So you can do 2 things:
1- Iterable a list
this.comments is an object but into that object you have the list you want, so just use the list instead.
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
//const updatedComments = [...this.comments];
const updatedComments = [...this.comments.comment];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
Or you can modify your schema making the comments a list instead of an object
2- comments as list in schema
Define the comments attribute as a list
const bugSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
...
...,
comments:[
{
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
}
]
});
And then, try to iterable it as how you been doing
bugSchema.methods.addComment = function(comment){
const username = comment.user;
const content = comment.content;
console.log(comment);
const updatedComments = [...this.comments];
updatedComments.push({
user : username,
content: content
});
this.comments = updatedComments;
return this.save();
};
I am not sure but comments is an object and not an array so you can't push using [...this.comments] and I think it is the comment you want to push?
const updatedComments = [...this.comment];
updatedComments.push({
user : username,
content: content
});
this.comment = updatedComments;
From your schema comments is not an array. you are trying to spread an object into an array. const updatedComments = [...this.comments]; also push works on array.
try to modify your schema definitions by declaring the commentSchema outside the bugSchema.
const commentSchema = new Schema({
user:{
type: String,
required: true
},
content: {
type: String,
required: true
}
})
const bugSchema = new Schema({
comments: {
type: [commentSchema]
}
})
Bug.findByIdAndUpdate(bugId, {$push: {comments: newComment}})
Don't use findByIdAndUpdate Mongoose method, you better use save
it is written here https://mongoosejs.com/docs/tutorials/findoneandupdate.html
The findOneAndUpdate() function in Mongoose has a wide variety of use cases. You should use save() to update documents where possible, but there are some cases where you need to use findOneAndUpdate(). In this tutorial, you'll see how to use findOneAndUpdate(), and learn when you need to use it.
Below a router example
router.put('/items', (req, res) => {
if (!req.body._id || !req.body.title) {
return res.status(501).send({ message: 'Missing parameters, or incorrect parameters' });
}
return itemModel.findOne({ _id: req.body._id }, (err, item) => {
if (err) {
return res.status(500).send({
message: err
});
}
item.title = req.body.title; // <------------- You rewrite what was before stored on title attribute
return item.save((err, item) => { // <------------- You save it, this is not gonna create a new one, except if it doesn't exist already
if (err) {
return res.status(400).send({
message: 'Failed to update item'
});
} else {
return res.status(200).send({
message: 'Item update succesfully',
data: item
});
}
});
});
});

GraphQL, Mysql equivalent OR operation

I have just learnt GraphQL and I want to find the user id=2 OR user id=3 now how will I make the GraphQL query,I am getting the whole set using the bellow query
{
users() {
id
username
posts {
title
tags {
name
}
}
}
}
2nd issue --
{
people(id:[1,2,3]) {
id
username
posts(id:2) {
title
tags {
name
}
}
}
}
if I add the arg on Posts filed then i got an error msg "Unknown argument id on field posts of type user"
This is my Schema js file
var graphql = require('graphql');
var Db = require('./db');
var users = new graphql.GraphQLObjectType({
name : 'user',
description : 'this is user info',
fields : function(){
return {
id :{
type : graphql.GraphQLInt,
resolve(user){
return user.id;
}
},
username :{
type : graphql.GraphQLString,
resolve(user){
return user.username;
}
},
posts:{
id:{
type : graphql.GraphQLString,
resolve(post){
return post.id;
}
},
type: new graphql.GraphQLList(posts),
resolve(user){
return user.getPosts();
}
}
}
}
});
var posts = new graphql.GraphQLObjectType({
name : 'Posts',
description : 'this is post info',
fields : function(){
return {
id :{
type : graphql.GraphQLInt,
resolve(post){
return post.id;
}
},
title :{
type : graphql.GraphQLString,
resolve(post){
return post.title;
}
},
content:{
type : graphql.GraphQLString,
resolve(post){
return post.content;
}
},
person :{
type: users,
resolve(post){
return post.getUser();
}
},
tags :{
type: new graphql.GraphQLList(tags),
resolve(post){
return post.getTags();
}
}
}
}
});
var tags = new graphql.GraphQLObjectType({
name : 'Tags',
description : 'this is Tags info',
fields : function(){
return {
id :{
type : graphql.GraphQLInt,
resolve(tag){
return tag.id;
}
},
name:{
type : graphql.GraphQLString,
resolve(tag){
return tag.name;
}
},
posts :{
type: new graphql.GraphQLList(posts),
resolve(tag){
return tag.getPosts();
}
}
}
}
});
var query = new graphql.GraphQLObjectType({
name : 'query',
description : 'Root query',
fields : function(){
return {
people :{
type : new graphql.GraphQLList(users),
args :{
id:{type: new graphql.GraphQLList(graphql.GraphQLInt)},
username:{
type: graphql.GraphQLString
}
},
resolve(root,args){
return Db.models.user.findAll({where:args});
}
},
posts:{
type : new graphql.GraphQLList(posts),
args :{
id:{
type: graphql.GraphQLInt
},
title:{
type: graphql.GraphQLString
},
},
resolve(root,args){
return Db.models.post.findAll({where:args});
}
},
tags :{
type : new graphql.GraphQLList(tags),
args :{
id:{
type: graphql.GraphQLInt
},
name:{
type: graphql.GraphQLString
},
},
resolve(root,args){
return Db.models.tag.findAll({where:args});
}
}
}
}
});
var Mutation = new graphql.GraphQLObjectType({
name : "mutation",
description : 'function for mutaion',
fields : function(){
return {
addPerson : {
type : users,
args :{
username : {
type : new graphql.GraphQLNonNull(graphql.GraphQLString)
},
email :{
type : new graphql.GraphQLNonNull(graphql.GraphQLString)
}
},
resolve(_, args){
return Db.models.user.create({
username : args.username,
email : args.email
});
}
}
}
}
})
var Schama = new graphql.GraphQLSchema({
query : query,
mutation : Mutation
})
module.exports = Schama;
In order to fetch multiple data from your schema using an array of ids you should define the args given to the users in your schema as follows:
fields: () => ({
users: {
type: new GraphQLList(USER_GRAPHQL_OBJECT_TYPE),
args: {
id: {type: new GraphQLList(GraphQLInt)}
},
resolve: (root, args) => {
// fetch users
}
}
})
notice the new GraphQLList wrapping the GraphQLInt type of the id.
Then, when querying your schema you can :
{
users(id: [2, 3]) {
id
username
posts {
title
tags {
name
}
}
}
}
Please let me know if it was helpful :)

this.find(...) never reaches callback within static method

I am working on a module which adds Friendship-based relationships to a Schema.
I'm basically trying to do what this guy is trying to do (which, AFAIK, should work--which is discouraging)
Why is find(...) in FriendshipSchema.statics.getFriends never reaching its callback?
EDIT - Please allow me to explain the expected execution flow...
inside accounts.js:
requires the 'friends-of-friends' module (loads friends-of-friends/index.js) which
requires friends-of-friends/friendship.js which exports a function that creates FriendshipSchema, adds static methods, returns Friendship Model.
requires friends-of-friends/plugin.js which exports the mongoose plugin that adds static and instance methods to `AccountSchema.
uses FriendsOfFriends.plugin (see friends-of-friends/index.js) to plug-in the functionality from friends-of-friends/plugin.js
defines AccountSchema.statics.search which calls this.getFriends.
Since this refers to the Account model once it is compiled, and since the plugin added schema.statics.getFriends, calling this.getFriends within AccountSchema.statics.search will call schema.statics.getFriends as defined in friends-of-friends/plugin.js, which will call Friendship.getFriends (defined by FriendshipSchema.statics.getFriends in friends-of-friends/friendship.js) which calls this.find(...) which should translate to Friendship.find(...)`
after retrieving an account document, I call account.search('foo', function (...) {...});, but as you can see in FriendshipSchema.statics.getFriends, the find method executes, but its callback is never invoked and the program hangs :(
I don't get any errors, so I know this is a logic problem, but I'm not sure why things are getting hung up where they are...
EDIT - see my answer below, I also needed to compile the models before I could call find on them.
account.js
var mongoose = require('mongoose'),
passportLocalMongoose = require('passport-local-mongoose');
var FriendsOfFriends = require('friends-of-friends')();
// define the AccountSchema
// username, password, etc are added by passportLocalMongoose plugin
var AccountSchema = new mongoose.Schema({
created: { type: Date, default: Date.now },
profile: {
displayName: { type: String, required: true, unique : true, index: true },
firstName: { type: String, required: true, trim: true, index: true },
lastName: { type: String, required: true, trim: true, index: true },
}
});
// plugin the FriendsOfFriends plugin to incorporate relationships and privacy
AccountSchema.plugin(FriendsOfFriends.plugin, FriendsOfFriends.options);
AccountSchema.statics.search = function (userId, term, done) {
debug('search')
var results = {
friends: [],
friendsOfFriends: [],
nonFriends: []
},
self=this;
this.getFriends(userId, function (err, friends) {
// never reaches this callback!
});
};
AccountSchema.methods.search = function (term, done) {
debug('method:search')
AccountSchema.statics.search(this._id, term, done);
};
module.exports = mongoose.model('Account', AccountSchema);
friends-of-friends/index.js
/**
* #author Jeff Harris
* #ignore
*/
var debug = require('debug')('friends-of-friends');
friendship = require('./friendship'),
plugin = require('./plugin'),
privacy = require('./privacy'),
relationships = require('./relationships'),
utils = require('techjeffharris-utils');
module.exports = function FriendsOfFriends(options) {
if (!(this instanceof FriendsOfFriends)) {
return new FriendsOfFriends(options);
}
var defaults = {
accountName: 'Account',
friendshipName: 'Friendship',
privacyDefault: privacy.values.NOBODY
};
this.options = utils.extend(defaults, options);
/**
* The Friendship model
* #type {Object}
* #see [friendship]{#link module:friendship}
*/
this.friendship = friendship(this.options);
/**
* mongoose plugin
* #type {Function}
* #see [plugin]{#link module:plugin}
*/
this.plugin = plugin;
debug('this.friendship', this.friendship);
};
friends-of-friends/friendship.js
var debug = require('debug')('friends-of-friends:friendship'),
mongoose = require('mongoose'),
privacy = require('./privacy'),
relationships = require('./relationships'),
utils = require('techjeffharris-utils');
module.exports = function friendshipInit(options) {
var defaults = {
accountName: 'Account',
friendshipName: 'Friendship',
privacyDefault: privacy.values.NOBODY
};
options = utils.extend(defaults, options);
debug('options', options);
var ObjectId = mongoose.Schema.Types.ObjectId;
var FriendshipSchema = new mongoose.Schema({
requester: { type: ObjectId, ref: options.accountName, required: true, index: true },
requested: { type: ObjectId, ref: options.accountName, required: true, index: true },
status: { type: String, default: 'Pending', index: true},
dateSent: { type: Date, default: Date.now, index: true },
dateAccepted: { type: Date, required: false, index: true }
});
...
FriendshipSchema.statics.getFriends = function (accountId, done) {
debug('getFriends')
var model = mongoose.model(options.friendshipName, schema),
friendIds = [];
var conditions = {
'$or': [
{ requester: accountId },
{ requested: accountId }
],
status: 'Accepted'
};
debug('conditions', conditions);
model.find(conditions, function (err, friendships) {
debug('this callback is never reached!');
if (err) {
done(err);
} else {
debug('friendships', friendships);
friendships.forEach(function (friendship) {
debug('friendship', friendship);
if (accountId.equals(friendship.requester)) {
friendIds.push(friendship.requested);
} else {
friendIds.push(friendship.requester);
}
});
debug('friendIds', friendIds);
done(null, friendIds);
}
});
debug('though the find operation is executed...');
};
...
return mongoose.model(options.friendshipName, FriendshipSchema);
};
friends-of-friends/plugin.js
var debug = require('debug')('friends-of-friends:plugin'),
mongoose = require('mongoose'),
privacy = require('./privacy'),
relationships = require('./relationships'),
utils = require('techjeffharris-utils');
module.exports = function friendshipPlugin (schema, options) {
var defaults = {
accountName: 'Account',
friendshipName: 'Friendship',
privacyDefault: privacy.values.NOBODY
};
options = utils.extend(defaults, options);
var Friendship = mongoose.model(options.friendshipName);
...
schema.statics.getFriends = function (accountId, done) {
debug('getFriends')
var model = mongoose.model(options.accountName, schema);
var select = '_id created email privacy profile';
Friendship.getFriends(accountId, function (err, friendIds) {
if (err) {
done(err);
} else {
model.find({ '_id' : { '$in': friendIds } }, select, done);
}
});
};
...
schema.methods.getFriends = function (done) {
schema.statics.getFriends(this._id, done);
};
};
The issue was related to which instance of mongoose was being required.
Within my main app, I was requiring mongoose from app/node_modules/mongoose whereas my friends-of-friends module--having listed mongoose as a dependency in package.json--was requiring mongoose from app/node_modules/friends-of-friends/node_modules/mongoose, which created two separate mongoose instances, which made things not work.
I removed mongoose as a dependency, removed the nested node_modules folder, and vioala, it works, again :)
should have RTFM
app/
| lib/
| node_modules/
| | mongoose/ <-- main app required here
| | friends-of-friends/
| | | node_modules/ <-- deleted; mongoose was only dep
| | | | mongoose/ <-- friends-of-friends module required here
| server.js

Define a function as a default in a field in mongoose

I want to do something like this:
var categorySchema = new Schema({
id: {
unique: true,
default: function() {
//set the last item inserted id + 1 as the current value.
}
},
name: String
});
This is posible?.
var categorySchema = new Schema({
id : {
type : Number
},
name : {
type : String
}
});
// Define a pre-save method for categorySchema
categorySchema.pre('save', function(next) {
var self = this;
// Example of your function where you get the last ID
getLastId(function(id){
// Assigning the id property and calling next() to continue
self.id = id;
next();
});
});

How to create random-salt-hash with crypto

I want to create a salt-hash using node.js crypto lib without having to parse any hardcoded data.
What do I mean with hardcoded?
var salt, hardcodedString = "8397dhdjhjh";
crypto.createHmac('sha512', hardcodedString).update(salt).digest("base64");
Isn't there any other way how I can create a random string without using raw javascript, random functions or hardcoding something?
Regards
UPDATE
var Crypto = require('crypto')
, mongoose = require('mongoose');
module.exports = mongoose.model('User', new mongoose.Schema({
username: {
type: String
, required: true
, index: { unique: true, sparse: true }
, set: toLower
},
email: {
type: String
, required: true
, index: { unique: true, sparse: true }
, set: toLower
},
salt: {
type: String
, set: generateSalt
},
password: {
type: String
, set: encodePassword
}
}),'Users');
function toLower(string) {
return string.toLowerCase();
}
function generateSalt() {
//return Math.round((new Date().valueOf() * Math.random())) + '';
Crypto.randomBytes('256', function(err, buf) {
if (err) throw err;
return buf;
});
// return Crypto.randomBytes('256'); // fails to
}
function encodePassword(password) {
return password;
// TODO: setter has no access to this.salt
//return Crypto.createHmac('sha512', salt).update(password).digest("base64");
}
function authenticate(plainPassword) {
return encodePassword(plainPassword) === this.password;
}
A quick look at the documentation turns up the crypto.randomBytes function.
var buf = crypto.randomBytes(16);
This returns a buffer containing raw bytes. If you want a string, you can use toString('base64') or toString('hex').

Categories

Resources