Conditional $or in MongoDB - node.js

I need to do from and to date querying in MongoDB. My solution is good when there is no from and to query parameters, but it crashes when I write this in URL.
What's the problem or maybe you have better solution for this.
Error
{"stringValue":""{ fromDate: 'Sat Dec 31 2022' }"","kind":"string","value":{"fromDate":"Sat Dec 31 2022"},"path":"date","reason":null,"valueType":"Object","statusCode":400}
Route
router.get("/:_id/logs", (req, res) => {
const fromDate = new Date(req.query.from).toDateString();
const toDate = new Date(req.query.to).toDateString();
User.findById(req.params._id)
.then((user) => {
Exercise.find({
$and: [
{ user: req.params._id },
fromDate !== "Invalid Date"
? { date: { $gte: { fromDate } } }
: {},
toDate !== "Invalid Date" ? { date: { $lte: { toDate } } } : {},
],
})
.limit(+req.query.limit)
.sort({ date: 1 })
.exec()
.then((exercises) =>
res.json({
username: user.username,
userId: user._id,
count: exercises.length,
logs: exercises,
})
)
.catch((err) => res.json({ ...err, statusCode: 400 }));
})
.catch((err) => res.json({ ...err, statusCode: 400 }));
});

In this case, it's simpler to build the query operator first:
const fromDate = new Date(req.query.from).toDateString();
const toDate = new Date(req.query.to).toDateString();
const dateOperator = {};
if (fromDate !== "Invalid Date") {
dateOperator.$gte = fromDate;
}
if (toDate !== "Invalid Date") {
dateOperator.$lte = toDate;
}
Exercise.find({
$and: [
{ user: req.params._id },
{ date: dateOperator },
],
})

Related

Store Json object in mongoDb with fetch request

I have a User model and trying to add education field to mongoDB as json object as follows,
User model
education: {
"school": String,
"years":Number
},
Client
//add Education section
const updateEducation = async (e) => {
e.preventDefault();
await fetch(`http://localhost:5000/api/user/updateEducation`, {
method: "PUT",
headers: { "Content-Type": "application/JSON", token: accessToken },
body: JSON.stringify({ userid: userid, educationSchool: educationSchool,
educationYearText: EducationYear}),
})
.then((res) => res.json())
.then((data) => {
console.log("User education is:", data.education +""+data.educationYear);
});
};
Server
const updateEducation = async (req, res) => {
try {
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{
$set: {
'education.school': req.body.educationSchool,
'education.years': req.body.educationYearText,
},
}
);
if (!user) {
res.status(404).json("user not exist");
}
res
.status(200)
.json({
education: user.education.School,
educationYear: user.education.years,
});
} catch (error) {
res.status(500).send({ error: error.message });
}
};
When im hitting this endpoint in postman http://localhost:5000/api/user/updateEducation
{
"userid":"63bbe4df75dca5aac7576e47",
"educationSchool":"Test college",
"educationYearText":"2018"
}
Im getting {
"error": "Plan executor error during findAndModify :: caused by :: Cannot create field 'school' in element {education: []}"
}
Whats wrong?
You should $push into an array:
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{
$push: {
education: {
school: req.body.educationSchool,
years: req.body.educationYearText,
}
},
},
{ new: true }
);

Cannot save object of array input data into mongodb

how I update my array data in nested array in node js and mongo db. when i was try to save data then massage was print but data not saved in mongo db, please check schema and back end code explain which sentence to create a problem.
// schema
mainmenu: [
{
arrname: String,
submenuarray: [
{
dishname1: {
type: String,
},
prize1: {
type: Number,
},
dishcategory1: {
type: String,
},
},
],
},
];
// back End
app.patch('/addsubmenu/:id/:mainid', async (req, res) => {
const mainid = req.params.mainid;
console.log(mainid);
const id = req.params.id;
console.log(id);
getschema
.findByIdAndUpdate(
{ _id: mainid, 'submenuarray._id': id },
{
$push: {
submenuarray: {
dishname1: req.body.dishname1,
prize1: req.body.prize1,
dishcategory1: req.body.dishcategory1,
},
},
}
)
.then((data) => {
console.log(data);
res.status(201).json(data);
})
.catch((err) => {
console.log(err);
});
});
The findByIdAndUpdate accept the id as first parameter, try to use findOneAndUpdate.
Also, set the flag { new: true} to return the updated object:
getschema
.findOneAndUpdate(
{ _id: mainid, 'submenuarray._id': id },
{
$push: {
submenuarray: {
dishname1: req.body.dishname1,
prize1: req.body.prize1,
dishcategory1: req.body.dishcategory1,
},
},
},
{ new: true }
)
.then((data) => {
console.log(data);
res.status(201).json(data);
})
.catch((err) => {
console.log(err);
});

