How to use promise, .then.. properly? - node.js

First of all, I'm sorry that I couldn't come up with a better title for this post. Also, I'm a beginner in nodejs. I have a problem for which I am hoping to find answer. Please help me and thank you so much.
I am trying to create a collection and insert data into it and retrieve the same data that has been inserted in the same code. This piece of code dbo.collection(nameofCollection).insertMany(data) inserts the data into the collection if exists already and if it doesn't exist, it will create the collection and then insert.
This is the code that I've written for this task :
var excel = require('excel4node');
const mongoose = require('mongoose');
const mongodb = require('mongodb')
const mdb = mongodb.MongoClient;
mdb.connect("mongodb://localhost:27017/", async function(err, db) {
if (err) throw err
const nameCollection = "tempCollection"
const dbo = db.db("reports")
const data = [
{
empID : "001ev",
empName : "xyz",
salary : 20000
},
{
empID : "00234",
empName : "abc",
salary : 10000
},
{
empID : "11345",
empName : "pqr",
salary : 15000
}
];
dbo.collection(nameCollection).insertMany(data, function(err, res) {
if (err) throw err;
console.log("Number of documents inserted: " + res.insertedCount);
console.log("first")
})
const records = await dbo.collection(nameCollection).find({}, { projection: { _id: 0} }).toArray()
console.log(records)
console.log("second")
})
Now, the issue that I'm facing with this code is that, when there is no collection in the data base, it is creating one and inserting the data. Up to that it is working fine. But, the thing is, find query is executing first and then insert is executing. And if that collection already exists then it is working fine, it is inserting the data and then find query is executing.
So, to put it short, if there is no collection with the name, nameCollection; find query is executed first, which returns []. Then the collection is created and insertion of data is happening. But, why is this happening, although in my code I wrote to create the collection first, insert data and then retrieve it?

This is because of asynchronous runtime. There is no guarantee that find function will be executed the last.
try adding this:
try {
const res = await dbo.collection(nameCollection).insertMany(data);
console.log("Number of documents inserted: " + res.insertedCount);
console.log("first");
const records = await dbo.collection(nameCollection).find({}, { projection: { _id: 0} }).toArray()
console.log(records)
console.log("second")
} catch (error) {
throw err;
}
In this case you can omit try/catch block since it does not have impact on error handling.

Related

Node.js and Mongoose. Reading from one collection and writing to a different one

I am trying to read from one Mongo collection and then take all that data and save it to a new collection. I am using the same schema, but changing the collection name in two different models :
UserInfo.js :
const mongoose = require('mongoose')
const userSchema = mongoose.Schema({
userID : String,
userName : String
})
let userInfo = mongoose.model('UserInfo', userSchema)
let backUpUserInfo = mongoose.model('BackUpUserInfo', userSchema)
module.exports = {
userInfo : userInfo,
backUpUserInfo : backUpUserInfo
}
I inserted data into the userinfos collection and then I am trying to read that and insert into the backupuserinfos collection :
backup.js :
const UserInfo = require('../Schema/UserInfo').userInfo;
const BackUpUserInfo = require('../Schema/UserInfo').backUpUserInfo;
async function backUp(){
UserInfo.find({}, async function(err1, userInfo){
if (err1) return console.log(err1)
for(let i in userInfo){
try{
let backUpUser = new BackUpUserInfo(userInfo[i])
await backUpUser.save(function (err2, user) {
if (err2) return console.error(err2)
console.log("collection updated")
})
}
catch(err){
console.log(err)
}
}
})
}
I am getting an error that seems to indicate that it is finding a duplicate when trying to save the information, even though the backup collection is empty. This makes me think it is trying to write to the user info collection instead of the back up one. I created the backUp model variable and that is what I run save on, which makes me think it should be specified to run to that collection. Am I doing something wrong here?
Here is the error :
VersionError: No matching document found for id
Easiest way in mongoose would be
UserInfo.aggregate([ { $match: {} }, { $out: "BackupUserInfo" } ])
Else you can use mongodb query OR mongodb copyTo()
db.myoriginal.aggregate([ { $match: {} }, { $out: "mycopy" } ])
//OR
db.source.copyTo("target");
But copyTo() is deprecated since version 3.0.

node.js db.collection.insertOne - MongoError: E11000 duplicate key error collection: _id_ dup

