How to find one object in mongodb [duplicate] - node.js

This question already has answers here:
MongoDB atomic "findOrCreate": findOne, insert if nonexistent, but do not update
(4 answers)
Closed 3 years ago.
I'm new to mongodb and am trying to compare a name typed from a input with a names saved in my collection and if there is this same name i want to increment "numberOfSearches" in database of this object name and if not let it create that one in collection. After all i don't know how to compare those names in "if", should i use Find method otherwise or there is other method for that?
app.post('/stronka', function(req,res){
var obj = {name: req.body.chosenCity, numberOfSearches: 1};
if(db.collection('miasta').find({name: obj.name})){
db.collection('miasta').updateOne(
{"name": obj.name},
{$inc: {"numberOfSearches": 1}}
)
res.redirect('/stronka');
console.log("first option");
}
else{
db.collection('miasta').insertOne(obj, function(err, result){
if(err) return console.log(err);
console.log('saved to database')
res.redirect('/stronka');
})
console.log("sec option");
}
})

One problem that you will likely run into here is that db.collection('...').find() will return an asynchronous Promise not a synchronous value. In order to use the result of the find(), you need to await it as such:
app.post('/stronka', async function(req,res){ <-- note the addition of the async keyword
var obj = {name: req.body.chosenCity, numberOfSearches: 1};
const result = await db.collection('miasta').find({ name: obj.name });
if (result) {
....
} else {
...
}
});
You will need to apply a similar principle to the updateOne and other calls to MongoDb, but I'll leave that as an exercise for you (:

Related

Mongoose subdocument update: dot notation assignment OR .findByIdAndUpdate() [duplicate]

