findOneAndUpdate with condition - node.js

I am trying to update a record based on condition:
Product.findOneAndUpdate({
number: fields.number[0]
}, {
category: fields.category[0],
name: fields.name[0]
},
function(err, product) {
});
So, I want to check if fields.category is present then update else do not update the original value.
fields.category ? fields.category[0] : ""
What is the proper way to do it?

After little search, found that I need to use findOne method and update based on property
Product.findOne({number: fields.number[0]}, function (err, product) {
product.category = fields.category ? fields.category[0] : product.category;
product.name = fields.name[0];
product.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
});

Related

Mongoose find query gives results in wrong order

I am saving an updated array of _id's to an array on a document. If I check the document, everything is updating quite nicely, but when I populate right afterward using a new find query, it doesn't work right. populate wasn't working for me, so I decided to handle it manually and I am successful at filling them in, but they're in the wrong order.
export function sort_update(req, res) {
Action.findOneAndUpdate({
_id: req.query.action
}, {
child_actions: req.body.newArray
}, {
useFindAndModify: false, new: true
}, function(err, doc) {
if (err) return res.send(500, {
error: err
});
console.log("child action id strings after update", doc.child_actions); // ["1", "2"] correct!
Action.find({'_id': {$in: doc.child_actions }}, function(err, child_action_docs) {
if (err) return res.send(500, {
error: err
});
console.log("child action objects after fill", child_action_docs); // [{ _id: "2" }, { _id: "1" }] wrong!
doc.child_actions = child_action_docs;
res.send(doc);
});
});
}
Why are they in the wrong order? Does {'_id': {$in: doc.child_actions }} simply not guarantee any order at all? Do I have to re-sort my objects to the doc.child_actions order deliberately or is there a simpler way of telling mongodb to keep the order in the original array?

Match one field and ignore others

I have my data like this:
{"_id":"5c0a17013d8ca91bf4ee7885",
"ep1":{
"email":"test#gmail.com",
"password":"123456",
"phoneNo":"123456789",
"endpointId":"ep1"
}
}
I want to match all the object that has key ep1 and value "email":"test#gmail.com" and set ep1:{}
Code:
My req object is like this {"endpointId":"ep1", "email":
"test#gmail.com"}
let query = {[req.body.endpointId]:{email: req.body.email}};
endpointModelData.endpoint.updateMany(query, {[req.body.endpointId]:{}}, {multi: true}, function(err, doc){
if (err) return res.send(500, { error: err });
console.log(doc)
return res.status(201).send("success.");
})
But this code only works if there is only one entry(that is email) inside my ep1 object
like this:
{"_id":"5c0a17013d8ca91bf4ee7885",
"ep1":{
"email":"test#gmail.com"
}
}
So, how can I match email and ignore other fields?
You need to match: {"ep1.email":"test#gmail.com"} so change your query to:
let query = { [`${req.body.endpointId}.email`]: req.body.email };
You can see that filter working here
On the end of the day your update should look like:
updateMany({ "ep1.email" : "test#gmail.com" }, { "ep1": {} }, {multi: true})

Mongodb/mongoose omit a field in response [duplicate]

I have a NodeJS application with Mongoose ODM(Mongoose 3.3.1). I want to retrieve all fields except 1 from my collection.For Example: I have a collection Product Which have 6 fields,I want to select all except a field "Image" . I used "exclude" method, but got error..
This was my code.
var Query = models.Product.find();
Query.exclude('title Image');
if (req.params.id) {
Query.where('_id', req.params.id);
}
Query.exec(function (err, product) {
if (!err) {
return res.send({ 'statusCode': 200, 'statusText': 'OK', 'data': product });
} else {
return res.send(500);
}
});
But this returns error
Express
500 TypeError: Object #<Query> has no method 'exclude'.........
Also I tried, var Query = models.Product.find().exclude('title','Image'); and var Query = models.Product.find({}).exclude('title','Image'); But getting the same error. How to exclude one/(two) particular fields from a collection in Mongoose.
Use query.select for field selection in the current (3.x) Mongoose builds.
Prefix a field name you want to exclude with a -; so in your case:
Query.select('-Image');
Quick aside: in JavaScript, variables starting with a capital letter should be reserved for constructor functions. So consider renaming Query as query in your code.
I don't know where you read about that .exclude function, because I can't find it in any documentation.
But you can exclude fields by using the second parameter of the find method.
Here is an example from the official documentation:
db.inventory.find( { type: 'food' }, { type:0 } )
This operation returns all documents where the value of the type field is food, but does not include the type field in the output.
Model.findOne({ _id: Your Id}, { password: 0, name: 0 }, function(err, user){
// put your code
});
this code worked in my project. Thanks!! have a nice day.
You could do this
const products = await Product.find().select(['-image'])
I am use this with async await
async (req, res) => {
try {
await User.findById(req.user,'name email',(err, user) => {
if(err || !user){
return res.status(404)
} else {
return res.status(200).json({
user,
});
}
});
} catch (error) {
console.log(error);
}
In the updated version of Mongoose you can use it in this way as below to get selected fields.
user.findById({_id: req.body.id}, 'username phno address').then(response => {
res.status(200).json({
result: true,
details: response
});
}).catch(err => {
res.status(500).json({ result: false });
});
I'm working on a feature. I store a userId array name "collectedUser" than who is collected the project. And I just want to return a field "isCollected" instead of "collectedUsers". So select is not what I want. But I got this solution.
This is after I get projects from database, I add "isCollected".
for (const item of projects) {
item.set("isCollected", item.collectedUsers.includes(userId), {
strict: false,
})
}
And this is in Decorator #Schema
#Schema({
timestamps: true,
toObject: {
virtuals: true,
versionKey: false,
transform: (doc, ret, options): Partial<Project> => {
return {
...ret,
projectManagers: undefined,
projectMembers: undefined,
collectedUsers: undefined
}
}
}
})
Finally in my controller
projects = projects.map(i => i.toObject())
It's a strange tricks that set undefined, but it really work.
Btw I'm using nestjs.
You can do it like this
const products = await Product.find().select({
"image": 0
});
For anyone looking for a way to always omit a field - more like a global option rather than doing so in the query e.g. a password field, using a getter that returns undefined also works
{
password: {
type: String,
required: true,
get: () => undefined,
},
}
NB: Getters must be enabled with option { toObject: { getters:true } }
you can exclude the field from the schema definition
by adding the attribute
excludedField : {
...
select: false,
...
}
whenever you want to add it to your result,
add this to your find()
find().select('+excludedFiled')

