Updating or adding sub document - node.js

I am struggling with very weird problem in MongoDB, what I want is that the query updates document if the condition matches and if not creates new (upsert).
The problem:
I get the correct results from the callback, as this returns the newly inserted document. But what appears the query gets never inserted in database.
What I think causes this, is the $set part, is looking for array but instead this must be object:
boards [
// Looks for element 48 and inserts there
]
{ '$set':
{ 'boards.48.acl': 'ReadWrite',
'boards.48.status': 'goal' } }
Here is the query:
// the 11 is example input
var scoreKey = util.format('scores.' + 11),
aclKey = scoreKey + '.acl',
statusKey = scoreKey + '.status',
setQuery = {
$set: { }
};
setQuery['$set'][aclKey] = acl;
setQuery['$set'][statusKey] = status;
db.sessions.findAndModify({
query: { '_id': sid },
update: setQuery,
upsert: true,
new: true
}, function (err, res) {
if (err) return cb(err);
else {
console.log(res);
return cb(null, res);
}
});
// Update
Well it seems all kind of the changes to database are silently failing, so the query seems to be correct.

Related

Mongoose updateOne with parameter {new:true} not showing actual updated value

I am struggling for a couple of hours to show the final value of an updated document (via mongoose updateOne). I successfully modify it as I can see "nModified: 1" when I call the endpoint on Postman, but I am not able to output the actual final document - even when using the parameter {new:true}
This is the code for the route:
// 3. We check if blockid is in this project
Block.findById(req.params.blockid)
.then(block => {
if (!block) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
// 4. We found the block, so we modify it
Block.updateOne(
{ _id: req.params.blockid },
{ $set: blockFields }, // data to be updated
{ new: true }, // flag to show the new updated document
(err, block) => {
if (err) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
console.log(block);
res.json(block);
}
);
})
.catch(err => console.error(err));
Instead, this is the output I am getting (Mongoose is on debug mode)
Any ideas?
Many thanks
{ new : true } will return the modified document rather than the original. updateOne doesn't have this option. If you need response as updated document use findOneAndUpdate.
Below are the mongoosejs function where you can use { new : true }
findByIdAndUpdate()
findOneAndUpdate()
findOneAndDelete()
findOneAndRemove()
findOneAndReplace()
Thank you #sivasankar for the answer. Here is the updated working version with findOneAndUpdate
And here the expected result:
you should give second param as object of keys value paris of data,
don't pass as $Set : blockfields, just add like below, if it is object containing parameters,
{ $set: blockFields }
Because code should be like this
Block.updateOne(
{ _id: req.params.blockid },
blockFields, // if blockfields is object containing parameters
{ new: true },
(err, block) => {
// lines of code
}
);
For more detail here is link to updateOne function detail updateOne

Nodejs and MongoDB : Unable to return value from a function

var config = require('config.json');
var mongo = require('mongoskin');
var db = mongo.db(config.connectionString, { native_parser: true });
module.exports.getNextSequence = function (name) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
console.log(temp); // <-- here the temp is getting printed correctly
}
);
return temp;
}
Using the above code, I am not able to return the value of doc.value.seq. When doing console.log(obj.getNextSequence) it prints undefined.
I want the function to return the value of doc.value.seq.
I'm not familiar with mongoskin so I'm not positive this is correct, but a database query is typically asynchronous, so you need to access the queried value via a callback.
I'm guessing your "getNextSequence" function is returning the "temp" variable before the database query completes (i.e. before the "temp = doc.value.seq" statement).
Try something like this:
module.exports.getNextSequence = function (name, callback) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
callback(temp);
}
);
}
Then access "temp" from within the callback passed to getNextSequence.
findAndModify is an asynchronous function. Your console.log line will run after you return temp, which will therefore be undefined. In order to get this to work, you'll want to use an asynchronous approach of your own. There are two available approaches in your situation.
Callbacks:
You're already using a callback, which you provide as the final argument to findAndModify. You could extend this approach and feed this into a callback of your own, as follows:
module.exports.getNextSequence = function (name, callback) {
db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true },
function (err, doc) {
if (err) {
return callback(err);
}
callback(null, doc.value.seq);
}
);
}
Of course, this will require you to pass a callback into getNextSequence and follow the callback pattern upstream. You might also want to handle the error from mongoskin and do some handling of your own.
Promises:
If you don't provide a callback to findAndModify, it will return a promise, which you can chain on to, as follows:
module.exports.getNextSequence = function (name) {
return db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true }
).then(function (doc) {
return doc.value.seq;
});
}
Again, this will require you to follow the promise pattern upstream. You'll want to read up on promises if you choose this approach, so that you can correctly handle errors, which I have not addressed in the example above.

Mongoose findOneAndUpdate not returning raw Mongo response

