MongoClient Native FindAndModify "Need update or remove" Error - node.js

My node.js client looks like this:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect(mongoendpoint, function(err, db) {
if(err) throw err;
var collection = db.collection('test-collection');
var ws = new WebSocket(websocket_Endpoint);
ws.on('open', function() {
log.info('Connected.');
});
ws.on('message', function(data, flags) {
wsevent = JSON.parse(data);
var args = {
'query': {
id: '1.2.3.4'
},
'update': {
$set: {
lastseen: "201405231344"
},
$addToSet: {
record: "event123"
}
},
'new': true,
'upsert': true
};
collection.findAndModify(args, function(err, doc){
log.info(err);
});
});
});
When I run this, I get the following error:
info: name=MongoError, ok=0, errmsg=need remove or update
I can't figure out why. I can run the exact same args json above using RoboMongo and the query works just fine.
Robomongo Query
db['test-collection'].findAndModify({"query":{"id":"1.2.3.4"},"update":{"$setOnInsert":{"lastseen":"201405231344"},"$addToSet":{"record":"event123"}},"new":true,"upsert":true});
What am I missing?

Your args section is wrong, it should be an array and does not need key values for "query" and "update". And the "options" value also needs to be an object (sub-document):
var args = [
{ id: '1.2.3.4' },
{
$set: {
lastseen: "201405231344"
},
$addToSet: {
record: "event123"
}
},
{
'new': true,
'upsert': true
}
];
Or specifically in the call:
collection.findAndModify(
{ id: '1.2.3.4' },
{
$set: { lastseen: "201405231344" },
$addToSet: { record: "event123" }
},
{
'new': true,
'upsert': false
},
function(err, doc){
Examples are also included on the manual page.

Related

how to set variable value in query function?

1.here i need "did" value outside if condition but when it's going in IF and execute query than it's not setting up the value of did which I define in function "did = doc.value.seqValue" getting undefined.
2. else working fine.
var did;
if (documents[0] == null) {
dbo
.collection("Domain")
.findOneAndUpdate(
{ _id: "Did" },
{ $inc: { seqValue: 1 } },
function (err, doc) {
if (err) throw err;
dbo.collection("Domain").insertOne(
{
_id: doc.value.seqValue,
UserID: parseInt(req.body.userid),
ClientID: parseInt(req.body.clientid),
Domain: req.body.domain,
IsDeleted: false,
CreatedBy: parseInt(createdby),
CreatedDate: new Date(),
LastUpdatedDate: null,
LastUpdatedBy: null,
},
function (err, data) {
if (err) throw err;
did = doc.value.seqValue;
}
);
}
);
} else {
did = documents[0].DomainID;
}

Mongoose and Postman: test a model with nested objects

I created a model like this in nodeJS, using Mongoose:
'use strict';
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var plantSchema = new Schema({
plantData: [
{
family: { type: String, default: 'Liliaceae' },
genusObj: {
genus: { type: String, required: 'Please enter the genus plant name' },
tulipGroup: { type: String }, // e.g. Single Early
tulipGroupNumber: { type: Number } // e.g. 1
},
species: { type: String, required: 'Please enter the species plant name' },
commonName: { type: String },
description: { type: String },
mainImage: {},
otherImages: {},
images: {},
}
],
detailsData: [ .... ]
});
module.exports = mongoose.model('plants', plantSchema);
And this is my controller:
var mongoose = require('mongoose'),
Plant = mongoose.model('plants');
// READ ALL
exports.list_all_plants = function(req, res) {
Plant.find({}, function(err, plants) {
if (err) {
res.send(err);
}
res.json(plants);
});
};
// CREATE
exports.create_new_plant = function(req, res) {
var new_plant = new Plant(req.body);
new_plant.save(function(err, plant_inserted) {
if (err) {
res.send(err);
}
res.json(plant_inserted);
});
};
// READ (probably plantId comes from an _id previously retrieved)
exports.read_a_plant = function(req, res) {
Plant.findById(req.params.plantId, function(err, plant_searched) {
if (err) {
res.send(err);
}
res.json(plant_searched);
});
};
// UPDATE
exports.update_a_plant = function(req, res) {
Plant.findOneAndUpdate(
{
_id: req.params.plantId
},
req.body,
{new: true},
function(err, plant_to_update) {
if (err) {
res.send(err);
}
res.json(plant_to_update);
}
);
};
// DELETE
exports.delete_a_plant = function(req, res) {
Task.remove(
{
_id: req.params.plantId
},
function(err, task) {
if (err) {
res.send(err);
}
res.json({ message: 'Plant successfully deleted' });
}
);
};
And finally, i have this router:
'use strict';
module.exports = function(app) {
var plantList = require('../controllers/plantController');
// plant routes
app.route('/plants')
.get(plantList.list_all_plants)
.post(plantList.create_new_plant);
app.route('/plants/:plantId')
.get(plantList.read_a_plant)
.put(plantList.update_a_plant)
.delete(plantList.delete_a_plant);
What I'd like to do is testing all this with Postman.
If I try with the GET method, using simply
http://localhost:3000/plants
everything works fine: I mean, it returns an empty array (mongodb is up and running, and everything is set).
Now I wanted to try to insert a new element with Postman: I selected POST and x-www-form-urlencoded under body. Required properties are plantData{genusObj{genus}} and plantData{species} : since I'm quite new with both postman and mongodb, how can I enter a sub-element in postman, to create a new Plant ?
there are only KEY and VALUE options, and i don't know how to write a sub-key like plantData->genusObj->genus.
P.S.: Suggestions on data model are welcome, I tried to build a generic plant database but oriented on tulips (so usually i can enter tulips, but if i need to enter something else, i can).
Well, it seems that this answer fits to me: in fact, on Postman i selected under "body" the "raw" option, then I selected JSON instead of TEXT from the dropdown menu, and finally I used this object (meanwhile I slightly changed the
model) - don't forget the " symbols everywhere, like I did - ' is not accepted:
{
"plantData": [
{
"family": "Liliaceae",
"genusObj": {
"genus": "Tulipa",
"tulipGroup": "Single Late",
"tulipGroupNumber": 5
},
"species": "TEST",
"sellName": "Queen of night",
"description": "black tulip",
"mainImage": "",
"otherImages": "",
"images": ""
}
],
"sellingData": [
{
"price": 0.50,
"availableQuantity": 100
}
],
"detailsData": [
{
"heightInCm": "60-65",
"floweringTime": "late spring",
"plantDepthCm": "20",
"plantSpacingCm": "10",
"bulbSizeInCm": "12",
"flowerColor": "Black",
"lightRequirements": "full sun"
}
]
}

Check for subdocument is deleted or not in node.js [duplicate]

Chatrooms.findOneAndUpdate({Roomname: room.Roomname},{ $setOnInsert: {status: true, userNum: 1}}, {new: true, upsert: true}, function(err, doc) {
if(err) console.log(err);
console.log("DOC " + doc)
if(doc.status) {
// FOUND ROOM SATTUS IS TRUE LOGIC
console.log(doc);
// return callback(true)
}
});
Above query will return to me the actual document that's updated or inserted but I can't check exactly which one it is. If I do an update instead of findOneandUpdate I'm returned this
{
ok: 1,
nModified: 0,
n: 1,
upserted: [ { index: 0, _id: 55df883dd5c3f7cda6f84c78 } ]
}
How do I return both the document and the write result or at least the upserted field from the write result.
As of 8 August 2019 (Mongoose Version 5.6.9), the property to set is "rawResult" and not "passRawResult":
M.findOneAndUpdate({}, obj, {new: true, upsert: true, rawResult:true}, function(err, d) {
if(err) console.log(err);
console.log(d);
});
Output:
{ lastErrorObject:
{ n: 1,
updatedExisting: false,
upserted: 5d4befa6b44b48c3f2d21c75 },
value: { _id: 5d4befa6b44b48c3f2d21c75, rating: 4, review: 'QQQ' },
ok: 1 }
Notice also the result is returned as the second parameter and not the third parameter of the callback. The document can be retrieved by d.value.
Version 4.1.10 of Mongoose has an option called passRawResult which if set to true causes the raw parameter to be passed. Leaving out this option seems to default to false and cause raw to always be undefined:
passRawResult: if true, passes the raw result from the MongoDB driver
as the third callback parameter
http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate
Alright so my main problem was that I couldn't get the _id of the document I inserted without not being able to check whether if it was updated/found or inserted. However I learned that you can generate your own Id's.
id = mongoose.Types.ObjectId();
Chatrooms.findOneAndUpdate({Roomname: room.Roomname},{ $setOnInsert: {_id: id, status: true, userNum: 1}}, {new: true, upsert: true}, function(err, doc) {
if(err) console.log(err);
if(doc === null) {
// inserted document logic
// _id available for inserted document via id
} else if(doc.status) {
// found document logic
}
});
Update
Mongoose API v4.4.8
passRawResult: if true, passes the raw result from the MongoDB driver as the third callback parameter.
I'm afraid Using FindOneAndUpdate can't do what you whant because it doesn't has middleware and setter and it mention it the docs:
Although values are cast to their appropriate types when using the findAndModify helpers, the following are not applied:
defaults
Setters
validators
middleware
http://mongoosejs.com/docs/api.html search it in the findOneAndUpdate
if you want to get the docs before update and the docs after update you can do it this way :
Model.findOne({ name: 'borne' }, function (err, doc) {
if (doc){
console.log(doc);//this is ur document before update
doc.name = 'jason borne';
doc.save(callback); // you can use your own callback to get the udpated doc
}
})
hope it helps you
I don't know how this got completely off track, but there as always been a "third" argument response to all .XXupdate() methods, which is basically the raw response from the driver. This always tells you whether the document is "upserted" or not:
Chatrooms.findOneAndUpdate(
{ "Roomname": room.Roomname },
{ "$setOnInsert": {
"status": true, "userNum": 1
}},
{ "new": true, "upsert": true },
function(err, doc,raw) {
if(err) console.log(err);
// Check if upserted
if ( raw.lasErrorObject.n == 1 && !raw.lastErrorObject.updatedExisting ) {
console.log("upserted: %s", raw.lastErrorObject.upserted);
}
console.log("DOC " + doc)
if (doc.status) {
// FOUND ROOM SATTUS IS TRUE LOGIC
console.log(doc);
// return callback(true)
}
});
Which will tell you the _id of the document that was just upserted.
From something like this in the "raw" response:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
status: true,
userNum: 1
__v: 0 },
ok: 1 }
Complete reproducible listing:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test', testSchema, 'test');
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.eachSeries(
["first","second"],
function(it,callback) {
console.log(it);
Test.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true },
function(err,doc,raw) {
console.log(raw),
console.log(doc),
callback(err);
}
);
},
callback
);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
Which outputs:
first
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e2a92328f7d03a06a2dd6b },
value: { _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 },
ok: 1 }
{ _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 }
second
{ lastErrorObject: { updatedExisting: true, n: 1 },
value: { _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 },
ok: 1 }
{ _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 }

