Upsert is not updating existing document - javascript

Mongo creates a new document if it doesn't already exist (must have the id of 1). And if it already exists, it should increment all values by 1. But the problem here is that it adds another document every time, instead of updating the already existing one.
const query = {
id: 1,
customer: {
worth: 0
}
},
update = { id: 1, $inc:
{
'customer.worth': 1
}
},
options = { upsert: true };
Model.findOneAndUpdate(query, update, options)
.catch(err => console.log(err));
How can I just increment the value of 1 if document already exists, instead of adding another document every time? I only want to have 1 document at any given time.

your query seems ok and inserts during update caused by upsert flag
Optional. If set to true, creates a new document when no document
matches the query criteria. The default value is false, which does not
insert a new document when no match is found.
read documentation
the following query worked for me
db.getCollection('test').findOneAndUpdate({
"id" : 1,
"customer" : {
"worth" : 0
}
}, {
$inc: {
'customer.worth': 1
}
})

Related

silence the updatedAt field with Sequelize

I would like to disable or silence the updatedAt field when I update a record. As per sequelize docs looks like this option was available on v5.9.2.
my sequelize version: ^5.22.5
I've tried the following however it performs the update but still proceeds to update the updatedAt field in the DB.
#1
model.update(
{
count: Number(model.count) + 2,
},
{
where: {
id: 10
},
silent: true
}
);
#2
model.increment('count', { by: 2, where: { id: 10 }, silent: true });
#3
model.save ({ silent: true })
silent is the app-level functionalities not to update the updatedAt column, however, if you have ON UPDATE CURRENT_TIMESTAMP, this is the DB-level automatic update on the column.
TL;DR
silent works by not passing updatedAt to UPDATE query. So if the DB's column doesn't have any automatic assignment, by not passing the new value for updatedAt, the column value should stay the same. While, if you have automatic update on the DB's column, not passing the value means it allows DB to automatically assign the new value.
How I can do "silence" without updating DB schema
To disable the DB's automatic field, you can pass the value that you want to assign for the column. By default, Sequelize also has an automatic assignment of updateAt column, so we need to overwrite it.
const model = sequelize.define('MyModel',
{
count: {
type: DataTypes.INTEGER
}
},
{
hooks: {
beforeUpdate: (record, options) => {
if(options['custom_silent']) { // Add my custom silencing option
// If custom_silent is true, pass previous(current value in DB) updatedAt value and assigned to it
record.dataValues.updatedAt = record._previousDataValues.updatedAt
}
// If custom_silent is not true, use default Sequelize behavior (updatedAt = (current timestamp))
}
}
}
)
How to use it
await modelInstance.update(
{
count: modelInstance.count + 1000,
},
{
custom_silent: true
}
);

Mongodb findOneAndUpdate - check if new document added, or just updated

I have the following code which is finding and updating an employee-store record (if it exists, otherwise it creates one).
I have lots of employees in different stores, and they can choose to change store at any point (as long as the location is in America)
Below is my code so far:
employee = await this.employeeStoreModel.findOneAndUpdate(
{ employee: employeeRecord._id, location: "America" },
{
employee: employeeRecord._id,
store: employeeRecord.store,
location: "America",
},
{ new: true, upsert: true }
);
This works correctly, however I am trying to return some messages from this based on what is being updated. It could be any of the following messages:
If it's a completely new employee-store record being added, then return "{{StoreID}} has a new employee - {{EmployeeID}}"
If it's a change of store on an existing employee-store record, then return "{{EmployeeID}} has changed from {{old StoreID}} to {{new StoreID}}"
Is this possible to do? Can anyone guide me on how I could start this?
In the Options field,
rawResult: true
You must add the parameter.
The result will be as in the example below.
{ response:
{ n: 1,
updatedExisting: false,
upserted: 5e6a9e5ec6e44398ae2ac16a },
value:
{ _id: 5e6a9e5ec6e44398ae2ac16a,
name: 'Will Riker',
__v: 0,
age: 29 },
ok: 1 }
Depending on whether there is an update or insert in the updatedExisting field, you can return any message you want.

update data in Mongo DB even when collection does not exist using node.js and mongoose

I need to update a field which exists or not: I've tried this
db.foo.update(
{ site: '"wisdom'},
{ $set: {'club': 'fc barcelona'}}, (upsert=true)
)
You can use upsert:true option of mongodb.
Basically it updates a document if the query string matches the document or if it is not
matched then basically creates it. By default it's value is set to false.
db.foo.update(
{ site_id: "xxx" },
{ $set: { "title.de": "", "content.de": "" } },
{upsert: true}
);
Reference: https://docs.mongodb.com/manual/reference/method/db.collection.update/

Cannot change _id field in Mongo DB from int to objectId using foreach [duplicate]

