Reputation: 357
I'm trying to build a simple task queue with express and mongoose. The idea is acquire a single client and return campaign id and client id (which is a subdocument of campaign). Each time someone acquires a client, its status code is set to 1. I've come up with the following query:
router.post('/lease', (err, res) => {
Campaign.findOneAndUpdate({'isEnabled': true, 'clients.contact_status_code': 0}, {
'$set': { 'clients.$.contact_status_code': 1 },
},
{
new: true,
projection: {
'clients.$': true,
},
},
(err, campaign) => {
if (err) {
return res.send(err);
}
res.json(campaign);
}
);
});
But all i'm getting after connecting to this endpoint is this:
{"_id":"591483241a84946a79626aef","clients":[{},{}]}
It seems to me that the problem is with the $ projection, but I have no idea how to fix this.
EDIT: I tried using the following code, utilizing $elemMatch:
router.post('/lease', (err, res) => {
Campaign.findOneAndUpdate({'isEnabled': true, 'clients.contact_status_code': 0}, {
'$set': { 'clients.$.contact_status_code': 1 },
},
{
new: true,
projection: {
clients: {
'$elemMatch': {contact_status_code: 1},
}
},
},
(err, campaign) => {
if (err) {
return res.send(err);
}
res.json(campaign);
}
);
});
Unfortunately, each request yields the first subdocument in the collection, that matched the criteria -- not specifically the one that was updated. Here is an example:
Say, i have the following document in mongo:
{
"_id" : ObjectId("591493d95d48e2738b0d4317"),
"name" : "asd",
"template" : "{{displayname}}",
"isEnabled" : true,
"clients" : [
{
"displayname" : "test",
"_id" : ObjectId("591493d95d48e2738b0d4319"),
"contact_status_code" : 0
},
{
"displayname" : "client",
"_id" : ObjectId("591493d95d48e2738b0d4318"),
"contact_status_code" : 0
}
],
"__v" : 0
}
I run the query for the first time and get the following result:
{"_id":"591493d95d48e2738b0d4317","clients":[{"displayname":"test","_id":"591493d95d48e2738b0d4319","contact_status_code":1}]}
Notice client id "591493d95d48e2738b0d4319" -- this time it runs as expected. But when i run the same query the second time, I get absolutely the same object, although I expect to get one with id "591493d95d48e2738b0d4318".
Upvotes: 3
Views: 9772
Reputation: 419
findAndModify
the function findOneAndUpdate
doesn't have the option new in its options listbefore
OR after
after
,returns the updated document rather than the originalbefore
,returns the original document rather than the updated.The default is
before
.
returnOriginal is Deprecated Use returnDocument instead
const filter={'isEnabled': true, 'clients.contact_status_code': 0}
const update={'$set': { 'clients.$.contact_status_code': 1 }}
const options={
returnDocument: 'after' //returns the updated document
projection: {
clients: {
'$elemMatch': {contact_status_code: 0},
// 0 because the old record gets matched
},
},
}
Campaign.findOneAndUpdate(filter,update, options
,(err, campaign) => {
if (err) {
return res.send(err);
}
res.json(campaign);
}
);
findOneAndUpdate Node.js MongoDB Driver API 3.6 documentation
Upvotes: 0
Reputation: 357
The issue was with new: true
Here is a working example:
Campaign.findOneAndUpdate({'isEnabled': true, 'clients.contact_status_code': 0}, {
'$set': { 'clients.$.contact_status_code': 1 },
},
{
//new: true <-- this was causing the trouble
projection: {
clients: {
'$elemMatch': {contact_status_code: 0}, // 0 because the old record gets matched
},
},
},
(err, campaign) => {
if (err) {
return res.send(err);
}
res.json(campaign);
}
);
I assume, when the new:true
is set, mongo loses the matching context. This approach returns the old record, unfortunately, but that still serves my needs to get the _id.
Upvotes: 9
Reputation: 405
Seems to me you are getting the campaign id, but you also want clients.$, have you tried clients.$._id?
Upvotes: 0