Mongoose document.updateOne() not working as expected

code
Hi, I am trying to update my document using document.updateOne() in mongoose but only the balance field, downlines,
const username = req.params.username;
try {
const partner = await Partner.findOne({ username });
await partner.updateOne({ isApproved: true });
const ref = await Partner.findOne({ username: partner.referer });
if (ref) {
await ref.updateOne(
{
$inc: { points: 10 },
referer: partner.referer,
$inc: { earnings: 10 },
$inc: { balance: 10 },
$push: { downlines: partner.username },
},
{ new: true }
);
const grandRef = await Partner.findOne({ username: ref.referer });
if (!grandRef) {
res
.status(200)
.json(
'Partner approval was successfull with a referrer but no grand referrer'
);
} else {
await grandRef.updateOne(
{
$inc: { points: 3 },
grandReferer: ref.referer,
$inc: { earnings: 3 },
$inc: { balance: 3 },
$push: { grandDownlines: partner.username },
},
{ new: true }
);
res
.status(200)
.json(
'Partner approval was successfull with both referrer and grand referrer'
);
}
} else {
res
.status(200)
.json(
'Partner approval was successfull without a referrer or a grand referrer'
);
}
} catch (err) {
res.status(500).json('Something went wrong');
console.log(err);
}
and grand_downlines updates but the earnings and points do not. Please check my snippet in the attached image and help.
Okay so I just realized because I am using "$inc" more than one at the same time and if I change the positions, the last "$inc" updates so I used expressions.
{
$push: { downlines: partner.username },
points: ref.points + 10,
balance: ref.balance + 10,
earnings: ref.earnings + 10,
},
{ new: true }

Is there any way to rename the path while we select the complex object from mongodb using mongoose in nodejs?

I want to rename the path of the fields which are coming from the response.
My Query:
const allLeads = await Lead.find().select({
"basic.mobileNumber": 1
});
res.send({ allLeads });
Response I'm Getting
{
"allLeads": [
{
"_id": "5d9f0e2118d1a445bae077aa",
"basic": {
"mobileNumber": "1223654789"
}
},
{
"_id": "5d9f16a8cba7744902acb422",
"basic": {
"mobileNumber": "1123654789"
}
}
]
}
how I want the response
{
_id: 5d9f0e2118d1a445bae077aa,
mobileNumber: "1223654789"
},
{
_id: 5d9f16a8cba7744902acb422,
mobileNumber: "1123654789"
}
So is there any way yo archive this using mongoose?
I did it like this. Is there any other and better way to do this?
let simpleLeadInfo = [];
await SwatiLead.find()
.select({
_id: 1,
"basic.mobileNumber": 1,
})
.exec((err, data) => {
if (!err) {
for (lead in data) {
const obj = {
id: data[lead]._id,
mobileNumber: data[lead].basic.mobileNumber,
};
simpleLeadInfo = [...simpleLeadInfo, obj];
}
return res.send({ error: false, status: "OK", simpleLeadInfo });
}
});

Update the same property of every document of a mongoDb collection with different values

