Mongoose: MongoDB followers and following structure on update - node.js

I'm doing a following and followers addons to my code: here is the code.
In summary, the workflow is like this: I update an array of followers adding the ObjectId of the following person and vice versa,
however at the first update, my doc returns 0, it means that is hasnt been update, do you have any hint?
if (typeof(req.body.userToFollow) != 'undefined') {
var conditions = { _id: req.user._id }
, update = {"$addToSet":{following: req.body.userToFollow}}
, options = {};
Users.update(conditions, update, options, function(err, doc){
if (!err && doc) {
var reconditions = { _id: req.body.userToFollow }
, reupdate = {"$addToSet": { followers : req.user._id } }
, reoptions = {};
Users.update(conditions, update, options, function(err, doc){
if (!err && doc) {
var body = JSON.stringify(doc);
res.header('Content-Type', 'application/json');
res.end(body);
} else {
console.log(err);
}
});
} else {
console.log("\n\n\n\n\n");
console.log(doc);
}
});
}
many thanks

Use safe mode by passing {safe:true} in the options to update(). Then you will be able to check the status of the update.

Related

Remove object array items by comparing object array from mongodb

There is document which is having array of object inside.
Like
Objectid('')
fruits : [{_id:'2'},{_id:'3'},{_id:'4'}]
I want to delete these items fruits_id = [{_id:'3'},{_id:'4'}].
fruit_group.updateOne(
{collection_id: collection_id},
{$pullAll: {"fruits": fruits_id}}
)
so far i found below logic which i think is not efficient.
routes.post('/removeFruits', async (request, response, next) => {
var post_data = request.body;
var collection_id = post_data.collection_id;
var fruits_ids = JSON.parse(post_data.fruits_ids);
var prev_fruits;
await fruit_group.findOne({'collection_id': collection_id}, function (err, result) {
if (err) {
console("Some error occurred");
response.json({'message': "Some error occurred", 'result': 'false'});
}
prev_fruits = result.fruits;
});
for (var i = 0; i < fruits_ids.length; i++) { // this will delete all occurring items from array
var key = fruits_ids[i].user_id;
prev_fruits.filter(x => x.user_id === key).forEach(x => prev_fruits.splice(prev_fruits.indexOf(x), 1));
}
await fruit_group.updateOne({'collection_id': collection_id}, {$set: {'fruits': prev_fruits}}, function (err, result) {
if (err) {
response.json({'message': "Some error occurred", 'result': 'false'});
}
response.json({'message': 'Deletion successfully', 'result': 'true'});
});
});
is there anyway to achieve the same result?
Assuming fruits_id = [{ _id: '3' }, { _id: '4' }], you could do something like this using $pull and $in:
await fruit_group.updateOne({'collection_id': collection_id}, { $pull: { fruits: { $in: fruits_id }}})
This follows the example of removing all items that equal a specified value.

Can I perform mongoose update from post save middleware?

