MongoDB two same records - node.js

I'm doing a project which is backed by Nodejs and MongoDB. I am quite new to MongoDB and I am lacking a clue why I quite often(almost always) get two same records in collections when I do posts. The two records only differ by ID, which are for example ObjectId("53aefb0fc68a0810504d2066") and 53aefb0fc68a0810504d2066, is this normal or am I doing something wrong? Thanks for any pointers.
Here is some node code:
server.js:
app.post("/:collection", function (req, res) {
var object = req.body;
var collection = req.params.collection;
collectionDriver.save(collection, object, function (err, docs) {
if (err) {
res.send(400, [err, object]);
} else {
res.send(201, docs);
}
});
});
collectionDriver:
save: function(collectionName, obj, callback) {
this.getCollection(collectionName, function(error, the_collection) {
if( error ) callback(error);
else {
obj.created_at = new Date();
the_collection.insert(obj, function() {
callback(null, obj);
});
}
});
},
getCollection: function(collectionName, callback) {
this.db.collection(collectionName, function(error, data) {
if (error) {
callback(error);
} else {
callback(null, data);
}
});
},

Everytime you ask MongoDB to save an object without an _id field, it automatically generates a new, globally unique ObjectID for that field and saves the object under that ObjectID.
When you want to use save to update an existing document, you need to make sure that the _id field is populated with the _id of the document you want to update.
Alternatively, you can create an unique index on those fields you consider relevant for determining what's a duplicate and what isn't. However, in that case an attempt to save an already existing document will throw an error instead of replacing the document.

Related

node.js and mongodb increment _id sequentially in existing database

Good morning everyone.
So I'm doing a class assignment wherein I'm connecting to an existing mongoDB database (via a node.js server), and I'm adding a bunch of data to it from a form on an HTML site. I've got the form information adding correctly, but there's two fields server side that aren't responding to new data. There is the primary key (_id) which instead of incrementing from the current last number, get's incremented by ObjectId('longnumericvale'), and CustomerID which get's populated by an undefined value. The last record existing in the database has both _id and CustomerId as the same number. How can I get node.js to look at this number and auto increment it when it's adding my form data as a new document in the mongodb DB. Code below:
app.post("/orderform", (req, res) => { //This takes post action from html and stores it
console.log("successfully posted")
var item = {
CustFirstName: req.body.CustFirstName,
CustLastName: req.body.CustLastName,
CustAddress: req.body.CustAddress,
CustCity: req.body.CustCity,
CustProv: req.body.CustProv,
CustPostal: req.body.CustPostal,
CustCountry: req.body.CustCountry,
CustHomePhone: req.body.CustHomePhone,
CustBusPhone: req.body.CustBusPhone,
CustEmail: req.body.CustEmail,
userid: req.body.userid,
passwd: req.body.passwd,
};
console.log("Customer information stored as:");//this is just a debug to the console to make sure it's grabbing the info
console.log(item);
//here we connect to the datbase, and insert every item above to its corresponding position in the "customers" collection of "travelexperts" database
mongo.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true },
(err, client) => {
if (err) throw err;
//select database
const db = client.db("travelexperts");
//grab a collection
const collection = db.collection("customers");
collection.insertOne(item, (err, res) => {
if (err) throw err;
console.log("Items inserted");
db.close;
});
});//endconnection
res.redirect('/thanks');
console.log("finished, redirected")
});
//render thank you page for after submitting order
app.get('/thanks', (req, res) =>{
res.render("thanks");
});
I've read the documentation for mongodb on autoincrementing, but it's a bit beyond me how to implement it in my specific example.
UPDATE
So maybe I didn't explain the problem really well. I'm trying to log into two separate collections now, and I have that working. with this code below:
app.post("/orderform", (req, res) => { //This takes post action from Order.html form and stores all form inputs with corresponding names in a variable called item
console.log("successfully posted")
var item = {
CustFirstName: req.body.CustFirstName,
CustLastName: req.body.CustLastName,
CustAddress: req.body.CustAddress,
CustCity: req.body.CustCity,
CustProv: req.body.CustProv,
CustPostal: req.body.CustPostal,
CustCountry: req.body.CustCountry,
CustHomePhone: req.body.CustHomePhone,
CustBusPhone: req.body.CustBusPhone,
CustEmail: req.body.CustEmail,
userid: req.body.userid,
passwd: req.body.passwd,
};
console.log("This should be an array of all entered customer info:");//this is just a debug to the console to make sure it's grabbing the info
console.log(item);
var book = {
BookingId: "",
BookingDate: new Date(),
BookingNo: "",
TravelerCount:"",
CustomerId: "",
TripTypeId:"",
PackageId: Number(req.query.packId),
};
console.log(book);
//here we connect to the datbase, and insert every item/booking above to its corresponding position in the "customers" collection of "travelexperts" database
mongo.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true },
(err, client) => {
if (err) throw err;
const db = client.db("travelexperts");
const collection = db.collection("customers");
const booking = db.collection("bookings");
collection.insertOne(item, (err, res) => {
if (err) throw err;
console.log("Items inserted");
});
booking.insertOne(book, (err, res) => {
if (err) throw err;
console.log("Items inserted");
});
db.close;
});//endconnection
res.redirect('/thanks');
console.log("finished, redirected")
});
So the issue is that for both collections and bookings, they have incomplete records. collections is 26 documents long, and the last _id is 144. So that's why I need to read that _id and increment it +1 for each new record. There's an _id field for each collection and then a customerId and BookinbId that should match it. Really appreciate all the help so far, but neither of these solutions seems to work. I tried both just before the collections.insertone line, and i tried console logging them and they just return [object Object] in the console instead of the _id value.
To insert a custom ID field for your documents, you should append the _id field with your item object.
The simplest wayc can be to create a global id_counter variable that you increment everytime you execute the /orderform API.
let idCounter = 1;
.
.
.
const itemDoc = {_id: idCounter++, ...item}
collection.insertOne(itemDoc, (err, res) => {
if (err) throw err;
console.log("Items inserted");
db.close;
});
.
.
.
You could also fetch the last inserted Item's id and simply increment it and it could save a lot of effort to keep the ids of the documents in sync incase the server restarts or something.
In that case, you could do something as below using collection.countDocuments() method:
let docLength = 0 // global variable
.
.
.
// inside your api endpoint controller...
collection.countDocuments({}, {}, (err, res)=> {
docLength = res;
})
const itemDoc = {_id: docLength++, ...item}
collection.insertOne(itemDoc, (err, res) => {
if (err) throw err;
console.log("Items inserted");
db.close;
});