I'm trying to determine whether the document was found in my findOneAndUpdate operation. If it wasn't, I return a 404 not found error. I figured I'd use the "passRawValue" option Mongoose provides, and check for a raw value- if raw is undefined, I know the doc was not found.
However regardless whether the doc is found or not, my raw value is undefined. I've verified that the doc I'm trying to update is in the DB at the time of the query by running a simple "findOne" query just before the update. Where am I going wrong?
let updateItemById = (userId, itemId, params, cb) => {
//this finds and prints the document I'm testing with -- I know its in the DB
// Item.findOne({ "_id" : itemId, ownerId: userId }, (err, doc) => {
// if (doc) {
// console.log("This is the doc: ", doc);
// }
// });
Item.findOneAndUpdate({ "_id" : itemId, ownerId: userId },
{
$set: {
params
}
}, { runValidators: 1, passRawResult: true}, (err, doc, raw) => {
if (err) {
//winston.log
return cb(ErrorTypes.serverError(), false);
}
else if (raw) {
return cb(null, true);
}
else {
return cb(ErrorTypes.notFound(), false);
}
});
}
Hi I have a hunch that you are passing params that has a property that doesn't exist in the document in the database. In such case, nothing was modified, hence db doesn't return raw as the third parameter.
Update:
So I did some few tests of my own, and I see that if we pass option strict:false then your code should work as intended. So your options section will look like this
{ runValidators: 1, passRawResult: true, strict: false, new:true}
Explanation:
Mongoose has a strict option which by default is true. It makes sure that the values being updated is defined in the schema. So when we provide the option strict as false, as described in the [mongoose documentation] (http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate) we can achieve updating document with new field.
I also added new:true option which will return you the updated document.
P.S.
I would like to add though, since our upsert is false, which means it won't insert new document when a match is not found, it will return null for doc, and you can simple check on that. Why are you checking on raw? Is there any particular reason for this?
I know it's been awhile but I had the same problem here so I decided to leave an answer that maybe can help other people.
I was able to check whether the findOneAndUpdate() method found a document or not by checking if the doc parameter was null on the callback function:
async Update(request: Request, response: Response) {
const productId = request.params.id;
const query = { _id: productId };
const options = { new: true };
try {
await Product.findOneAndUpdate(query, request.body, options, (err, doc, res) => {
if (doc === null)
return response.status(404).send({
error: 'Product not found'
})
return response.status(204).send();
});
}
catch (err) {
return response.status(400).send({
error: 'Product update failed'
});
}
}

FindOneAndUpdate not updating nested field with passed in parameters

I am trying to create a service that can be used to update nested fields in a Mongoose model. In the following example I am trying to set the field 'meta.status' to the value 2. This is the service:
angular.module('rooms').factory('UpdateSvc',['$http', function($http)
{
return function(model, id, args)
{
var url = '/roomieUpdate/' + id;
$http.put(url, args).then(function(response)
{
response = response.data;
console.log(response.data);
});
}
}]);
This is how it is called in the controller:
var newStatus = {'meta.$.status' : 2};
var update = UpdateSvc("roomie", sessionStorage.getItem('userID'), newStatus);
And this is the model:
var RoomieSchema = new Schema(
{
meta:
{
created:
{
type: Date,
default: Date.now
},
status:
{
type: Number,
default: '1',
}
}
}
And this is the route:
app.put('/roomieUpdate/:id', function(req,res)
{
var id = req.params.id;
Roomie.findOneAndUpdate(
{_id: mongoose.Types.ObjectId(id)},
req.body,
{ new : true },
function(err, doc)
{
if(err)
{
console.log(err);
}
res.json(doc);
console.log(doc);
});
});
The argument is received correctly, but I can't seem to get this to work. I am not even getting an error message. console.log(doc) simply prints out the object and the field meta.status remains '1'. I have done a direct Mongo search on the target object to make sure that I wasn't just reading the old document. I've tried a great many things like separating the key and value of req.body and use {$set:{key:value}}, but result is the same.
findOneAndUpdate() by default will return the old document, not the new (updated) document.
For that, you need to set the new option:
Roomie.findOneAndUpdate({
_id : mongoose.Types.ObjectId(id)
}, req.body, { new : true }, function(err, doc) {
...
});
As it turns out, var newStatus = {'meta.$.status' : 2}; should have been var newStatus = {'meta.status' : 2}; The document now updates correctly.
The reason the $ was there in the first place was probably based on this thread:
findOneAndUpdate - Update the first object in array that has specific attribute
or another of the many threads I read through about this issue. I had tried several solutions with and without it, but couldn't get anything to go right.

getting sequence number from mongodb always undefined

I am trying to get by code the next sequence number but it always says "undefined".
I did this in my mongoDB before:
db.PresentationCollection.insert(
{
_id: "editorID",
seq: 0
}
)
my code (name is editorID):
function getNextSequence(name, db) {
var collection = db.get('PresentationCollection');
var ret = collection.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}
You're missing the callback. Callback-based asynchronous functions generally do not return anything meaningful. See the documentation for findAndModify in the node binding's readme.
I had the same problem from following this link and it is indeed the callback not being specified and your code not waiting for the returned result - mongo db documents create auto increment
Here is what I did to solve it. Keep in mind I am using Q for promise helping but you could use straight up javascript promises.
function _getNextSequence(name) {
var deferred = Q.defer();
db.counters.findAndModify(
{ _id: name }, //query
[], //sort
{ $inc: { seq: 1 } }, //update
{ new:true }, //options
function(err, doc) { //callback
if (err) deferred.reject(err.name + ': ' + err.message);
if (doc){
deferred.resolve(doc.value.seq);
}
});
return deferred.promise;
}

Resources