I want update an _id field of one document. I know it's not really good practice. But for some technical reason, I need to update it.
If I try to update it I get:
db.clients.update({ _id: ObjectId("123")}, { $set: { _id: ObjectId("456")}})
Performing an update on the path '_id' would modify the immutable field '_id'
And the update is rejected. How I can update it?
You cannot update it. You'll have to save the document using a new _id, and then remove the old document.
// store the document in a variable
doc = db.clients.findOne({_id: ObjectId("4cc45467c55f4d2d2a000002")})
// set a new _id on the document
doc._id = ObjectId("4c8a331bda76c559ef000004")
// insert the document, using the new _id
db.clients.insert(doc)
// remove the document with the old _id
db.clients.remove({_id: ObjectId("4cc45467c55f4d2d2a000002")})
To do it for your whole collection you can also use a loop (based on Niels example):
db.status.find().forEach(function(doc){
doc._id=doc.UserId; db.status_new.insert(doc);
});
db.status_new.renameCollection("status", true);
In this case UserId was the new ID I wanted to use
In case, you want to rename _id in same collection (for instance, if you want to prefix some _ids):
db.someCollection.find().snapshot().forEach(function(doc) {
if (doc._id.indexOf("2019:") != 0) {
print("Processing: " + doc._id);
var oldDocId = doc._id;
doc._id = "2019:" + doc._id;
db.someCollection.insert(doc);
db.someCollection.remove({_id: oldDocId});
}
});
if (doc._id.indexOf("2019:") != 0) {... needed to prevent infinite loop, since forEach picks the inserted docs, even throught .snapshot() method used.
Here I have a solution that avoid multiple requests, for loops and old document removal.
You can easily create a new idea manually using something like:_id:ObjectId()
But knowing Mongo will automatically assign an _id if missing, you can use aggregate to create a $project containing all the fields of your document, but omit the field _id. You can then save it with $out
So if your document is:
{
"_id":ObjectId("5b5ed345cfbce6787588e480"),
"title": "foo",
"description": "bar"
}
Then your query will be:
db.getCollection('myCollection').aggregate([
{$match:
{_id: ObjectId("5b5ed345cfbce6787588e480")}
}
{$project:
{
title: '$title',
description: '$description'
}
},
{$out: 'myCollection'}
])
You can also create a new document from MongoDB compass or using command and set the specific _id value that you want.
As a very small improvement to the above answers i would suggest using
let doc1 = {... doc};
then
db.dyn_user_metricFormulaDefinitions.deleteOne({_id: doc._id});
This way we don't need to create extra variable to hold old _id.
Slightly modified example of #Florent Arlandis above where we insert _id from a different field in a document:
> db.coll.insertOne({ "_id": 1, "item": { "product": { "id": 11 } }, "source": "Good Store" })
{ "acknowledged" : true, "insertedId" : 1 }
> db.coll.aggregate( [ { $set: { _id : "$item.product.id" }}, { $out: "coll" } ]) // inserting _id you want for the current collection
> db.coll.find() // check that _id is changed
{ "_id" : 11, "item" : { "product" : { "id" : 11 } }, "source" : "Good Store" }
Do not use $match filter + $out as in #Florent Arlandis's answer since $out fully remove data in collection before inserting aggregate result, so effectively you will loose all data that don't match to $match filter

Update a row in nedb

I have the following data in nedb.
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":14,"_id":"fdaaTWSxloQZdYlT"]
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":1,"_id":"fzh2cedAXxT76GwB"]
["UserId":"1446943507761","UserName":"xxx","link":"xxx.html","taskDone":"false","id":0,"_id":"k4loE7XR5gioQk54"]
I am trying to update row with id 0 and set the value of taskDone to true. I use the following query to set the value to true
db.taskmap.update({ _id: "k4loE7XR5gioQk54", UserName:"xxx" }, { $set: { taskDone: "true"} }, function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
});
It updates the value but it updates as a new row. It basically inserts a new row with same values except for the taskdone value as true. It does not delete the existing data. Hence in the final data table after update i get tow rows for id 0 with all values same except for the taskDone. I am not sure if i am doing anything wrong. It will be helpful if anybody can tell me a correct way of updating the value.
You should call db.loadDatabase(); again at the end of db.update(); to see, that no second row with the same _id: appears instead the specific doc gets directly updated.
Edit:
It appears that sometimes when you do db.update() the document that should be updated appears twice instead on the database. It happened to me when I was updating an entry in a list of servers, multiple entries with the modified values were appearing on the database. So to avoid this simply do the following. (I took the same code that was suggested as the answer)
db.update(
{ _id: "k4loE7XR5gioQk54", UserName:"xxx" },
{ $set: { taskDone: "true"} },
{},// this argument was missing
function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
db.loadDatabase();
}
);
Doing this prevents this from happening. I tested it multiple times and the issue disappeared.
update wants four arguments
var Datastore = require('nedb');
var db = new Datastore();
db.insert(
[
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":14,
"_id":"fdaaTWSxloQZdYlT"
},
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":1,
"_id":"fzh2cedAXxT76GwB"
},
{
"UserId":"1446943507761",
"UserName":"xxx",
"link":"xxx.html",
"taskDone":"false",
"id":0,
"_id":"k4loE7XR5gioQk54"
}],
function (err, newDocs) {
// empty here
}
);
db.update(
{ _id: "k4loE7XR5gioQk54", UserName:"xxx" },
{ $set: { taskDone: "true"} },
{},// this argument was missing
function (err, numReplaced) {
console.log("replaced---->" + numReplaced);
}
);
// should give the correct result now
db.find({}).exec(function (err, docs) {console.log(docs);});

Categories