Mongodb upsert command in nodejs - node.js

I have created a collection named Account.
At first shot, I am inserting around 5 records.
{"Id":"0012800000C6Q19AAF","Name":"Aditya Kumar"}
{"Id":"0012800000C6MldAAF","Name":"GenePoint"}
{"Id":"0012800000C6MlbAAF","Name":"United Oil & Gas, UK"}
{"Id":"0012800000C6MlcAAF","Name":"United Oil & Gas, Singapore"}
{"Id":"0012800000C6MlTAAV","Name":"Edge Communications"}
{"Id":"0012800000C6MlUAAV","Name":"Burlington Textiles Corp of America"}
I am bringing data from the external system, which has already an ID field associated with it.
Again next time when a request comes for inserting extra 5 records, I want to check if Id field of upcoming records already exists in my MongoDB(if yes, then do an update else insert).
And don't get confused by Id field coming from external system with _Id field created for every MongoDB document.
Here is what I have tried:
var x= fields["data"]; obj='Account';
db.createCollection(obj);
db.collection(obj).insert(fields, function(err, doc) {
if (err) {
handleError(res, err.message, "Failed to create new contact.");
} else {
res.status(201).json(doc.ops[0]);
}
})

Use the updateOne method:
db.collection.updateOne(
{ _id: id },
{ $set: { name: 'name' } },
{ upsert: true }
);
If a record with the matching id is found then the name field will be updated, otherwise a new record will be created.

Related

I get a "($) prefixed field '$numberDecimal' in 'price.$numberDecimal' is not valid for storage." error when trying to update a field on my back-end

lately I've been trying to update a price field in my mongodb database but I'm facing an annoying issue.
The collection I'm trying to modify is listingsandReviews, within the database sample_airbnb.
The field in question is made as such price {$numberDecimal: priceVar}.
On my back end (using Express.js DAO architecture), my update query looks as such:
// console.log(roomContent.amenities)
let price = roomContent.price.$numberDecimal
try {
const roomIdResponse = await rooms.updateOne(
{ _id: roomId },
{ $set: {
name: roomContent.name,
address: {street: roomContent.address.street},
price: {'$numberDecimal': price},
property_type: roomContent.property_type,
bedrooms: roomContent.bedrooms,
beds: roomContent.beds,
minimum_nights: roomContent.minimum_nights,
maximum_nights: roomContent.maximum_nights,
amenities: roomContent.amenities,
description: roomContent.description
}
},
{ upsert: true }
);
return roomIdResponse;
}
catch (e) {
console.error(`Unable to update room: ${e}`);
return { error: e };
}
}
the shell gives me back that error Unable to update room: MongoServerError: The dollar ($) prefixed field '$numberDecimal' in 'price.$numberDecimal' is not valid for storage.
I've been looking at every resource possible, some saying I should try NumberDecimal(), others decimal128, but nothing works so far.
You could say I could fix the issue on the front-end, but that wouldn't make the database structure consistent, which I want to avoid.

How to update the childs value of an object present in the mongod using mongoose with node.js?

i want to update particular child's value which is actually present inside the object and wants all my rest value be the same.
i want to use mongoose npm package.
for example:
basic:{
name: "ABC",
mobile: 1234567890,
age: 20
},
other:{
pincode: 123456,
email: "abc#abc.com"
}
My Code
Model.findOneAndUpdate(
{ "basic.mobile": 1234567890 },
{
basic:{ name: "CDE"}
},
(err, doc) => {
if (err) return res.send({ error: err });
res.send(`updated`);
}
);
it works but it override the previous basic data and create a new one which i provided. it should update but also rest value should be there.
how can i do this?
Your question contains the answer. For Filter you are referring a single field using dot operator properly that is
"basic.mobile": 1234567890
You should do similar thing for update as well, that is the second argument of your function call, the way you have defined is wrong becuase it is a new object - that should be used when you want replace whole basic object in DB. If you want to just update the name field then you should use dot operator like below.
"basic.name":"CDE"
So the whole function would look like below.
Model.findOneAndUpdate(
{ "basic.mobile": 1234567890 },
{ "basic.name": "CDE"},
(err, doc) => {
if (err) return res.send({ error: err });
res.send(`updated`);
}
);

NodeJS: Insert record in dynamodb if not exist

I need to store user's info in DynamoDB and send a mail to the same user if it doesn't already exist in DynamoDB table. I am doing this in for loop. The list contains only 2 records. The issue is only the second record gets inserted in table and the mail is sent twice to the same user. Here is the code:
module.exports.AddUser = function(req, res, usersList, departmentId) {
var _emailId = "";
var _userName = "";
var _departmentId = departmentId;
for (var i = 0; i < usersList.length; i++) {
_emailId = usersList[i].emailId;
_userName = usersList[i].userName;
var params = {
TableName: "UsersTable",
Key: {
"emailId": _emailId,
"departmentId": _departmentId
}
};
docClient.get(params, function(err, data) {
if (!err) {
if (!data.items)
AddUserAndSendEmail("UsersTable", _emailId, _userName);
//The above function is being called twice but for the same user.
//It has a check so not inserting the same record twice but
//sending two mails to the same user.
}
});
}
res.end("success");
}
function AddUserAndSendEmail(tableName, emailId, _userName) {
var params = {
TableName: tableName,
Item: {
"emailId": emailId,
"departmentId": 101//Default Department
}
};
docClient.put(params, function(err, data) {
if (!err) {
//Send Email Code Here
} else {
console.log("error");
}
});
}
What could be the reason for this strange behavior? Really frustrated, I am about to give up on this.
1) Please note that DynamoDB is eventually consistent. If you insert the item and check whether the item exists immediately, it may not always find the item in the database.
This means the second iteration of the loop may not always find the first item inserted into the table.
2) If the item already exists in the table, the Put api will update the item and give successful response.
This means the Put will be successful for the same email id and department id in the second iteration because it updates the record if it is already present.
GetItem – The GetItem operation returns a set of Attributes for an
item that matches the primary key. The GetItem operation provides an
eventually consistent read by default. If eventually consistent reads
are not acceptable for your application, use ConsistentRead.
PutItem – Creates a new item, or replaces an old item with a new item
(including all the attributes). If an item already exists in the
specified table with the same primary key, the new item completely
replaces the existing item. You can also use conditional operators to
replace an item only if its attribute values match certain conditions,
or to insert a new item only if that item doesn’t already exist.
Based on the above points, there is a possibility to get two emails if you have same email id and department id in the array.

