Sequelize - Cannot save associations in 1:N relationship - postgresql

What you are doing?
trying to add associations and save them to database
js
var Device = models.Device;
var Log = models.Log;;
var logs = [
{
"message_level" : 1,
"message" : "test log 1"
},
{
"message_level" : 1,
"message" : "test log 2"
},
{
"message_level" : 1,
"message" : "test log 3"
}
];
var devID = 'X3dE4DEW';
describe('Logs', function() {
it('should create log', function(done) {
Device.create({
"originalID" : devID
}).then(
function() {
return Device.findOne({"where": {originalID: devID},
include: [ { model: Log, as : "Logs" } ] }).then(
function(device) {
if (device) {
logs = logs.map(
function(log) {
return Log.build(log);
}
);
console.log(logs) // is NOT Empty -> OK
return device.addLogs(logs).then(
function(device) {
return device.getLogs().then(
function(logs) {
console.log(logs); // EMPTY [] -> NOT OK
logs.length.should.equal(3);
done();
}
);
}
);
}
return Promise.reject('Not valid Device ID');
}
).catch(function(error){
console.log(error);
done();
});
}
);
});
});
here is how are they defined
// models
// Device
Device = pg.define('device', {
originalID: {
type: Sequelize.STRING(16) // Up to 16
}
});
// LogRecord
Log = pg.define('log', {
message_level: {
type: Sequelize.INTEGER
},
message: {
type: Sequelize.STRING(100) // Up to 100
}
});
Device.hasMany(Log, { as : "Logs" });
Log.belongsTo(Device);
What do you expect to happen?
addLogs should save associations items to the database
What is actually happening?
Items are not saved, device.getLogs() is returning an empty array []
{ AssertionError: expected 0 to equal 3 }
Dialect: PG
Database version: 9.6.1
Sequelize version: ~3.30.x

There's several solutions to associate rows
if you're using add<Association(s)> function, be sure that associated objects are already stored in database.
or you can use create<Association> to create model and associate.
var device;
Device.create({
"originalID" : devID
}).then(function(createdDevice) {
device = createdDevice;
return Promise.all(logs.map(log=>{return device.createLog(log);}));
}).then(function(result) {
return device.getLogs();
}).then(function(logs) {
console.log(logs);
logs.length.should.equal(3);
done();
}).catch(function(error){
console.log(error);
done();
});
Use promises style like above, it's more understandable, not "go deep into promises".

Related

mongoose When I Use update it updates Nothing with status 200(success)