Selecting only modified subdocument from Mongo

I have a mongoose query like this:
var query = Events.findOneAndUpdate({ '_id': event._id,'participants._id':participant._id},{'$set': {'participants.$': participant}}, {upsert:false,new: true},function(err,result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
console.log(result.participants[0]);
res.jsonp(result.participants[0]);
});
and the query works properly modifying the participants subdocument inside Events collection.
The problem:
I need only the modified participant to be returned as JSON and I am not in need of the entire participants array but I am not able to achieve this since I get all the participants when I do console.log(result.participants);
How do I get only the modified subdocument after the query?
You may have to use the native JS filter() method as in the following:
Events.findOneAndUpdate(
{ '_id': event._id, 'participants._id': participant._id },
{ '$set': { 'participants.$': participant } },
{ upsert: false, new: true },
function(err, result){
if(err){
return res.status(500).jsonp({
error: 'Unable to update participant'
});
}
var modified = result.participants.filter(function(p){
return p._id === participant._id
})[0];
console.log(modified);
res.jsonp(modified);
}
);

Mongoose update or create many values in one query

I have a document like this:
{
"_id" : ObjectId("565e906bc2209d91c4357b59"),
"userEmail" : "abc#example.com",
"subscription" : {
"project1" : {
"subscribed" : false
},
"project2" : {
"subscribed" : true
},
"project3" : {
"subscribed" : false
},
"project4" : {
"subscribed" : false
}
}
}
I'm using express to for my post web service call like this:
router.post('/subscribe', function(req, res, next) {
MyModel.findOneAndUpdate(
{
userEmail: req.body.userEmail
},
{
// stuck here on update query
},
{
upsert: true
}, function(err, raw) {
if (err) return handleError(err);
res.json({result: raw});
}
)
});
My req contains data like this:
{
userEmail: "abc#example.com",
subscription: ["project1", "project4"]
}
So these are the steps I would like to perform on this call:
Check user exists, otherwise create the user. For instance, if abc#example.com doesn't exist, create a new document with userEmail as abc#example.com.
If user exists, check project1 and project4 exists in subscription object. If not create those.
If project1 and project4 exists in subscription, then update the subscribed to true.
I'm not sure whether I can achieve all the above 3 steps with a single query. Kindly advise.
Vimalraj,
You can accomplish this using $set. The $set operator replaces the value of a field with the specified value. According to the docs:
If the field does not exist, $set will add a new field with the specified value, provided that the new field does not violate a type constraint. If you specify a dotted path for a non-existent field, $set will create the embedded documents as needed to fulfill the dotted path to the field.
Since your req.subscription will be an array you'll have to build your query. to look like this:
{
$set: {
"subscription.project1":{"subscribed" : true},
"subscription.project4":{"subscribed" : true}
}
You can use reduce to create an object from req.subscription = ["project1","project4"] array
var subscription= req.subscription.reduce(function(o,v){
o["subscription." + v] = {subscription:true};
return o;
},{});
Then your code becomes:
router.post('/subscribe', function(req, res, next) {
var subscription= req.subscription.reduce(function(o,v){
o["subscription." + v] = {subscription:true};
return o;
},{});
MyModel.findOneAndUpdate(
{
userEmail: req.body.userEmail
},
{
$set: subscription
},
{
upsert: true
}, function(err, raw) {
if (err) return handleError(err);
res.json({result: raw});
}
)
});

Resources