I have a collection in mongoDb which looks like this
{
"slno" : NumberInt(1),
"name" : "Item 1"
}
{
"slno" : NumberInt(2),
"name" : "Item 2"
}
{
"slno" : NumberInt(3),
"name" : "Item 3"
}
I am receiving a request from angularJs frontend to update this collection to
{
"slno" : NumberInt(1),
"name" : "Item 3"
}
{
"slno" : NumberInt(2),
"name" : "Item 1"
}
{
"slno" : NumberInt(3),
"name" : "Item 2"
}
I am using Mongoose 5.0 ORM with Node 6.11 and express 4.15. Please help me find the best way to achieve this.
You basically want bulkWrite(), which can take the input array of objects and use it to make a "batch" of requests to update the matched documents.
Presuming the array of documents is being sent in req.body.updates, then you would have something like
const Model = require('../models/model');
router.post('/update', (req,res) => {
Model.bulkWrite(
req.body.updates.map(({ slno, name }) =>
({
updateOne: {
filter: { slno },
update: { $set: { name } }
}
})
)
})
.then(result => {
// maybe do something with the WriteResult
res.send("ok"); // or whatever response
})
.catch(e => {
// do something with any error
})
})
This sends a request given the input as:
bulkWrite([
{ updateOne: { filter: { slno: 1 }, update: { '$set': { name: 'Item 3' } } } },
{ updateOne: { filter: { slno: 2 }, update: { '$set': { name: 'Item 1' } } } },
{ updateOne: { filter: { slno: 3 }, update: { '$set': { name: 'Item 2' } } } } ]
)
Which efficiently performs all updates in a single request to the server with a single response.
Also see the core MongoDB documentation on bulkWrite(). That's the documentation for the mongo shell method, but all the options and syntax are exactly the same in most drivers and especially within all JavaScript based drivers.
As a full working demonstration of the method in use with mongoose:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const testSchema = new Schema({
slno: Number,
name: String
});
const Test = mongoose.model('Test', testSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
const data = [1,2,3].map(n => ({ slno: n, name: `Item ${n}` }));
const request = [[1,3],[2,1],[3,2]]
.map(([slno, n]) => ({ slno, name: `Item ${n}` }));
mongoose.connect(uri)
.then(conn =>
Promise.all(Object.keys(conn.models).map( k => conn.models[k].remove()))
)
.then(() => Test.insertMany(data))
.then(() => Test.bulkWrite(
request.map(({ slno, name }) =>
({ updateOne: { filter: { slno }, update: { $set: { name } } } })
)
))
.then(result => log(result))
.then(() => Test.find())
.then(data => log(data))
.catch(e => console.error(e))
.then(() => mongoose.disconnect());
Or for more modern environments with async/await:
const { Schema } = mongoose = require('mongoose');
const uri = 'mongodb://localhost/test';
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const testSchema = new Schema({
slno: Number,
name: String
});
const Test = mongoose.model('Test', testSchema);
const log = data => console.log(JSON.stringify(data, undefined, 2));
const data = [1,2,3].map(n => ({ slno: n, name: `Item ${n}` }));
const request = [[1,3],[2,1],[3,2]]
.map(([slno,n]) => ({ slno, name: `Item ${n}` }));
(async function() {
try {
const conn = await mongoose.connect(uri)
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
await Test.insertMany(data);
let result = await Test.bulkWrite(
request.map(({ slno, name }) =>
({ updateOne: { filter: { slno }, update: { $set: { name } } } })
)
);
log(result);
let current = await Test.find();
log(current);
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Which loads the initial data and then updates, showing the response object ( serialized ) and the resulting items in the collection after the update is processed:
Mongoose: tests.remove({}, {})
Mongoose: tests.insertMany([ { _id: 5b1b89348f3c9e1cdb500699, slno: 1, name: 'Item 1', __v: 0 }, { _id: 5b1b89348f3c9e1cdb50069a, slno: 2, name: 'Item 2', __v: 0 }, { _id: 5b1b89348f3c9e1cdb50069b, slno: 3, name: 'Item 3', __v: 0 } ], {})
Mongoose: tests.bulkWrite([ { updateOne: { filter: { slno: 1 }, update: { '$set': { name: 'Item 3' } } } }, { updateOne: { filter: { slno: 2 }, update: { '$set': { name: 'Item 1' } } } }, { updateOne: { filter: { slno: 3 }, update: { '$set': { name: 'Item 2' } } } } ], {})
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"insertedIds": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 3,
"nModified": 3,
"nRemoved": 0,
"upserted": [],
"lastOp": {
"ts": "6564991738253934601",
"t": 20
}
}
Mongoose: tests.find({}, { fields: {} })
[
{
"_id": "5b1b89348f3c9e1cdb500699",
"slno": 1,
"name": "Item 3",
"__v": 0
},
{
"_id": "5b1b89348f3c9e1cdb50069a",
"slno": 2,
"name": "Item 1",
"__v": 0
},
{
"_id": "5b1b89348f3c9e1cdb50069b",
"slno": 3,
"name": "Item 2",
"__v": 0
}
]
That's using syntax which is compatible with NodeJS v6.x
A small change in Neil Lunn's answer did the job.
const Model = require('../models/model');
router.post('/update', (req,res) => {
var tempArray=[];
req.body.updates.map(({slno,name}) => {
tempArray.push({
updateOne: {
filter: {slno},
update: {$set: {name}}
}
});
});
Model.bulkWrite(tempArray).then((result) => {
//Send resposne
}).catch((err) => {
// Handle error
});
Thanks to Neil Lunn.

Resources