I use update Query for push some data in array in Mongodb and I use mongoose in nodeJs.Pplease anyone can help out from this.
Model Schema :
var mongoose = require('mongoose')
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt')
var schema = new Schema({
email: { type: String, require: true },
username: { type: String, require: true },
password: { type: String, require: true },
creation_dt: { type: String, require: true },
tasks : []
});
module.exports = mongoose.model('User',schema)
So I use this schema and I want to push data in tasks array and here is my route code for pushing data.
Route For Update Data in Tasks:
router.post("/newTask", isValidUser, (req, res) => {
addToDataBase(req, res);
});
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
var usr = new User(req.user);
usr.update({ email: req.user.email }, { $push: { tasks: dataa } });
console.log(req.user.email);
try {
doc = await usr.save();
return res.status(201).json(doc);
} catch (err) {
return res.status(501).json(err);
}
}
Here I create a async function and call that function in route but when I post data using postman it response with status code 200(success) but it updates nothing in my database.
Output screenshot:
as you can see in this image task : [].. it updates nothing in that array but status is success
I don't know why is this happening.
You can achieve this task easier using findOneAndUpdate method.
router.put("/users", isValidUser, async (req, res) => {
var data = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
try {
const user = await User.findOneAndUpdate(
{ email: req.user.email },
{
$push: {
tasks: data,
},
},
{ new: true }
);
if (!user) {
return res.status(404).send("User with email not found");
}
res.send(user);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Also I strongly suggest using raw / JSON data for request body, that's how most ui libraries (reactjs, angular) send data.
To be able to parse json data, you need to add the following line to your main file before using routes.
app.use(express.json());
TEST
Existing user:
{
"tasks": [],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Request body:
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
Response:
{
"tasks": [
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Also as a side note, you had better to create unique indexes on username and email fields. This can be done applying unique: true option in the schema, but better to create these unique indexes at mongodb shell like this:
db.users.createIndex( { "email": 1 }, { unique: true } );
db.users.createIndex( { "username": 1 }, { unique: true } );
It's been awhile since I've done mongoose, but I'm pretty sure <model>.update() also actively updates the record in Mongo.
You use .update() when you want to update an existing record in Mongo, but you are instantiating a new User model (i.e. creating a new user)
try the following code instead for a NEW USER:
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
// email field is already in `req.user`
var usr = new User({ ...req.user, tasks: [dataa] });
console.log(req.user.email);
try {
await usr.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
Now, if you wanted to update an existing record :
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
try {
await usr. updateOne({ email : req.user.email}, { $push: { tasks: dataa } });
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
For more info read: https://mongoosejs.com/docs/documents.html

MongoDB putting they key into $set instead of using it for lookup?

I am trying to update a message using userID as my _id
Is splitting it up into findOne - Save - Update the best way?
//
// Find and update message
//
var messageModel = require('../models/messageModel');
var messageTable = mongoose.model('messageModel');
var messageRecord = new messageModel();
var findMessage = () => {
return new Promise((resolve, reject) => {
console.log("=====START findMessage=====")
messageTable.findOne(
{ _id: userID }
,function(err, data) {
if (err) {
reject(new Error('findMessage: ' + err))
return;
}
// Who will have this as unread?
if (userManager==true) {
messageRecord.readUser = false;
messageRecord.readManager = true;
} else {
messageRecord.readUser = true;
messageRecord.readManager = false;
}
// If message not found, then create new one
if (!data) {
console.log("=====CREATE NEW RECORD=====")
messageRecord._id = userID;
messageRecord.activityDate = Math.round(new Date().getTime()/1000);
messageRecord.messages = {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
messageRecord.save(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
}
// If message found, then add message
console.log("=====ADD LINE TO RECORD=====")
messageTable.update (
{ _id: userID },
{
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})
console.log("=====RESOLVE findMessage=====")
resolve();
return;
});
})};
Do I need to put upsert:true? (what ever that means)
Or should I use findOneAndUpdate?
And would you use findOneAndUpdate or just update? And why?
I tought it went like this:
findone
if not found then save
if found then update
UPDATE - Thanks to lascot I ended up doing this, and it works great!
// Save message
messageTable.update (
{ _id: userID },
{
$setOnInsert: {
_id: userID
},
$set: {
activityDate : Math.round(new Date().getTime()/1000),
readUser : messageRecord.readUser,
readManager : messageRecord.readManager
},
$push: {
messages: {
"message" : message,
"date" : Math.round(new Date().getTime()/1000),
"property" : propertyID,
"booking" : bookingID,
"manager" : userManager
}
}
},
{ upsert: true }
).exec(function (err, res) {
if (err) {
reject(new Error('findMessage: ' + err));
return;
}
})

How to make querys when tou have many to many relationships between models?

i am trying to make a game. I need tu create a Match. I think the problem on this Way. The User create a Match. In a third table I save playerId and gameId. When another user join the match, I save again, playerId and gameId. Then, I make a query with player with gameId in common, and start the game.
first, One User may have many Games. second, One Match may have many Games. this is the Match model:
module.exports = {
attributes: {
name: {
type: 'string'
},
description: {
type: 'string'
},
game: {
collection: 'game',
via: 'gameId',
}
}
};
This is the User model:
var bcrypt = require('bcrypt');
module.exports = {
attributes: {
name: {
type:'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
},
passwordConfirmation: {
type: 'string'
},
passwordEncrypted: {
type: 'string'
},
creator: {
collection: 'game',
via: 'playerId'
},
toJSON: function(){
var obj = this.toObject();
delete obj.password;
delete obj.passwordConfirmation;
delete obj._csrf;
return obj;
}
}, beforeCreate: function(values, next){
console.log("Acabo de entrar a eforeCreate");
var password = values.password;
var passwordConfirmation = values.passwordConfirmation;
if(!password || !passwordConfirmation || password != values.passwordConfirmation) {
var passwordDoesNotMatchError = [{
name: 'passwordDoesNotMatchError',
message: 'Las contraseñas deben coincidir'
}]
return next({
err: passwordDoesNotMatchError
});
}
require('bcrypt').hash(values.password, 10, function passwordEncrypted(err, EncryptedPassword){
values.EncryptedPassword = EncryptedPassword;
next();
});
}
};
This is the Game model:
module.exports = {
attributes: {
gameId: {
model: 'match'
},
playerId: {
model: 'user'
}
}
};
finally, this is my controller:
module.exports = {
createMatch: function(req,res){
var matchObj = {
name: req.param('name'),
description: req.param('description'),
}
Match.create(matchObj, function(err, match){
if(err){
console.log("el error fue: " + err);
return res.send(err);
} console.log("Entro en create");
return res.json(match);
})
var gameObj = {
gameId: 'aclaration: I dont know how do I get the match.id',
playerId: req.session.me
}
Game.create(gameObj,function(err,game){
console.log("entro a GameCreate");
if(err){
return res.send(err);
} return res.json(game);
})
}
};
I can create the Match, but Game.create send this error:
_http_outgoing.js:344 throw new Error('Can\'t set headers after they are sent.'); ^
Error: Can't set headers after they are sent.
Somebody can help me? probably, I have many errors. Thanks.
Couple of things here:
Having an explicit Game model is not required in Sails. It can manage it implicitly, unless you want to store more information than just gameId and userId. So, you can just do away with Game model.
Please refer for async programming: How do I return the response from an asynchronous call?
Below code should work for you. Hope it helps.
module.exports = {
createMatch: function(req, res) {
var matchObj = {
name: req.param('name'),
description: req.param('description'),
};
Match.create(matchObj, function(err, match) {
if (err) {
console.log("el error fue: " + err);
return res.send(err);
}
console.log("Entro en create");
var gameObj = {
gameId: match.id,
playerId: req.session.me
};
Game.create(gameObj, function(err, game) {
console.log("entro a GameCreate");
if (err) {
return res.send(err);
}
return res.json(game);
// return res.json(match);
});
});
}
};

Meteor call method to create Stripe customer when user/email exists in database

Submit a subscription plan form, which on the client runs through jquery validation and then calls createCustomer method. In the method, look for username and email (from the form in lowercase) and do a findOne and if no username and email exist in mongo, call stripeCreateCustomer and stripeCreateSubscription methods.
The issue is, it still registers the user to the Stripe dashboard with payment. EVEN though I receive error message saying username is taken, or email is taken. Why is stripeCreateCustomer and stripeCreateSubscription method still running?
Other than that, I believe it's working nicely. I just need email verification.. if anyone can help me with that as well, it'd be really nice. Thanks.
~/server/signup.js
import Future from 'fibers/future';
Meteor.methods({
createCustomer(customer) {
check(customer, {
username: String,
email: String,
password: String,
plan: String,
token: String
});
const usernameRegEx = new RegExp(customer.username, 'i');
const emailRegEx = new RegExp(customer.email, 'i');
const lookupCustomerUsername = Meteor.users.findOne({'username': usernameRegEx});
const lookupCustomerEmail = Meteor.users.findOne({'email': emailRegEx});
if(!lookupCustomerUsername) {
if(!lookupCustomerEmail) {
const newCustomer = new Future();
Meteor.call('stripeCreateCustomer', customer.token, customer.email, function(error, stripeCustomer) {
if(error) {
console.log(error)
} else {
const customerId = stripeCustomer.id,
plan = customer.plan;
Meteor.call('stripeCreateSubscription', customerId, plan, function(error, response) {
if(error) {
console.log(error)
} else {
try {
const user = Accounts.createUser({
username: customer.username,
email: customer.email,
password: customer.password
});
const subscription = {
customerId: customerId,
subscription: {
plan: customer.plan,
payment: {
card: {
type: stripeCustomer.sources.data[0].brand,
lastFour: stripeCustomer.sources.data[0].last4
},
nextPaymentDue: response.current_period_end
}
}
}
Meteor.users.update(user, {
$set: subscription
}, function(error, response) {
if(error) {
console.log(error)
} else {
newCustomer.return(user)
}
});
} catch(exception) {
newCustomer.return(exception);
}
}
});
}
});
return newCustomer.wait();
} else {
throw new Meteor.Error('customer-exists', 'email address is already taken')
}
} else {
throw new Meteor.Error('customer-exists', 'username is already taken')
}
}
});
~/server/stripe.js
import Future from 'fibers/future';
const secret = Meteor.settings.private.stripe.testSecretKey;
const Stripe = StripeAPI(secret);
Meteor.methods({
stripeCreateCustomer(token, email) {
check(token, String);
check(email, String);
const stripeCustomer = new Future();
Stripe.customers.create({
source: token,
email: email
}, function(error, customer) {
if(error) {
stripeCustomer.return(error)
} else {
stripeCustomer.return(customer)
}
});
return stripeCustomer.wait();
},
stripeCreateSubscription(customer, plan) {
check(customer, String);
check(plan, String);
const stripeSubscription = new Future();
Stripe.customers.createSubscription(customer, {
plan: plan
}, function(error, subscription) {
if(error) {
stripeSubscription.return(error)
} else {
stripeSubscription.return(subscription)
}
});
return stripeSubscription.wait();
}
});
for MasterAM:
Sorry for keep asking and thanks for sticking around. I'm very new at this... I provided a findOne of example user's document. After submitting the form, (if username or email does not exist in db) this is what is added:
{
"_id" : "WyWmdyZenEJkgAmJv",
"createdAt" : ISODate("2016-05-12T07:27:16.459Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$u8hyzWPu6Dnda1r8j7GkBuvQiF2iGFa5DdjEoD/CkHhT0jU.IsHhu"
}
},
"username" : "test",
"emails" : [
{
"address" : "test#test.com",
"verified" : false
}
],
"customerId" : "cus_8R6QC2ENNJR13A",
"subscription" : {
"plan" : "basic_monthly",
"payment" : {
"card" : {
"type" : "Visa",
"lastFour" : "4242"
},
"nextPaymentDue" : 1465716435
}
}
}

ng-repeat showing functions names and data

I'm a beginner with angularjs and mongolab ..
I have this code, to edit a record in mongolab :
function EditCtrl($scope, $location, $routeParams, Project) {
var self = this;
Project.get({id: $routeParams.projetId}, function(projet) {
self.original = projet;
$scope.projet = new Project(self.original);
});
$scope.save = function() {
$scope.projet.update(function() {
$location.path('/list');
});
};
}
It works perfectly.
I wanted to display all the keys and values from the record, this is the code :
<div ng-repeat="(key, val) in projet">
<div>{{key}}:{{val}}</div>
</div>
And this is the result :
_id:{}
destroy:
name:Test test
rang:4
update:
In my record, i only have the _id, name and rang. I don't know why "destroy:" dans "update:" are displayed! probably because i use this code to connect to mongolab :
angular.module('mongolab', ['ngResource']).
factory('Project', function($resource) {
var Project = $resource('https://api.mongolab.com/api/1/databases' +
'/_____/collections/_________/:id',
{apiKey: '___________________'}, {
update: {method: 'PUT'}
}
);
Project.prototype.update = function(cb) {
return Project.update({id: this._id.$oid},
angular.extend({}, this, {_id: undefined}), cb);
};
Project.prototype.destroy = function(cb) {
return Project.remove({id: this._id.$oid}, cb);
};
return Project;
});
What should i do to only display the record data ?
thanks
Return a service and pass the item you receive from get() back to update and destroy.
factory('Project', function($resource) {
return {
get: function() {
return $resource('https://api.mongolab.com/api/1/databases' +
'/_____/collections/_________/:id',
{apiKey: '___________________'}, {
update: {method: 'PUT'}
},
update : function(itm, cb) {
return item.update({id: item._id.$oid},
angular.extend({}, item, {_id: undefined}), cb);
},
destroy : function(item, cb) {
return item.remove({id: item._id.$oid}, cb);
};
Otherwise you can instantiate only one and reference it
factory('Project', function($resource) {
var item =$resource('https://api.mongolab.com/api/1/databases' +
'/_____/collections/_________/:id',
{apiKey: '___________________'}, {
update: {method: 'PUT'}
return {
update : function(cb) {
return item.update({id: item._id.$oid},
angular.extend({}, item, {_id: undefined}), cb);
},
destroy : function(cb) {
return item.remove({id: item._id.$oid}, cb);
};