I am using .pull to remove a record from an array in mongo db and it works fine, but a comment I read somewhere on stack overflow (can't find it again to post the link) is bothering me in that it commented that it was bad to use .save instead of using .findByIdAndUpdate or .updateOne
I just wanted to find out if this is accurate or subjective.
This is how I am doing it currently. I check if the product with that id actually exists, and if so I pull that record from the array.
exports.deleteImg = (req, res, next) => {
const productId = req.params.productId;
const imgId = req.params.imgId;
Product.findById(productId)
.then(product => {
if (!product) {
return res.status(500).json({ message: "Product not found" });
} else {
product.images.pull(imgId);
product.save()
.then(response => {
return res.status(200).json( { message: 'Image deleted'} );
})
}
})
.catch(err => {
console.log(err);
});
};
I think what they were saying though was it should rather be done something like this (an example I found after a google)
users.findByIdAndUpdate(userID,
{$pull: {friends: friend}},
{safe: true, upsert: true},
function(err, doc) {
if(err){
console.log(err);
}else{
//do stuff
}
}
);
The main difference is that when you use findById and save, you first get the object from MongoDB and then update whatever you want to and then save. This is ok when you don't need to worry about parallelism or multiple queries to the same object.
findByIdAndUpdate is atomic. When you execute this multiple times, MongoDB will take care of the parallelism for you. Folllowing your example, if two requests are made at the same time on the same object, passing { $pull: { friends: friendId } }, the result will be the expected: only one friend will be pulled from the array.
But let's say you've a counter on the object, like friendsTotal with starting value at 0. And you hit the endpoint that must increase the counter by one twice, for the same object.
If you use findById, then increase and then save, you'd have some problems because you are setting the whole value. So, you first get the object, increase to 1, and update. But the other request did the same. You'll end up with friendsTotal = 1.
With findByIdAndUpdate you could use { $inc: { friendsTotal: 1 } }. So, even if you execute this query twice, on the same time, on the same object, you would end up with friendsTotal = 2, because MongoDB use these update operators to better handle parallelism, data locking and more.
See more about $inc here: https://docs.mongodb.com/manual/reference/operator/update/inc/

How to update some data based on array value in Mongoose?

I'd like to update some data in Mongoose by using array value that I've find before.
Company.findById(id_company,function(err, company) {
if(err){
console.log(err);
return res.status(500).send({message: "Error, check the console. (Update Company)"});
}
const Students = company.students;
User.find({'_id':{"$in" : Students}},function(err, users) {
console.log(Students);
// WANTED QUERY : Update company = null from Users where _id = Students[];
});
});
Students returns users._id in array with object inside, and I use that to find users object, and then I want to set null a field inside users object, that field named as "company". How I can do that? Thank you.
From what you posted (I took the liberty to use Promises but you can roughly achieve the same thing with callbacks), you can do something like:
User.find({'_id':{"$in" : Students}})
.then( users =>{
return Promise.all( users.map( user => {
user.company = null;
return user.save()
}) );
})
.then( () => {
console.log("yay");
})
.catch( e => {
console.log("failed");
});
Basically, what I'm doing here is making sure .all() user models returned by the .find() call are saved properly, by checking the Promised value returned for .save()ing each of them.
If one of these fails for some reasons, Promise.all() return a rejection you can catch afterhand.
However, in this case, each item will be mapped to a query to your database which is not good. A better strategy would be to use Model.update(), which will achieve the same, in, intrinsically, less database queries.
User.update({
'_id': {"$in": Students}
}, {
'company': <Whatever you want>
})
.then()
use .update but make sure you pass option {multi: true} something like:
User.update = function (query, {company: null}, {multi: true}, function(err, result ) { ... });

Issue in saving values in array in mongoose model using save() [duplicate]

This question already has answers here:
Push items into mongo array via mongoose
(11 answers)
Closed 5 years ago.
I am trying to update a document in a collection. I am particularly trying to update an array of Strings.The following is my Schema
var sampleSchema= mongoose.Schema({
fullName : String,
email : String,
rolesTaken : [String],
});
I basically have an array of such schema's. I want to update the rolesTaken array for all of them.
So I did the following
var async = require('async');
var ATmapping = require('path to Schema');
ATmapping.find({},function(err,lists){
if(err){
console.log(err);
return;
}
if(lists.length > 0){
async.each(lists,function(list,callback){
if(!list.rolesTaken){
list.rolesTaken =[];
}
list.rolesTaken.push("Patient");
list.markModified('rolesTaken');
list.save(function(err,item){
if (err){
console.log(err);
}
console.log('Saved', item);
callback();
});
});
}
});
I have browsed a lot and the most popular solution was to add markModified. But it doesn't seem to work in my case.When I added mongoose.debug i got the following
atmappings.update({ _id: ObjectId("59074b127adeef0004b84ac3"), __v: 7 }, { '$set': { rolesTaken: [] }, '$inc': { __v: 1 } })
As you can see the roles taken is empty despite me adding markmodified and save(). I would like to know if I am missing something which is preventing me from saving the values in the schema.
Thanks in advance.
Here is how I push a comment to a thread in one of my apps using mongoose.
Thread.findByIdAndUpdate(body.thread, { $push: { comments: comment.id }})
I simply find the thread by the id, then use the $push operator to push the comment id to the comments attribute where I populate later.
You can read more into the $push operator here (mongoose v3.4) https://docs.mongodb.com/manual/reference/operator/update/push/

Find and delete a field in mongodb using mongoose [duplicate]

I'm using the Mongoose Library for accessing MongoDB with node.js
Is there a way to remove a key from a document? i.e. not just set the value to null, but remove it?
User.findOne({}, function(err, user){
//correctly sets the key to null... but it's still present in the document
user.key_to_delete = null;
// doesn't seem to have any effect
delete user.key_to_delete;
user.save();
});
In early versions, you would have needed to drop down the node-mongodb-native driver. Each model has a collection object that contains all the methods that node-mongodb-native offers. So you can do the action in question by this:
User.collection.update({_id: user._id}, {$unset: {field: 1 }});
Since version 2.0 you can do:
User.update({_id: user._id}, {$unset: {field: 1 }}, callback);
And since version 2.4, if you have an instance of a model already you can do:
doc.field = undefined;
doc.save(callback);
You'll want to do this:
User.findOne({}, function(err, user){
user.key_to_delete = undefined;
user.save();
});
I use mongoose and using any of the above functions did me the requirement. The function compiles error free but the field would still remain.
user.set('key_to_delete', undefined, {strict: false} );
did the trick for me.
At mongo syntax to delete some key you need do following:
{ $unset : { field : 1} }
Seems at Mongoose the same.
Edit
Check this example.
Try:
User.findOne({}, function(err, user){
// user.key_to_delete = null; X
`user.key_to_delete = undefined;`
delete user.key_to_delete;
user.save();
});
if you want to remove a key from collection try this method.
db.getCollection('myDatabaseTestCollectionName').update({"FieldToDelete": {$exists: true}}, {$unset:{"FieldToDelete":1}}, false, true);
Could this be a side problem like using
function (user)
instead of
function(err, user)
for the find's callback ? Just trying to help with this as I already had the case.
Mongoose document is NOT a plain javascript object and that's why you can't use delete operator.(Or unset from 'lodash' library).
Your options are to set doc.path = null || undefined or to use Document.toObject() method to turn mongoose doc to plain object and from there use it as usual.
Read more in mongoose api-ref:
http://mongoosejs.com/docs/api.html#document_Document-toObject
Example would look something like this:
User.findById(id, function(err, user) {
if (err) return next(err);
let userObject = user.toObject();
// userObject is plain object
});
the problem with all of these answers is that they work for one field. for example let's say i want delete all fields from my Document if they were an empty string "".
First you should check if field is empty string put it to $unset :
function unsetEmptyFields(updateData) {
const $unset = {};
Object.keys(updatedData).forEach((key) => {
if (!updatedData[key]) {
$unset[key] = 1;
delete updatedData[key];
}
});
updatedData.$unset = $unset;
if (isEmpty(updatedData.$unset)) { delete updatedData.$unset; }
return updatedData;
}
function updateUserModel(data){
const updatedData = UnsetEmptyFiled(data);
const Id = "";
User.findOneAndUpdate(
{ _id: Id },
updatedData, { new: true },
);
}
I believe that, if you desire remove a specific field into a collection, you should do this:
User.remove ({ key_to_delete: req.params.user.key_to_delete});
you can use
delete user._doc.key

How can I save multiple documents concurrently in Mongoose/Node.js?

At the moment I use save to add a single document. Suppose I have an array of documents that I wish to store as single objects. Is there a way of adding them all with a single function call and then getting a single callback when it is done? I could add all the documents individually but managing the callbacks to work out when everything is done would be problematic.
Mongoose does now support passing multiple document structures to Model.create. To quote their API example, it supports being passed either an array or a varargs list of objects with a callback at the end:
Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
if (err) // ...
});
Or
var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
Candy.create(array, function (err, jellybean, snickers) {
if (err) // ...
});
Edit: As many have noted, this does not perform a true bulk insert - it simply hides the complexity of calling save multiple times yourself. There are answers and comments below explaining how to use the actual Mongo driver to achieve a bulk insert in the interest of performance.
Mongoose 4.4 added a method called insertMany
Shortcut for validating an array of documents and inserting them into
MongoDB if they're all valid. This function is faster than .create()
because it only sends one operation to the server, rather than one for each
document.
Quoting vkarpov15 from issue #723:
The tradeoffs are that insertMany() doesn't trigger pre-save hooks, but it should have better performance because it only makes 1 round-trip to the database rather than 1 for each document.
The method's signature is identical to create:
Model.insertMany([ ... ], (err, docs) => {
...
})
Or, with promises:
Model.insertMany([ ... ]).then((docs) => {
...
}).catch((err) => {
...
})
Mongoose doesn't have bulk inserts implemented yet (see issue #723).
Since you know the number of documents you're saving, you could write something like this:
var total = docArray.length
, result = []
;
function saveAll(){
var doc = docArray.pop();
doc.save(function(err, saved){
if (err) throw err;//handle error
result.push(saved[0]);
if (--total) saveAll();
else // all saved here
})
}
saveAll();
This, of course, is a stop-gap solution and I would recommend using some kind of flow-control library (I use q and it's awesome).
Bulk inserts in Mongoose can be done with .insert() unless you need to access middleware.
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
Use async parallel and your code will look like this:
async.parallel([obj1.save, obj2.save, obj3.save], callback);
Since the convention is the same in Mongoose as in async (err, callback) you don't need to wrap them in your own callbacks, just add your save calls in an array and you will get a callback when all is finished.
If you use mapLimit you can control how many documents you want to save in parallel. In this example we save 10 documents in parallell until all items are successfully saved.
async.mapLimit(myArray, 10, function(document, next){
document.save(next);
}, done);
I know this is an old question, but it worries me that there are no properly correct answers here. Most answers just talk about iterating through all the documents and saving each of them individually, which is a BAD idea if you have more than a few documents, and the process gets repeated for even one in many requests.
MongoDB specifically has a batchInsert() call for inserting multiple documents, and this should be used from the native mongodb driver. Mongoose is built on this driver, and it doesn't have support for batch inserts. It probably makes sense as it is supposed to be a Object document modelling tool for MongoDB.
Solution: Mongoose comes with the native MongoDB driver. You can use that driver by requiring it require('mongoose/node_modules/mongodb') (not too sure about this, but you can always install the mongodb npm again if it doesn't work, but I think it should) and then do a proper batchInsert
Newer versions of MongoDB support bulk operations:
var col = db.collection('people');
var batch = col.initializeUnorderedBulkOp();
batch.insert({name: "John"});
batch.insert({name: "Jane"});
batch.insert({name: "Jason"});
batch.insert({name: "Joanne"});
batch.execute(function(err, result) {
if (err) console.error(err);
console.log('Inserted ' + result.nInserted + ' row(s).');
}
Use insertMany function to insert many documents. This sends only one operation to the server and Mongoose validates all the documents before hitting the mongo server. By default Mongoose inserts item in the order they exist in the array. If you are ok with not maintaining any order then set ordered:false.
Important - Error handling:
When ordered:true validation and error handling happens in a group means if one fails everything will fail.
When ordered:false validation and error handling happens individually and operation will be continued. Error will be reported back in an array of errors.
Here is another way without using additional libraries (no error checking included)
function saveAll( callback ){
var count = 0;
docs.forEach(function(doc){
doc.save(function(err){
count++;
if( count == docs.length ){
callback();
}
});
});
}
You can use the promise returned by mongoose save, Promise in mongoose does not have all, but you can add the feature with this module.
Create a module that enhance mongoose promise with all.
var Promise = require("mongoose").Promise;
Promise.all = function(promises) {
var mainPromise = new Promise();
if (promises.length == 0) {
mainPromise.resolve(null, promises);
}
var pending = 0;
promises.forEach(function(p, i) {
pending++;
p.then(function(val) {
promises[i] = val;
if (--pending === 0) {
mainPromise.resolve(null, promises);
}
}, function(err) {
mainPromise.reject(err);
});
});
return mainPromise;
}
module.exports = Promise;
Then use it with mongoose:
var Promise = require('./promise')
...
var tasks = [];
for (var i=0; i < docs.length; i++) {
tasks.push(docs[i].save());
}
Promise.all(tasks)
.then(function(results) {
console.log(results);
}, function (err) {
console.log(err);
})
Add a file called mongoHelper.js
var MongoClient = require('mongodb').MongoClient;
MongoClient.saveAny = function(data, collection, callback)
{
if(data instanceof Array)
{
saveRecords(data,collection, callback);
}
else
{
saveRecord(data,collection, callback);
}
}
function saveRecord(data, collection, callback)
{
collection.save
(
data,
{w:1},
function(err, result)
{
if(err)
throw new Error(err);
callback(result);
}
);
}
function saveRecords(data, collection, callback)
{
save
(
data,
collection,
callback
);
}
function save(data, collection, callback)
{
collection.save
(
data.pop(),
{w:1},
function(err, result)
{
if(err)
{
throw new Error(err);
}
if(data.length > 0)
save(data, collection, callback);
else
callback(result);
}
);
}
module.exports = MongoClient;
Then in your code change you requires to
var MongoClient = require("./mongoHelper.js");
Then when it is time to save call (after you have connected and retrieved the collection)
MongoClient.saveAny(data, collection, function(){db.close();});
You can change the error handling to suit your needs, pass back the error in the callback etc.
This is an old question, but it came up first for me in google results when searching "mongoose insert array of documents".
There are two options model.create() [mongoose] and model.collection.insert() [mongodb] which you can use. View a more thorough discussion here of the pros/cons of each option:
Mongoose (mongodb) batch insert?
Here is an example of using MongoDB's Model.collection.insert() directly in Mongoose. Please note that if you don't have so many documents, say less than 100 documents, you don't need to use MongoDB's bulk operation (see this).
MongoDB also supports bulk insert through passing an array of
documents to the db.collection.insert() method.
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
email : { type: String, index: { unique: true } },
name : String
});
var User = mongoose.model('User', userSchema);
function saveUsers(users) {
User.collection.insert(users, function callback(error, insertedDocs) {
// Here I use KrisKowal's Q (https://github.com/kriskowal/q) to return a promise,
// so that the caller of this function can act upon its success or failure
if (!error)
return Q.resolve(insertedDocs);
else
return Q.reject({ error: error });
});
}
var users = [{email: 'foo#bar.com', name: 'foo'}, {email: 'baz#bar.com', name: 'baz'}];
saveUsers(users).then(function() {
// handle success case here
})
.fail(function(error) {
// handle error case here
});

Resources