In node.js, from one javascript loop, I am trying to insert one json object into one mongodb collection but getting duplicate key error on _id column.
{ MongoError: E11000 duplicate key error collection: app.Tab2017index: id dup key: { : ObjectId('5cbc813227b2ca2864b3c66a') }
Here is my part of my javascript code, which is causing error.
const MongoClient = require('mongodb').MongoClient;
const assert = require('assert');
const url = 'mongodb://localhost:27017';
const dbName = 'app';
var jsonData = {};
MongoClient.connect(url,{useNewUrlParser: true}, function(err, client) {
assert.equal(null, err);
if(err) { return console.dir(err); }
const db = client.db(dbName);
const collection = db.collection('Tab2017')
for (var i = 0; i < 5; i++) {
jsonData["test"] = "line";
console.log('LINE_'+i+'- '+JSON.stringify(jsonData));
collection.insertOne(jsonData, (err, result) => {
if(err) { console.dir(err); }
console.log('mongodb insert done');
})
}
})
Above code is showing error on console,
D:\app\server\routes>node linmon.route-backup3.js
LINE_0- {"test":"line"}
LINE_1- {"test":"line","_id":"5cbc813227b2ca2864b3c66a"}
LINE_2- {"test":"line","_id":"5cbc813227b2ca2864b3c66a"}
LINE_3- {"test":"line","_id":"5cbc813227b2ca2864b3c66a"}
LINE_4- {"test":"line","_id":"5cbc813227b2ca2864b3c66a"}
mongodb insert done
-------------------------------------------
{ MongoError: E11000 duplicate key error collection: app.Tab2017 index: _id_ dup key: { : ObjectId('5cbc813227b2ca2864b3c66a') }
at Function.create (D:\app\server\node_modules\mongodb\node_modules\mongodb-core\lib\error.js:43:12)
at toError (D:\app\server\node_modules\mongodb\lib\utils.js:149:22)
at coll.s.topology.insert (D:\app\server\node_modules\mongodb\lib\operations\collection_ops.js:859:39)
at D:\app\server\node_modules\mongodb\node_modules\mongodb-core\lib\connection\pool.js:397:18
at process._tickCallback (internal/process/next_tick.js:61:11)
I am not inserting _id columns value and allowing it to be system generated. Here is the output of getindexes.
> db.Tab2017.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "app.Tab2017"
}
]
>
When i insert same object through mongo shell command line, it works without error. Looks like javascript does not wait for mongodb insert operation to complete.
I have solved the problem myself. Nodejs performs asynchronous operation so it completes the loop but keeps inserting into mongodb collection as well. I noticed that, first operation always gets successful and fails for subsequent inserts. Somehow mongodb generates "_id" value for first insert and keeps that same "_id" in json objects. That's why it throws duplicate error. I just added one line to remove "_id" from json object before mongo insert and it worked.
Here is my modified code.
for (var i = 0; i < 5; i++) {
jsonData["test"] = "line";
**delete jsonData["_id"];**
console.log('LINE_'+i+'- '+JSON.stringify(jsonData));
collection.insertOne(jsonData, (err, result) => {
if(err) { console.dir(err); }
console.log('mongodb insert done');
})
}
I had a similar issue. The problem was scope (as mentioned above in a comment by Neil Lunn).
I was inserting a document into a collection through a Netlify serverless function (Lambda function). I had no DB-Schema. First entry worked fine. All subsequent entries failed.
The code that was failing:
const mongodb = require('mongodb')
const MongoClient = mongodb.MongoClient
const myInitialDocument = { title: null }
function createClient (connectionString) {
const client = new MongoClient(
connectionString,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
return client
}
exports.handler = async function (event) {
const dbClient = createClient('connection_string_to_db')
let entryToDb
try {
await dbClient.connect()
entryToDb = await dbClient.db().collection('somecollection').insertOne(myInitialDocument)
} catch (err) {
console.log(err)
}
dbClient.close()
return {
body: JSON.stringify(entryToDb)
}
}
The problem above is that myInitialDocument gets mutated and after the initial call it includes the _id.
Updated code that worked (has no myInitialDocument const):
const mongodb = require('mongodb')
const MongoClient = mongodb.MongoClient
function createClient (connectionString) {
const client = new MongoClient(
connectionString,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
return client
}
exports.handler = async function (event) {
const dbClient = createClient('connection_string_to_db')
let entryToDb
try {
await dbClient.connect()
entryToDb = await dbClient.db().collection('somecollection').insertOne({ title: null })
} catch (err) {
console.log(err)
}
dbClient.close()
return {
body: JSON.stringify(entryToDb)
}
}
Just delete the _id field from the object before going to insert.
delete jsonData["_id"];
If you use mongoose just try with this.
jsonData["_id"] = new mongoose.Types.ObjectId();
Definitely this will solve your problem and you can maintain uniqueness by without droping indexes.
_id field in MongoDB is a unique key field. You cannot insert duplicate values to the _id column.
For example, you have a document with _id value equal to 1 exist in the collection and you are trying to insert one more document with same _id value(1) you will get the E11000 duplicate key error.
In your case, you are not generating a new _id value when inserting the second document which results in a duplicate error. Try creating a new _id for every insert or don't pass the _id value so that MongoDB backend will take care of this.
You might need to drop the index,then try to insert .
db.collection.dropIndex()

Mongoose findById returns null even with valid id

I have already seen the discussion about the following question with a similar title
mongoose 'findById' returns null with valid id
But my problem is not the database name since all my other connections with the same database in fact the queries on the same collection are working fine.
I am using mongoose 4.13.6, node js 6.11 and mongo 3.4.
It is a post request .
var query=req.body;
I am sending the search parameters as
var findFruit =
{
_id:query._id
}
When I print my findFruit I get :
_id:'5a1cf77920701c1f0aafb85e'
The controller function for this is :
Fruit.findById(findFruit._id,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
I even tried find
Fruit.find(findFruit,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
The collection for sure has the entry under this id .
I went through this git issue as well https://github.com/Automattic/mongoose/issues/3079
Unfortunately I cannot downgrade mongoose as it might affect multiple other working functions.
Edit :
I tried creating ObjectId like :
var mongoose = require('mongoose');
var ObjectID = require('mongodb').ObjectID;
var objectId = new ObjectID();
// Convert the object id to a hex string
var originalHex = objectId.toHexString();
// Create a new ObjectID using the createFromHexString function
var newObjectId = new ObjectID.createFromHexString(query._id);
The model file :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var FruitSchema = new Schema({
name : {type : String, unique : true},
color : {type : String}
});
module.exports = mongoose.model('Fruit', FruitSchema);
All my findById("id") calls returned null.
When looking at the collection with Compass I realized that all my _id elements were Strings. Note that the entire collection was imported.
I created a new element in Compass and the _id was created as ObjectId! and when I called findById("id") with that element's id it worked!
My conclusion is that there is obviously a bug with import. I have not found a way to convert the string _id fields to ObjectId's in the actual collection.
All my findById("id") calls returned null, when _id elements are Strings.
In the first place:
Check your mongodb database, if _id is stored as String, findById(id) can not find since it identifies ObjectId. If you've used import database by using mongoimport command and including _id in JSON:
Solution 1:
modify your JSON and for each document, change _id for instance:
_id: "5a68fde3f09ad7646ddec17e" to the following and run mongoimport again:
"_id": { "$oid": "5a68fde3f09ad7646ddec17e" }
Solution 2:
delete _id in the JSON file, drop collection and import again. Mongo will auto-create _id.
After any of solutions above, findById("id") will work.
Secondly:
Specifically in such cases where your _id elements are string, might be a better idea to use mongodb package: npm i mongodb
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("your_db_name");
dbo.collection("fruits_collection")
.find({_id:'5a1cf77920701c1f0aafb85e'})
//.find({_id:'5a1cf77920701c1f0aafb85e'}, { projection: { _id: 0, name: 1, color: 1} }) // to select specific columns desired
.toArray(function (err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
The above simple code, assumed you manage error handling yourself, either through try-catch, or
sending 404 as status code, or redirect to error page template, depending on whether the code is embedded in the Express route handler or not.
Hope this helped .. :)
Still trying to figure out why findById didn't work for me but the following piece of code did it
var mongoose = require('mongoose');
var newObjectId=new mongoose.Types.ObjectId(query._id);
var params={
'_id':newObjectId
}
Fruit.find(params).exec(function (err,fruit) {
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
#Shoom. Yes, worked for me, thanks. findById() expects ObjectID, not a String.
I did not have a constraint to create documents with a specific id, so I imported with no _id. The db had newly-assigned _id as ObjectID.
findById(id), (and updateOne({ _id: id }, ...), started working as expected.

Object #<Promise> has no method 'limit'

when I run code
var collection = db.get('categories');
console.log(collection.find().limit(1).sort( { _id : -1 } ));
on nodejs using mongodb I am getting error Object # has no method 'limit' . I am a beginner to node and really stuck on this section of node
here is full code for geting last insert document.
router.post('/addcategory', function(req, res) {
// Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var name = req.body.name;
var description = req.body.description;
// Set our collection
var collection = db.get('categories');
// Submit to the DB
collection.insert({
"name" : name,
"description" : description,
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
// And forward to success page
/******************/
console.log(collection.find().limit(1).sort( { _id : -1 } ));
/*************/
}
});
});
The key piece of missing information here was that you are using Monk, not the native MongoDB Node.JS driver. The command you have for find() is how you would use the native driver (with the changes suggested by #BlakesSeven above for asynchronity), but Monk works a little bit differently.
Try this instead:
collection.find({}, { limit : 1, sort : { _id : -1 } }, function (err,res) {
console.log(res);
});
The method is still asynchronous so you still need to invoke itm either as a promise with .then() or a callback. No methods are sychronous and return results in-line.
Also the result returned from the driver is s "Cursor" and not the object(s) you expect. You either iterate the returned cursor or just use .toArray() or similar to convert:
collection.find().limit(1).sort({ "_id": -1 }).toArray().then(function(docs) {
console.log(docs[0]);
});
Or:
collection.find().limit(1).sort({ "_id": -1 }).toArray(function(err,docs) {
console.log(docs[0]);
});
But really the whole premise is not correct. You seem to basically want to return what you just inserted. Event with the correction in your code, the returned document is not necessarily the one you just inserted, but rather the last one inserted into the collection, which could have occurred from another operation or call to this route from another source.
If you want what you inserted back then rather call the .insertOne() method and inspect the result:
collection.insertOne({ "name": name, "description": description },function(err,result) {
if (err) {
res.send("There was an error");
} else {
console.log(result.ops)
}
});
The .insert() method is considered deprecated, but basically returns the same thing. The consideration is that they return a insertWriteOpResult object where the ops property contains the document(s) inserted and their _id value(s),

NodeJS + MongoDB: Getting data from collection with findOne ()

I have a collection "companies" with several objects. Every object has "_id" parameter. I'm trying to get this parameter from db:
app.get('/companies/:id',function(req,res){
db.collection("companies",function(err,collection){
console.log(req.params.id);
collection.findOne({_id: req.params.id},function(err, doc) {
if (doc){
console.log(doc._id);
} else {
console.log('no data for this company');
}
});
});
});
So, I request companies/4fcfd7f246e1464d05000001 (4fcfd7f246e1464d05000001 is _id-parma of a object I need) and findOne returns nothing, that' why console.log('no data for this company'); executes.
I'm absolutely sure that I have an object with _id="4fcfd7f246e1464d05000001". What I'm doing wrong? Thanks!
However, I've just noticed that id is not a typical string field. That's what mViewer shows:
"_id": {
"$oid": "4fcfd7f246e1464d05000001"
},
Seems to be strange a bit...
You need to construct the ObjectID and not pass it in as a string. Something like this should work:
var BSON = require('mongodb').BSONPure;
var obj_id = BSON.ObjectID.createFromHexString("4fcfd7f246e1464d05000001");
Then, try using that in your find/findOne.
Edit: As pointed out by Ohad in the comments (thanks Ohad!), you can also use:
new require('mongodb').ObjectID(req.params.id)
Instead of createFromHexString as outlined above.
That's because _id field in mongo isn't of string type (as your req.params.id). As suggested in other answers, you should explicitly convert it.
Try mongoskin, you could use it like node-mongodb-native driver, but with some sugar. For example:
// connect easier
var db = require('mongoskin').mongo.db('localhost:27017/testdb?auto_reconnect');
// collections
var companies = db.collection('companies');
// create object IDs
var oid = db.companies.id(req.params.id);
// some nice functions…
companies.findById();
//… and bindings
db.bind('companies', {
top10: function(callback) {
this.find({}, {limit: 10, sort: [['rating', -1]]).toArray(callback);
}
});
db.companies.top10(printTop10);
You can use findById() which will take care of the id conversion for you.
company = Company.findById(req.params.id, function(err, company) {
//////////
});
In case these didn't work for you, this worked for me for accessing a blog post:
const getSinglePost = async (req, res) => {
let id = req.params.id;
var ObjectId = require('mongodb').ObjectId;
const db = await client.db('CMS');
const data = await db.collection("posts").findOne({ _id: ObjectId(id) })
if (data) {
res.status(200).send(data)
} else res.status(400).send({ message: "no post found" })
}

Resources