Mongoose update returns undefined

How can I update a field with new properties that is initially set to be an empty object?
For example, I have the following schema:
import mongoose from 'mongoose';
var RunSchema = mongoose.Schema(
{
runId: { type: String },
reports: {
cookieSummary: {
name: String,
path: String
}
}
}
)
export default mongoose.model('Run', RunSchema);
And I'm trying to update the following document:
{
"_id": {
"$oid": "5a0565c2537e0b5d9d08ee6b"
},
"__v": 0,
"reports": {},
"runId": "8r4LNN3fRqd3qNgdW"
}
But when I run this code, it returns undefined:
Run.findOneAndUpdate({runId: '8r4LNN3fRqd3qNgdW'},
{
$set: {'reports.cookieSummary': { 'name': 'test' }},
}, (err, doc) => { console.log(doc) })
The object notation works after adding type to fields, like this: name: { type: String }
Try to use dot notation, as you're setting just one field:
Run.findOneAndUpdate(
{ runId: '8r4LNN3fRqd3qNgdW' },
{ $set: {'reports.cookieSummary.name': 'test' } },
(err, doc) => { console.log(doc) })
According to the docs, the command you're using should work but you write it wrongly. Try like this:
Run.findOneAndUpdate(
{ runId: '8r4LNN3fRqd3qNgdW' },
{ $set: { 'reports.cookieSummary': {'name': 'test'} } },
(err, doc) => { console.log(doc) })
if it does not work, maybe mongo expect that the object matches its schema when you use the command like this. But I don't think so.
Let me know.
Your query for update a document is good only the mistake is at the end of curly braces of $set. You entered un-necessary comma at the end that is actually creating problem in this case. So I suggest you to remove it and run this :
Run.findOneAndUpdate({runId: '8r4LNN3fRqd3qNgdW'},
{
$set: {'reports.cookieSummary': { 'name': 'test' }}
}, (err, doc) => { console.log(doc) });
and then see. Rest of your query is fine.
Hope It will work for you.
Thanks.
Try using below code, it will update the document and return the updated document.
var Q = require('q');
var deferred = Q.defer();
Run.findOneAndUpdate({ runId: '8r4LNN3fRqd3qNgdW' }, { $set: { 'reports.cookieSummary.name': 'test' } }, { new: true },
(err, doc) => {
console.log(doc);
deferred.resolve(doc);
});
return deferred.promise;
I made a small change. Test this solution.
Run.findOneAndUpdate({runId: '8r4LNN3fRqd3qNgdW'},
{
$set: {"reports": {'cookieSummary':{'name': 'test'}}},
}, (err, doc) => { console.log(doc) })