Is it possible to update a document from a post save mongoose middleware? Because it is not working for me.
I have tried in different ways.
Way 1:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
// update the record with quoteString
this.update({ _id: this.id }, this, { new: true }, function(err, result) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 2: because this contains the saved object id so I tried directly.
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
this.quoteString = doc.quoteNumber + "";
this._doc.quoteString = doc.quoteNumber + "";
enter code here
// update the record with quoteString
this.update(function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
Way 3:
QuoteSchema.post('save', function(doc) {
if (doc.quoteString) {
return;
}
var _quoteString = doc.quoteNumber+"";
this.update({ _id: doc._id }, { $set: { "quoteString": _quoteString } }, function(err) {
if (!err) {
console.log("Document Updated");
}
});
console.log('post save', doc.quoteString);
});
None of these ways works for me.
All I have to do is to update QuoteNumber field after the save. QuoteNumber is being generated from mongoose autoincrement which requires a number field. and I'm also saving a string version of quoteNumber in quoteString field so that in the UI, I can perform regex search in an autocomplete. As regular expression does not work with number type.
any suggestions will be helpful. Thanks.
Just make the autoincrementing field virtual and you don't have to worry about post save hook...
const QuoteSchema = new Schema(
{
quoteNumber: { type: Number },
quoteString: { type: String },
},
);
QuoteSchema.virtual('quote').set(function(value) {
this.quoteNumber = Number(value);
this.quoteString = String(value);
});
QuoteSchema.virtual('quote').get(function() {
return this.quoteNumber;
});
Setup:
QuoteSchema.plugin(autoIncrement.plugin, { model: 'Quote', field: 'quote' });

Mongodb, Node - Updating multiple objects synchronously

Using Mongodb, Nodejs, Async.js and Express.js
I am trying to update multiple documents, where each document has its own update, at the same time. I want to wait for all documents to update so that I can notify user that all documents have updated.
The issue I am having now is that my callback function is not firing, or if it is, then nothing is happening. Here is my progress:
db.client.collection('page').find({page_id: page_id}).toArray(function(page_err, document_page) {
if(page_err) {
throw page_err;
} else if(document_page === '' || document_page === undefined || document_page === null) {
throw page_err;
} else {
var count = 0;
async.each(data, function iteratee(i, callback) {
var item_id = (i.item_id === '') ? new ObjectId() : new ObjectId(i.item_id);
var query = {item_id: item_id};
var update = {
_id : new ObjectId(),
page_id : page_id,
section_id : null,
item_id : item_id,
created : new Date().toISOString(),
item_type : "dish",
item: {
title: i.title,
description: i.description,
price: i.price,
star: false,
double_star: false
},
last_modified: new Date().toISOString()
};
var options = { upsert: true };
db.client.collection('item').updateOne(query, {$set: update}, options, function(item_err, results) {
if(item_err) {
res.sendStatus(500);
} else if(results === '' || results === undefined || results === null) {
res.sendStatus(400);
} else {
++count;
if(count === data.length) {
callback();
return;
}
}
});
}, function() {
console.log('sending 200 status');
res.sendStatus(200);
});
}
});
When I run the code I do enter the if statement where I call callback(). I have been stuck on this for a few hours and I cannot get it to work. If you need more info, I'd be happy to provide it. For simplicity's sake I removed many console.logs to avoid clutter as well.
All iterations need to fire the callback otherwise it will hang indefinitely. callback must be called in every iteration. Always.
If you encounter an error, you need to call callback(error). The problem you'll have is that async.each schedules all iterations beforehand so iteratee will fire data.length times regardless of whether an error is encountered half way through execution or not. If you need to run them in series you can use async.eachSeries which will take more time but gives you better control and no need to rollback.
So code wise it looks like this:
db.client.collection('page').find({page_id: page_id}).toArray(function(page_err, document_page) {
if(page_err) {
throw page_err;
} else if(document_page === '' || document_page === undefined || document_page === null) {
throw page_err;
} else {
async.each(data, function iteratee(i, callback) {
var item_id = (i.item_id === '') ? new ObjectId() : new ObjectId(i.item_id);
var query = {item_id: item_id};
var update = {
_id : new ObjectId(),
page_id : page_id,
section_id : null,
item_id : item_id,
created : new Date().toISOString(),
item_type : "dish",
item: {
title: i.title,
description: i.description,
price: i.price,
star: false,
double_star: false
},
last_modified: new Date().toISOString()
};
var options = { upsert: true };
db.client.collection('item').updateOne(query, {$set: update}, options, function(item_err, results) {
if(item_err) {
callback(500);
} else if(results === '' || results === undefined || results === null) {
callback(400)
} else {
callback();
}
});
}, function(err) {
// Passing the status code only for the example.
// `err` should be an object with more metadata probably
if(err) {
res.sendStatus(err);
return;
}
console.log('sending 200 status');
res.sendStatus(200);
});
}
});

How to check if that data already exist in the database before save

Hey guys I have a question, how to do validations before saving the edited or posted (post or put action ) data in mongoose !?
for Example, if action already exist in the database, the user will receive a some sort of error. I try this but not working :
1-NOT WORK
var mongoose = require("mongoose"),
Schema = mongoose.Schema;
var actionSchema = new Schema({
action: {
type: String,
required: true,
},
});
var data = mongoose.model('Action', actionSchema);
actionSchema.pre('save', function (next) { // Middlware to verify if action already existe
var self = this;
data.find({
action: self.action
}, function (err, actions) {
if (!actions.length) {
next();
} else {
console.log('action exists: ', self.name);
next(new Error("Action exists!"));
}
});
});
module.exports = mongoose.model('Action', actionSchema);
2 --- NOT WORK SECOND METHODE : ------------------------------------
var data = mongoose.model('Action', actionSchema);
actionSchema.pre('save', function (next) {
data.count({
action: this.action
}, function (err, count) {
if (count == 1) {
console.log('action exists: ', this.action);
next(new Error("Action exists!"));
//already exists
} else {
next();
//do the action
}
});
});
3- WORKING ALTERNATIVE -- NODE JS CONTROLLER ----I found this trick (work good) that is to do a check before the update (check)
But I would like to know if there is possibility to do it before my save in my model MONGOOSE !?
// router.put('/actions/:action_id');
Action.findById(req.params.action_id, function (err, upaction) {
if (err) { //no action id in database match with params.action_id
res.send(err);
} else { // find == true
// chek if action name existe
Action.findOne({
'action': req.body.action
})
.exec(function (err, found_action) {
if (err) { // ereur bizare sest produite
next(err);
}
if (found_action) { // name action exist
res.send('name action existe');
}
else { // name action no exist
upaction.action = req.body.action;
upaction.save(function (err, acti) {
if (err) {
res.send('error on save');
}
res.send(upaction); // send a document
});
}
});
}
});
Check whether the data already avaliable and then perform the action you want
var data = mongoose.model('data', actionSchema);
data.count({action: this.action}, function(err, count) {
if(count == 1){
//already exists
}
else{
actionSchema.pre('save', function (next) {
});
}
});
I don't understand why are you doing too many operation to do a single operation. Mongodb provides update function which has ability to check and insert. If you want a document to be inserted only when some condition comes true or false. update can do that in a singly query. Here you go.
Action.update({ action:{ $eq:req.body.action }},{ $setOnInsert: new_action }, { upsert: true }, function(err, res){
if(!err && !!res.upserted){
// no document was found hence inserted
}else if(!err && !res.upserted){
// already existing
}else{
// something wicked happend
}
})
Here you need to pay attention that new_action must not be a instance of your mongoose model rather it should be simple object/document which you want to insert.

How can i have auto-increment field in nedb?

I want to have exactly auto-increment field like relational or objective databases, so i need an integer _id field with automatically set field value, value should be one more last record _id value like this:
data:
{_id:1,name"foo"}
{_id:2,name"bar"}
remove last record:
{_id:1,name"foo"}
add new record:
{_id:1,name"foo"}
{_id:3,name"newbar"}
I added a function to my datastore and calculate maximum of _id and plus 1 max(_id)+1 and set as field value, but there is problem here:
When we use auto-increment field in relational databases, it works like i said and after you remove last record it reserved a deleted record number and new inserted records continue increment but in my way its says the _id of removed record for new record.
My code is:
var Datastore = require('nedb'),
localDb = new Datastore({
filename: __dirname + '/dbFilePath.db',
autoload: true
});
localDb.getMax = function(fieldName, onFind){
db.find({}).sort({_id:-1}).limit(1).exec(function (err, docs) {onFind && onFind(err, docs['_id']);});
return localDb;
}
localDb.insertAutoId = function(data, onAdd){
var newIndex = 0;
localDb.getMax(function (err, maxValue) {
newIndex = maxValue+1;
if(!data["_id"])
data["_id"] = newIndex;
localDb.insert(data, function (err, newDoc) {
onAdd && onAdd(err, newDoc);
});
});
return localDb;
}
An improved answer for nedb would be:
db.getAutoincrementId = function (cb) {
this.update(
{ _id: '__autoid__' },
{ $inc: { seq: 1 } },
{ upsert: true, returnUpdatedDocs: true },
function (err, affected, autoid) {
cb && cb(err, autoid.seq);
}
);
return this;
};
Which is equivalent to the mongodb way:
db.getAutoincrementId = function (cb) {
this.findAndModify({
query: { _id: '__autoid__' },
update: { $inc: { seq: 1 } },
new: true
}
function (err, autoid) {
cb && cb(err, autoid.seq);
}
);
return this;
};
You can store the last value of the index in the database. Something like this:
var Datastore = require('nedb');
var db = new Datastore({
filename: __dirname + '/dbFilePath.db',
autoload: true
});
// Initialize the initial index value
// (if it already exists in the database, it is not overwritten)
db.insert({_id: '__autoid__', value: -1});
db.getAutoId = function(onFind) {
db.findOne( { _id: '__autoid__' }, function(err, doc) {
if (err) {
onFind && onFind(err)
} else {
// Update and returns the index value
db.update({ _id: '__autoid__'}, { $set: {value: ++doc.value} }, {},
function(err, count) {
onFind && onFind(err, doc.value);
});
}
});
return db;
}
I do not know if it will be useful for you anymore I use a database to store the next ids, inspired in the mysql system. Who always reserves the next id.
So I created a function that verifies if there is an id to the db, if it does not, it add with the value "1", and when it updates it looks for and if it exists and it performs the sequence.
This gave me full control over my ids.
The schema would be:
{
name: nameDb,
nextId: itemID
}
If you want you can create functions for updating documents, versioning, etc.
example:
db.autoincrement = new Datastore({filename: 'data/autoincrement.db', autoload: true});
function getUniqueId(nameDb, cb) {
db.autoincrement.findOne({name: nameDb}, function (err, doc) {
if (err) {
throw err;
} else {
if (doc) {
const itemID = doc.nextId + 1;
db.autoincrement.update({name: nameDb}, {
name: nameDb,
nextId: itemID
}, {}, function (err, numReplaced) {
db.autoincrement.persistence.compactDatafile();
if (err) {
throw err;
} else {
// console.log(numReplaced);
}
cb(doc.nextId);
});
} else {
const data = {
name: nameDb,
nextId: 2
};
db.autoincrement.insert(data, function (err, newDoc) {
if (err) {
throw err;
} else {
// console.log(newDoc);
}
cb(1);
});
}
}
});
}
insert new document example:
function insert(req, cb) {
getUniqueId("testdb", function (uniqueId) {
data.itemId = uniqueId;
db.testdb.insert(data, function (err, newDoc) {
if (err) {
cb({error: '1', message: 'error#2'});
throw err;
}
cb({error: '0', message: 'Item add'});
});
});
}

Resources