How to update a field using its previous value in MongoDB/Mongoose

For example, I have some documents that look like this:
{
id: 1
name: "foo"
}
And I want to append another string to the current name field value.
I tried the following using Mongoose, but it didn't work:
Model.findOneAndUpdate({ id: 1 }, { $set: { name: +"bar" } }, ...);
Edit:
From Compatibility Changes in MongoDB 3.6:
MongoDB 3.6.1 deprecates the snapshot query option.
For MMAPv1, use hint() on the { _id: 1} index instead to prevent a cursor from returning a document more than once if an intervening write operation results in a move of the document.
For other storage engines, use hint() with { $natural : 1 } instead.
Original 2017 answer:
You can't refer to the values of the document you want to update, so you will need one query to retrieve the document and another one to update it. It looks like there's a feature request for that in OPEN state since 2016.
If you have a collection with documents that look like:
{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name" : "bar" }
Using the MongoDB shell, you can do something like this:
db.test.find({ name: "bar" }).snapshot().forEach((doc) => {
doc.name = "foo-" + doc.name;
db.test.save(doc);
});
The document will be updated as expected:
{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name": "foo-bar" }
Note the .snapshot() call.
This ensures that the query will not return a document multiple times because an intervening write operation moves it due to the growth in document size.
Applying this to your Mongoose example, as explained in this official example:
Cat.findById(1, (err, cat) => {
if (err) return handleError(err);
cat.name = cat.name + "bar";
cat.save((err, updatedCat) => {
if (err) return handleError(err);
...
});
});
It's worth mentioning that there's a $concat operator in the aggregation framework, but unfortunately you can't use that in an update query.
Anyway, depending on what you need to do, you can use that together with the $out operator to save the results of the aggregation to a new collection.
With that same example, you will do:
db.test.aggregate([{
$match: { name: "bar" }
}, {
$project: { name: { $concat: ["foo", "-", "$name"] }}
}, {
$out: "prefixedTest"
}]);
And a new collection prefixedTest will be created with documents that look like:
{ "_id" : ObjectId("XXX"), "name": "foo-bar" }
Just as a reference, there's another interesting question about this same topic with a few answers worth reading: Update MongoDB field using value of another field
If this is still relevant, I have a solution for MongoDB 4.2.
I had the same problem where "projectDeadline" fields of my "project" documents were Array type (["2020","12","1"])
Using Robo3T, I connected to my MongoDB Atlas DB using SRV link. Then executed the following code and it worked for me.
Initial document:
{
_id : 'kjnolqnw.KANSasdasd',
someKey : 'someValue',
projectDeadline : ['2020','12','1']
}
CLI Command:
db
.getCollection('mainData')
.find({projectDeadline: {$not: {$eq: "noDeadline"}}})
.forEach((doc) => {
var deadline = doc.projectDeadline;
var deadlineDate = new Date(deadline);
db
.mainData
.updateOne({
_id: doc._id},
{"$set":
{"projectDeadline": deadlineDate}
}
)}
);
Resulting document:
{
_id : 'kjnolqnw.KANSasdasd',
someKey : 'someValue',
projectDeadline : '2020-12-01 21:00:00.000Z'
}

Mongodb duplicate errorr

db.collection("resource").update({name: name}, {
name: name,
type: type
}, {
upsert: true
}
I differentiate documents by their names. I do not add document if it exists with the same. But I want to warn user by saying "It already exists, operation failed" How can I achieve it?
It sounds like you want to insert documents, not update or insert documents.
1: Add unique index on resource.name ahead of time.
db.resouces.createIndex({ name: 1 }, { unique: true })
Important: do this once, not on every request.
See mongodb create index docs.
2: Use insert instead of update + upsert.
It sounds like you want to actually insert a document, and get an error if there is a duplicate key.
db.resources.insert({ name: "AJ" }) // ok
db.resources.insert({ name: "AJ" }) // error!
You will get a duplicate key error on the second insert. Error code 11000.
See mongodb docs on insert.
3: Use promise-try-catch in javascript.
The code to do error checking looks like:
var db = require("mongojs")(DATABASE_URL, [ "resources" ])
var duplicateKey = function (err) {
return err.code == "11000"
}
db.resources.insert({ name: name })
.then(function () {
// success!
})
.catch(duplicateKey, function () {
// sorry! name is taken
})

Resources