How to check whether a document was inserted or updated when using findOneAndUpdate?

Chatrooms.findOneAndUpdate({Roomname: room.Roomname},{ $setOnInsert: {status: true, userNum: 1}}, {new: true, upsert: true}, function(err, doc) {
if(err) console.log(err);
console.log("DOC " + doc)
if(doc.status) {
// FOUND ROOM SATTUS IS TRUE LOGIC
console.log(doc);
// return callback(true)
}
});
Above query will return to me the actual document that's updated or inserted but I can't check exactly which one it is. If I do an update instead of findOneandUpdate I'm returned this
{
ok: 1,
nModified: 0,
n: 1,
upserted: [ { index: 0, _id: 55df883dd5c3f7cda6f84c78 } ]
}
How do I return both the document and the write result or at least the upserted field from the write result.
As of 8 August 2019 (Mongoose Version 5.6.9), the property to set is "rawResult" and not "passRawResult":
M.findOneAndUpdate({}, obj, {new: true, upsert: true, rawResult:true}, function(err, d) {
if(err) console.log(err);
console.log(d);
});
Output:
{ lastErrorObject:
{ n: 1,
updatedExisting: false,
upserted: 5d4befa6b44b48c3f2d21c75 },
value: { _id: 5d4befa6b44b48c3f2d21c75, rating: 4, review: 'QQQ' },
ok: 1 }
Notice also the result is returned as the second parameter and not the third parameter of the callback. The document can be retrieved by d.value.
Version 4.1.10 of Mongoose has an option called passRawResult which if set to true causes the raw parameter to be passed. Leaving out this option seems to default to false and cause raw to always be undefined:
passRawResult: if true, passes the raw result from the MongoDB driver
as the third callback parameter
http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate
Alright so my main problem was that I couldn't get the _id of the document I inserted without not being able to check whether if it was updated/found or inserted. However I learned that you can generate your own Id's.
id = mongoose.Types.ObjectId();
Chatrooms.findOneAndUpdate({Roomname: room.Roomname},{ $setOnInsert: {_id: id, status: true, userNum: 1}}, {new: true, upsert: true}, function(err, doc) {
if(err) console.log(err);
if(doc === null) {
// inserted document logic
// _id available for inserted document via id
} else if(doc.status) {
// found document logic
}
});
Update
Mongoose API v4.4.8
passRawResult: if true, passes the raw result from the MongoDB driver as the third callback parameter.
I'm afraid Using FindOneAndUpdate can't do what you whant because it doesn't has middleware and setter and it mention it the docs:
Although values are cast to their appropriate types when using the findAndModify helpers, the following are not applied:
defaults
Setters
validators
middleware
http://mongoosejs.com/docs/api.html search it in the findOneAndUpdate
if you want to get the docs before update and the docs after update you can do it this way :
Model.findOne({ name: 'borne' }, function (err, doc) {
if (doc){
console.log(doc);//this is ur document before update
doc.name = 'jason borne';
doc.save(callback); // you can use your own callback to get the udpated doc
}
})
hope it helps you
I don't know how this got completely off track, but there as always been a "third" argument response to all .XXupdate() methods, which is basically the raw response from the driver. This always tells you whether the document is "upserted" or not:
Chatrooms.findOneAndUpdate(
{ "Roomname": room.Roomname },
{ "$setOnInsert": {
"status": true, "userNum": 1
}},
{ "new": true, "upsert": true },
function(err, doc,raw) {
if(err) console.log(err);
// Check if upserted
if ( raw.lasErrorObject.n == 1 && !raw.lastErrorObject.updatedExisting ) {
console.log("upserted: %s", raw.lastErrorObject.upserted);
}
console.log("DOC " + doc)
if (doc.status) {
// FOUND ROOM SATTUS IS TRUE LOGIC
console.log(doc);
// return callback(true)
}
});
Which will tell you the _id of the document that was just upserted.
From something like this in the "raw" response:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
status: true,
userNum: 1
__v: 0 },
ok: 1 }
Complete reproducible listing:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema({
name: String
});
var Test = mongoose.model('Test', testSchema, 'test');
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.eachSeries(
["first","second"],
function(it,callback) {
console.log(it);
Test.findOneAndUpdate(
{ "name": "Bill" },
{ "$set": { "name": "Bill" } },
{ "new": true, "upsert": true },
function(err,doc,raw) {
console.log(raw),
console.log(doc),
callback(err);
}
);
},
callback
);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
Which outputs:
first
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e2a92328f7d03a06a2dd6b },
value: { _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 },
ok: 1 }
{ _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 }
second
{ lastErrorObject: { updatedExisting: true, n: 1 },
value: { _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 },
ok: 1 }
{ _id: 55e2a92328f7d03a06a2dd6b, name: 'Bill', __v: 0 }

Resources