Update fields of Mongoose document object from request body

The question(Mongoose save all parameters from request body) shows how to create new mongoose document object with request body in a line of code.
Extending this question, I want to know how to set all fields of mongoose document object at once.
router.route('/car')
.put(function(req, res, next) {
Car.findById(req.params.id, function (err, car) {
if (err) {
res.status(400).send(err);
} else {
if (!car)
return next(new Error('Failed to load car: ' + req.params.id));
car.name = req.body.name; // Code refactoring required
car.seats = req.body.seats;
car.engine_size = req.body.engine_size;
car.save(function(err) {
if(err)
res.status(400).send(err);
else
res.json(car);
});
}
});
});
In creating document var car = new Car(req.body); is perfect way to fill fields from request body.
For updating exist mongoose document, is there better way than below lines:
car.name = req.body.name;
car.seats = req.body.seats;
car.engine_size = req.body.engine_size;
You should use update instead of find.
router.route('/car')
.put(function(req, res, next) {
Car.update({ _id: mongoose.Types.ObjectId(req.params.id) },req.body)
.then(function (success) {
res.json();
})
.catch(function (error) {
res.status(404).send(err);
});
});
You can use Document.set().
car.set(req.body)
try {
await car.save()
}catch(err){
console.error(e)
}
For updating exist mongoose document, Is there better way than below lines?
For the better utilization of network data and performance, You should keep in mind that, update() method updates values in the existing document while the save() method replaces the existing document with the document passed in save() method.
So if your model is large and you want to update only few number of fields then update is better option.
Hope this helps.

Unable to delete a document with passed "email" and "_id" anyhow

