How to properly use Mongoose query, Find and update object array - javascript

Here is Main Code to look into
function update(feed){
//.. do set up
var target = feed.title;
var value = {index: feed.value};
var query = { test: target, 'array.index': {$ne: value.index} },
update = { $push : {"array" : value} },
options = { upsert: true, new: true };
Model.findOneAndUpdate(query, update, options, function(err, docs) {
if(err) throw err;
console.log(docs);
}
);}
(I modified the code to be general. if you need specific code, just ask me to update this post)
I am trying to do Model.findOneandUpdate with upsert: true.
My code executes when specific event emitted
feedparser.on('readable', function() {
update(this.read());
});
Becasue of 'array.index': {$ne: value.index} query, the code creates new one after the first execution.
Therefore,
db.collection.find()
returns multiple documents that has same property with different ObjectId.
For Example,
{"_id":ObjectId("1"), test: "A", array:[{index:"1"}]}
{"_id":ObjectId("2"), test: "A", array:[{index:"1"}]}
I want to be the code do
Check document exist or not.
if Exist, add new value to the Document's array. Also, new value should be unique by index.
if NOT Exist, create new Document and add new value to new Document's array.
UPDATE:
I also try to do it by
var doc = Model.findOne({ test: target });
if(doc != null){
var query = { _id: doc._id, 'array.index': {$ne: value.index} };
var update = { $push : {"array" : value} };
var options = {new: true};
Model.update(query, update, options, function(err, d){
if(err) throw err;
console.log(d);
});
}else{
doc = {
test: target,
array:[value]
};
Model.create(doc, function (err, res){
if(err) throw err;
console.log(res);
});
}
This code result in do Nothing.
UPDATE
I also try this by
Model.findOne({ test:target }, function(err, doc){
if(doc === null){
doc = {
test: target
arrays:[value]
};
Animation.create(doc, {new: true}, function (err, res){
if(err) throw err;
console.log(res);
});
}else{
var query = { _id: doc._id, 'arrays.index': {$ne: value.index} };
var update = { $push : {"arrays" : value} };
var options = {new: true};
Animation.update(query, update, options, function(err, res){
if(err) throw err;
console.log(res);
});
}
});
But, it create new Document by each different index value.
UPDATE
var query = { test: target, 'array:index': {$ne: value.index} };
var update = { $push : {'array' : value}};
var options = {new: true};
Model.findOneAndUpdate(query, update, options, function(err, doc){
if(err) throw err;
if(doc === null){
doc = new Model({
test: target,
array:[value]
});
doc.save();
}
});
It also does not work...

I found how to solve this problem!
First, the reason why the problem arise is I am trying to do upsert with $ne.
Here is my code,
Animation.findOne({ title: AnimationTitle, team: TeamName }, function(err, result){
if(err) { throw err; }
if(result){
Animation.findOneAndUpdate({ title: AnimationTitle, team: TeamName, 'episodes.number': {$ne: episode.number} }, { $addToSet : {'episodes' : episode}}, {new: true}, function(err, result){
if(err) { throw err; }
});
}else{
Animation.findOneAndUpdate({ title: AnimationTitle, team: TeamName }, { $addToSet : {'episodes' : episode}}, {new: true, upsert: true}, function(err, result){
if(err) { throw err; }
});
}
});

Related

MongoDB Error 'Cannot read property '_id' of undefined'

I'm setting up a web-app with chat rooms for teachers and their students. Teachers will invite their students to the program and therefore I need to validate whether the students have an account already.
I've scoured the internet for solutions but none of the solutions are for the same issue as mine
function insertUsers(collectionName, userArray) {
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db('squeakdb');
for (var i=0; i<userArray.length; i++) {
dbo.collection(collectionName).find({ studentId: userArray[i].studentId }).toArray(function (err, res) {
console.log(res == '');
// If res == '' is true, it means the user does not already have an account
if (res == '') {
dbo.collection(collectionName).insertOne(userArray[i], function(error, result) {
if (error) throw error;
console.log('Inserted');
});
}
});
}
});
}
insertUsers('userlist', [{ 'studentId': 'STU0001' }, { 'studentId': 'STU0018', 'firstName': 'testName' }]);
The expected result is for the first object in the array to not be inserted into the database, and for the second object to be inserted.
The current result is the first object not being inserted (as expected) and the second object producing the following error:
TypeError: Cannot read property '_id' of undefined
I've discovered why the error occurred, it was caused by doing an asynchronous call inside a for loop. Here is the fixed code.
function insertUsers(collectionName, userArray) {
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db('squeakdb');
userArray.forEach(function (index){
dbo.collection(collectionName).find({ studentId: index.studentId }).toArray(function (err, res) {
console.log(res.length == 0);
if (res.length == 0) {
dbo.collection(collectionName).insertOne(index, function(error, result) {
if (error) throw error;
console.log('Inserted');
});
}
});
});
});
}