I wanted to delete a document with concerned _id and email when I click on "Remove task" in the HTML file.
Following is the code which removes that task:
I've passed value of email and _id(only hexadcemial string value) to the code:
collection.findOneAndDelete({email:email,_id:taskid},function (err, result) {
if (err) {
console.log(err);
} else {
console.log("Removed!");
console.log(result);
callback(result);
}
db.close();
});
But, the function is not recognizing _id that I've passed. The value of "taskid" variable is 566836bd8db43d3d56e23a4a i.e. only strings value from _id:
ObjectId("566836bd8db43d3d56e23a4a")
var taskid=566836bd8db43d3d56e23a4a;
I've tried every possible declaration of taskid to convert it so that the function could recognize the value of _id and match it:
var taskid= "ObjectId("+'"'+req.param('taskid')+'"'+")";
But till now, I am not able to match the _id with the taskid. Any fix?
if you are going to compare with ObjectId then
var ObjectId = require('mongoose').Types.ObjectId
collection.findOneAndDelete({email:email,_id:new ObjectId(taskid)},function (err, result) {
if (err) {
console.log(err);
} else {
console.log("Removed!");
console.log(result);
callback(result);
}
db.close();
});
Should work for you.
If you feel the job too hard for each and every query then you can create an new method.
String.prototype.toObjectId = function() {
var ObjectId = (require('mongoose').Types.ObjectId);
return new ObjectId(this.toString());
};
// Every String can be casted in ObjectId now
console.log('545f489dea12346454ae793b'.toObjectId());

How do I update a doc in Cloudant using Cloudant Node.js

So, what I'm doing should be really simple, and maybe it is and I'm just doing something wrong. I want to update an existing document in my database but I'm having some issues, can someone please advise?
Nano's Documentation states the following for insert:
db.insert(doc, [params], [callback])
Therefore, I should surely be able to do the following:
var user = {
'firstname' : 'my',
'secondname' : 'name'
};
db.insert(user, {_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
However, whenever I try this, it creates an entirely new document. If I do the following then it will indeed update my document, but of course, with nothing in this document other than the _rev:
db.insert({_rev: '2-cc5825485a9b2f66d79b8a849e162g2f'}, function(err, body) {});
So the question is, how do I pass in my object and get it to update, rather than creating a new record?
var user = {
'firstname' : 'my',
'secondname' : 'name',
'_id': <id from prev object>,
'_rev': <rev from prev object>
};
db.insert(user, function(err, body) {});
the _id and _rev are both required in order for the update to work.
they should be in the object that you are sending also.
The first argument in the db.insert(...) command is the document which you want to create/update. If you pass in a doc with a ._rev attribute, then it will replace the document with that same _rev in Cloudant with the doc passed in as the first argument of your db.insert(...). If the doc does not include a ._rev attribute, then Cloudant will create an entirely new document.
This explains the behavior you were experiencing in both the scenarios you tried. In order to make an update to your doc, make sure to include ._id and ._rev attributes, along with the rest of your doc's attributes when you use it as the first argument to your db.insert(...) function.
Got it! Here's the solution:
db.get('user', { revs_info: true }, function(err, doc) {
if (!err) {
console.log(doc);
doc.firstname = 'my';
doc.secondname = 'name';
db.insert(doc, doc.id, function(err, doc) {
if(err) {
console.log('Error inserting data\n'+err);
return 500;
}
return 200;
});
}
});
First get the record id and rev id (_id,_rev)
const newData={email:"aftabfalak956#gmail.com",name:"Aftab Falak"}
cloudant.use("user").find({selector:{_id:"user_id"}}, (err, documents) => {
var revision = documents.docs[0]._rev;
const data={...documents.docs[0],...newData};
cloudant.use("user").insert(data,{_rev:revision},function(err){
if (!err) {
console.log('success', 'The record was updated successfully');
}
else {
console.log('failure', err);
}
});
});

Mongoose $addToSet return new list entrys

I have a question working with mongoose 4.0.1
I am trying to add new picture objects to an array inside a model. This is the code of my endpoint that is actually doing the job:
// Add new images
exports.pictures = function(req, res) {
Incident.findByIdAndUpdate(
req.params.id,
{$addToSet: {"pictures": {$each: req.body}}},
{new: true},
function(err, incident) {
if (err) { return handleError(res, err); }
return res.send(201).json(incident.pictures);
}
);
};
The problem: The callback object (incident) stores all information of the model which was found and updated. But I want to return only the new array entries which were created.
How can I receive the actual changes of the operation instead of the whole object that is storing the pictures array?
I solved the problem by creating a new schema for pictures and adding a reference to the incident model.
The endpoint changed as well:
Create new picture instances for a array of pictures
Find incident by id
Save the references of the picture instances to a array inside the incident
Return the id of the picture instances
var _ = require('lodash');
// Add new images
exports.pictures = function(req, res) {
Picture.create(req.body, function(err, pictures) {
if (err) { return handleError(res, err); }
Incident.findByIdAndUpdate(
req.params.id,
{$addToSet: {"pictures": {$each: pictures}}},
function(err) {
if (err) { return handleError(res, err); }
var pictureIds = _.map(pictures, '_id');
return res.status(201).json(pictureIds);
}
);
});
};

Resources