How to add object to nested Array using Node.js and Mongoose

How can I add object to my nested Array in PartnerSchema?
I separate documents, because in the future there will be more of nested arrays.
This is my schema:
var productSchema = new mongoose.Schema({
name: String
});
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}]
});
module.exports = {
Partner: mongoose.model('Partner', partnerSchema),
Product: mongoose.model('Product', productSchema)
}
And this is my backend:
var campSchema = require('../model/camp-schema');
router.post('/addPartner', function (req, res) {
new campSchema.Partner({ name : req.body.name }).save(function (err, response) {
if (err) console.log(err);
res.json(response);
});
});
router.post('/addProduct', function (req, res) {
campSchema.Partner.findByIdAndUpdate({ _id: req.body.partnerId },
{
$push: {
"products": {
name: req.body.dataProduct.name
}
}
}, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
I can add Partner by using /addPartner and it works fine.
Problem is with second function /addProduct I can't add Product to Array in Partner Schema. I have an error: CastError: Cast to undefinded failed for value "[object Object]" at path "products"
Since the products field in Partner model is an array that holds _id refs to the Product model, you are supposed to push an _id to the array, not an object hence Mongoose complains with an error.
You should restructure your code to allow the saving of the Product _id ref to the Partner model:
router.post('/addProduct', function (req, res) {
var product = new campSchema.Product(req.body.dataProduct);
product.save(function (err) {
if (err) return throw err;
campSchema.Partner.findByIdAndUpdate(
req.body.partnerId,
{ "$push": { "products": product._id } },
{ "new": true },
function (err, partner) {
if (err) throw err;
res.json(partner);
}
);
});
});

Cannot read property "rid" of undefined

[TypeError: Cannot read property 'rid' of undefined]
Is the error that I get when I try to execute this controller on my post route.
I've tested it out with Postman.
I've tried to console.log(result) but I get undefined.
My query gets executed and my row is inserted into my table. I've checked it. Password is also hashed.
The problem is that I don't get any out binds that should be returned.
Problematic code (IMO) is
...
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
...
oracle-NodeDB Wrapper
var oracledb = require('oracledb');
module.exports.OBJECT = oracledb.OBJECT;
function executeSQL(config ,sql, bindParams , options) {
return new Promise(function(resolve, reject) {
oracledb.getConnection(
config,
function(err, connection) {
if (err) {
return reject(err);
}
connection.execute(
sql,
bindParams,
options,
function(err, result) {
if (err) {
doRelease(connection);
return reject(err);
}
resolve(result);
doRelease(connection);
});
});
});
}
function doRelease(connection) {
connection.release(
function(err) {
if (err) {
console.log(err.message);
}
}
);
}
module.exports.executeSQL = executeSQL;
Controller
var database = require('../database/oracledbWrapper');
var dbconfig = require('../database/dbconfig').dbconfig;
var jwt = require('jsonwebtoken');
var bcrypt = require('bcrypt');
exports.createUser = function(req, res, next) {
var user = {
email: req.body.email
};
var unhashedPassword = req.body.password;
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(unhashedPassword, salt, function(err, hash) {
if (err) {
return next(err);
}
user.hashedPassword = hash;
insertUser(user, function(err, user) {
var payload;
if (err) {
return next(err);
}
payload = {
sub: user.email,
role: user.role
};
res.status(200).json({
user: user,
token: jwt.sign(payload, config.jwtSecretKey, {expiresInMinutes: 60})
});
});
});
});
}
function insertUser(user, cb) {
var bindParams = {
email: user.email.toLowerCase(),
password: user.hashedPassword,
rid: {
type: database.NUMBER,
dir: database.BIND_OUT
},
remail: {
type: database.STRING,
dir: database.BIND_OUT
},
rrole: {
type: database.STRING,
dir: database.BIND_OUT
}
};
database.executeSQL(
dbconfig,
'insert into express_users (email, password, role ) values ( :email, :password, \'BASE\' ) returning id, email, role into :rid , :remail, :rrole',
bindParams,
{}
)
.then(function(result) {
console.log(result);
cb(null, {
id: result.outBinds.rid[0],
email: result.outBinds.remail[0],
role: result.outBinds.rrole[0]
});
})
.catch(function(err) {
console.log(err);
next(err);
});
}
Route
var RESTfulAPICon = require('../controllers/RESTfulAPI');
var indexCon = require('../controllers/index');
var views = require('express').Router();
views.route('/users').post(RESTfulAPICon.createUser);
exports.views = views;
The problem was in my wrapper , mainly here
module.exports.OBJECT = oracledb.OBJECT;
I export only the OBJECT property , but I try to access BIND_OUT properties later on. And they are non existent.
If I do the full export like this
module.exports.OBJECT = oracledb;
Then I can access BIND_OUT properties.

How to update inner document in node using mongoose

I want to insert cont and cont_type inside pro_picture. But this is not a embedded schema.
My schema:
var userSch = new Schema({
name: String,
email: String,
profile_picture: {
cont: String,
cont_type: String
}
});
var User = mongoose.model('user', userSch);
Now I want to find the user whose email is "something#example.com' and update the pro_picture.
I tried the following code.
User.findOne({email: data.email}, function (err, user) {
var userData = new User({
profile_picture: {
cont: data.pro_cont,
cont_type: data.pro_cont_type
}
});
user.profile_picture = userData;
console.log(user.profile_picture);
console.log(user);
user.save(function (err) {
if(err) throw err;
callback(rjTypes.result.SUCCESS);
});
It shows
profile_picture: null //in console
but the field is not present in collection
Finally i found it..
I solved by using this technique....
User.findOne({email: data.email}, function (err, user) {
user.profile_picture.cont = data.pro_cont;
user.profile_picture.cont_type = data.pro_cont_type;
console.log(user.profile_picture);
console.log(user);
//user.profile_picture.cont_type = data.pro_cont_type;
user.save(function (err) {
if(err) throw err;
callback(rjTypes.result.SUCCESS);
});
});
I think you're doing one nesting to much, try changing
var userData = new User({
profile_picture: {
cont: data.pro_cont,
cont_type: data.pro_cont_type
}
});
to
var userData = {
cont: data.pro_cont,
cont_type: data.pro_cont_type
};

How to know if Mongoose's upsert created a new document?

I have this code in node.js/express.js:
var User = mongoose.model('User');
var usersRouter = express.Router();
usersRouter.put('/:id', function(req, res) {
req.body._id = req.params.id;
var usr = new User(req.body);
usr.validate(function (err) {
if (err) {
res.status(400).json({});
return;
}
var upsertData = usr.toObject();
delete upsertData._id;
User.update({_id: usr._id}, upsertData, {upsert: true}, function(err) {
if (err) {
res.status(500).json({});
return;
}
res.status(204).json({});
});
});
});
It works fine, but I would like to send a different response to the client if a new document has been created (status 201 with json in response body) or an existing one has been updated (status 204).
Is there a way to tell the difference from the callback of User.update?
Use the third parameter from callback function:
...
User.update({_id: usr._id}, upsertData, {upsert: true}, function(err, num, n) {
if (err) {
res.status(500).json({});
return;
}
if (!n.updatedExisting) {
/* new document */
}
res.status(204).json({});
});
...
n is an object like this:
{ updatedExisting: false,
upserted: <ObjectId>,
n: 1,
connectionId: 11,
err: null,
ok: 1 }
updatedExisting property is true when a document was updated -- so it was created before. If it's false, then it means that the new document was created during this call.

Categories