how to update nested object using mongoose nodeJS and axios - javascript

I am trying to update the property date which is inside the object remindTime.
here is the original data
{
"_id": {
"$oid": "614d3cedfb2600340fdb28f9"
},
"date": "2021-09-23",
"title": "First test",
"description": "Going",
"remindTime": {
"date": "2021-09-23",
"time": "11:50:09 pm"
},
"isComplete": false,
"instantMessage": false,
"owner": "wxTWH8zqSwaIXPAVsjZoRCkvjx73",
"uuid": "0E561203-0BD0-4E15-AB17-858FFBD972D9",
"createdAt": {
"_delegate": {
"_methodName": "FieldValue.serverTimestamp"
}
},
"__v": 0
}
axios request data coming in on req.query = { _ref: '1632545954ref', owner: 'wxTWH8zqSwaIXPAVsjZoRCkvjx73' }
axios request data coming in on req.body = { remindTime: { date: '2021-09-28' } }
Templates.updateMany((req.query, req.body))
.then(
(record) => {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.json(record);
},
(err) => res.status(400).json(err)
)
Here is the output but not what I desire
{
"_id": {
"$oid": "614d3cedfb2600340fdb28f9"
},
"date": "2021-09-23",
"title": "First test",
"description": "Going",
"remindTime": {
"date": "2021-09-24"
},
"isComplete": false,
"instantMessage": false,
"owner": "wxTWH8zqSwaIXPAVsjZoRCkvjx73",
"uuid": "0E561203-0BD0-4E15-AB17-858FFBD972D9",
"createdAt": {
"_delegate": {
"_methodName": "FieldValue.serverTimestamp"
}
},
"_ref": "1632545954ref",
"__v": 0
}
As you can see that the property date in remindTime was updated however it erased the time property
I am hoping to only update the date property and keep the time property as is like shown below but with updated date
{
"_id": {
"$oid": "614d3cedfb2600340fdb28f9"
},
"date": "2021-09-23",
"title": "First test",
"description": "Going",
"remindTime": {
"date": "2021-09-28",
"time": "11:50:09 pm"
},
"isComplete": false,
"instantMessage": false,
"owner": "wxTWH8zqSwaIXPAVsjZoRCkvjx73",
"uuid": "0E561203-0BD0-4E15-AB17-858FFBD972D9",
"createdAt": {
"_delegate": {
"_methodName": "FieldValue.serverTimestamp"
}
},
"__v": 0
}

You can use $set operator to update your date property exclusively, without loosing other fields.
req.body = { remindTime: { $set: { date: '2021-09-28' } } }
You can check below links for more detailed info:
MongoDB $set
updateMany opertors

Related

How to return single object (mongoose/mongoDB)

I have this data stored in database.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v":0
}
I want to be able to return a single object with the id equal to the req.params.id, in this case that would be 62ff74bfe80b11ade2d34455.
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
I researched stackoverflow and came across this potential solution: Mongoose retrieve one document from nested array which implemented the aggregate framework. But when I test this in postman, the request isn't made.
const getTask = asyncHandler(async (req, res) => {
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id,
},
},
{
$project: {
columns: {
$first: {
$filter: {
input: "$columns.tasks",
cond: {
$eq: ["$$this._id", req.params.id],
},
},
},
},
},
},
{
$replaceRoot: {
newRoot: "$columns",
},
},
]);
});
Having an array inside an array complicates the query a bit, but here's one way to retrieve the data you want.
db.Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id
}
},
{"$unwind": "$columns"},
{
$match: {
"columns.tasks._id": req.params.id
}
},
{
"$project": {
"task": {
"$first": {
"$filter": {
"input": "$columns.tasks",
"cond": {"$eq": ["$$this._id", req.params.id]}
}
}
}
}
},
{"$replaceWith": "$task"}
])
Try it on mongoplayground.net. [The mongoplayground.net example uses "62ff74bfe80b11ade2d34455" rather than req.params.id.]

How to remove deeply nested object (Node.js, mongoose)

I'm making a kanban task management app and I'm trying to remove a task with the _id: req.params.id which has the value of 62fa5ae05778ec97bc6ee23a. I tried the following:
const task = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{ $pull: { "columns.$.tasks.$._id": req.params.id } },
{ new: true }
);
But I get the error Too many positional (i.e. '$') elements found in path'columns.$.tasks.$._id'
I searched for a while and came across arrayFilters from the docs but I'm struggling a lot to understand how to implement it for this particular need.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23b"
},
{
"name": "do homework",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23c"
}
],
"_id": "62fa5ae05778ec97bc6ee23a"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v": 0
}
You need to use $[] positional operator in order to pull from the nested array. Try running this query:
db.Board.updateOne({
"_id" : "62fa5aa25778ec97bc6ee231",
}, {
$pull: { 'columns.$[].tasks': { '_id': '62fa5ae05778ec97bc6ee23a' } }
});

How can I get count of the Documents and filter them in efficient way? (mongoose)

I'm implementing search function that is simply find document in mongoDB. I want to .skip(x) and .limit(x) on result to simulate paging result, but can I get total count of document (before skip and limit) and get filtered result at once?
Code that produce Expected Output :
db.Datas.find({ type: "Unknown" })
.then((result) => {
let count = result.length;
db.Datas.find({ type: "Unknown" })
.sort({ createdAt: -1 })
.skip((req.query.page - 1) * 10)
.limit(10)
.then((res) => {
res.json({ count: count, result: res });
});
})
.catch((err) => {});
But querying twice it somehow annoying, and it might be slow at large database.
I tried something like find({}).then(x => { ... }).sort(...) ... but isn't working because it only returns Promise.
How can I do this things in efficient way?
or is just getting whole documents and skip, limit with JS-way (using .splice, or etc..) will be faster and efficient?
You can use $facet aggregation to achieve this.
db.Datas.aggregate([
{
$match: {
"type": "Unknown"
}
},
{
$sort: {
createdAt: -1
}
},
{
$facet: {
totalRecords: [
{
$count: "total"
}
],
data: [
{
$skip: 0
},
{
$limit: 5
}
]
}
}
])
Playground
Let's say you have these documents:
db={
"Datas": [
{
"_id": "5e390fc33285e463a0799689",
"type": "Known",
"createdAt": "2020-02-04T06:31:31.311Z",
"__v": 0
},
{
"_id": "5e390fd03285e463a079968a",
"type": "Known",
"createdAt": "2020-02-04T06:31:44.190Z",
"__v": 0
},
{
"_id": "5e390fda3285e463a079968b",
"type": "Unknown",
"createdAt": "2020-02-04T06:31:54.248Z",
"__v": 0
},
{
"_id": "5e390fdf3285e463a079968c",
"type": "Unknown",
"createdAt": "2020-02-04T06:31:59.993Z",
"__v": 0
},
{
"_id": "5e390fec3285e463a079968d",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:12.336Z",
"__v": 0
},
{
"_id": "5e390ffd3285e463a079968e",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:29.670Z",
"__v": 0
},
{
"_id": "5e3910163285e463a079968f",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:54.131Z",
"__v": 0
},
{
"_id": "5e3910213285e463a0799690",
"type": "Unknown",
"createdAt": "2020-02-04T06:33:05.166Z",
"__v": 0
}
]
}
Response will be like this:
[
{
"data": [
{
"__v": 0,
"_id": "5e3910213285e463a0799690",
"createdAt": "2020-02-04T06:33:05.166Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e3910163285e463a079968f",
"createdAt": "2020-02-04T06:32:54.131Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390ffd3285e463a079968e",
"createdAt": "2020-02-04T06:32:29.670Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390fec3285e463a079968d",
"createdAt": "2020-02-04T06:32:12.336Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390fdf3285e463a079968c",
"createdAt": "2020-02-04T06:31:59.993Z",
"type": "Unknown"
}
],
"totalRecords": [
{
"total": 6
}
]
}
]
As you see, we got the total records with the filtered, sorted, skipped and limited data.

Incorrect output when using lodash on results returned by mongoose

I have a result set returned from a mongodb query and am using lodash to reformat it. I am trying to convert the array of objects into a single object. The problem is when I use lodash on the result set, I get unexpected output.
NOTE: Running the snippet on codepen/codesandbox gives correct output, but not when used directly from mongoose results.
Mongoose Query
try {
const petInfo = await pets.find({ userId: user_id, petId: pet_id })
.select({
"_id": 0,
"createdAt": 0,
"updatedAt": 0,
"__v": 0
});
if(!petInfo) {
return res.status(400).json({ message: "FAILED_TO_FETCH_PET_INFO" });
}
let newObj = _.reduce(petInfo, (acc, cur)=> { return _.assign(acc, cur) }, {});
return res.status(200).json(newObj);
}
catch (error) {
req.errorMsg = error.message; // Log actual error
return res.status(500).json({ message: "SOME_ERROR_OCCURRED" });
}
Result from mongoose find query (petInfo)
[
{
"age": {
"days": "",
"months": "",
"years": ""
},
"userId": "45422605180207851194",
"name": "Oscar",
"gender": "FEMALE",
"type": "Dog",
"breed": "",
"weight": "",
"spayOrNeuter": false,
"petId": "KSVv7yJLnUWX3n"
}
]
Lodash snippet
let newObj = _.reduce(petInfo, (acc, cur)=> { return _.assign(acc, cur) }, {});
return res.status(200).json(newObj);
Result after modification
{
"$__": {
"strictMode": true,
"selected": {
"_id": 0,
"createdAt": 0,
"updatedAt": 0,
"__v": 0
},
"getters": {
"age": {
"days": "",
"months": "",
"years": ""
}
},
"wasPopulated": false,
"scope": {
"age": {
"days": "",
"months": "",
"years": ""
},
"userId": "45422605180207851194",
"name": "Oscar",
"gender": "FEMALE",
"type": "Dog",
"breed": "",
"weight": "",
"spayOrNeuter": false,
"petId": "KSVv7yJLnUWX3n"
},
"activePaths": {
"paths": {
"userId": "init",
"petId": "init",
"name": "init",
"gender": "init",
"type": "init",
"breed": "init",
"age.days": "init",
"age.months": "init",
"age.years": "init",
"weight": "init",
"spayOrNeuter": "init"
},
"states": {
"ignore": {},
"default": {},
"init": {
"userId": true,
"name": true,
"gender": true,
"type": true,
"breed": true,
"age.days": true,
"age.months": true,
"age.years": true,
"weight": true,
"spayOrNeuter": true,
"petId": true
},
"modify": {},
"require": {}
},
"stateNames": [
"require",
"modify",
"init",
"default",
"ignore"
]
},
"pathsToScopes": {},
"cachedRequired": {},
"session": null,
"$setCalled": {},
"emitter": {
"_events": {},
"_eventsCount": 0,
"_maxListeners": 0
},
"$options": {
"skipId": true,
"isNew": false,
"willInit": true
},
"nestedPath": "age"
},
"isNew": false,
"_doc": {
"age": {
"days": "",
"months": "",
"years": ""
},
"userId": "45422605180207851194",
"name": "Oscar",
"gender": "FEMALE",
"type": "Dog",
"breed": "",
"weight": "",
"spayOrNeuter": false,
"petId": "KSVv7yJLnUWX3n"
},
"$locals": {},
"$init": true
}
The reason for this is because mongoose doesn't return the object you think it does. Instead, it returns a 'mongoose' object with a bunch of methods ect ect. There are 2 ways around this, either call .lean() on your query or toJSON() on the result to sanitize result into normal js object.
.lean()
const petInfo = await pets.find({ userId: user_id, petId: pet_id })
.select({
"_id": 0,
"createdAt": 0,
"updatedAt": 0,
"__v": 0
}).lean();
.toJSON()
const petInfo = await pets.find({ userId: user_id, petId: pet_id })
.select({
"_id": 0,
"createdAt": 0,
"updatedAt": 0,
"__v": 0
}).lean();
const parsed = petInfo.toJSON()
Your query will return the result set as document objects instead of plain objects, which is why you get all the additional information like strictMode, getters, etc.
You could use the lean() function in order to get plain objects only (see https://mongoosejs.com/docs/tutorials/lean.html), e.g.:
const petInfo = await pets.find({ userId: user_id, petId: pet_id })
.select({
"_id": 0,
"createdAt": 0,
"updatedAt": 0,
"__v": 0
}).lean();

how can i add document values depending on day createdAt in MongoDB?

I need to add a value present in the "value" property of each document that createdAt is the current day, that the document has a "finished" property as true, and that the document has a "value" and "period.check_out" property.
So far, I have been able to select all documents that have the "period.check_out" property and sum the values. The problem is selecting the documents of the current day. I've tried to sort by day in many ways, but I'm not getting.
I enabled nas database models, so whenever I create a new document, it automatically adds me a createdAt, updatedAt and _v.
document example:
[
{
"_id": "1",
"client": {
"name": "raul germano"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.59,
"name": "pro parking oficial"
},
"finished": false,
"createdAt": "2019-09-25T23:54:32.802Z",
"updatedAt": "2019-09-25T23:54:32.802Z"
},
{
"_id": "2",
"client": {
"name": "raul vitor"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.32,
"name": "pro parking oficial"
},
"period": {
"check_in": {
"user": "caboco",
"moment": "2019-09-25T00:05:36.273Z"
},
"check_out": {
"user": "outro caboco",
"moment": "2019-09-25T00:09:56.506Z"
}
},
"hours": 0.07,
"value": 0.17,
"finished": true,
"createdAt": "2019-09-26T23:54:33.463Z",
"updatedAt": "2019-09-26T23:54:33.463Z"
},
{
"_id": "3",
"client": {
"name": "raul germano"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.60,
"name": "pro parking oficial"
},
"finished": true,
"createdAt": "2019-09-25T23:54:33.463Z",
"updatedAt": "2019-09-25T23:54:33.463Z",
"period": {
"check_in": {
"user": "caboco",
"moment": "2019-09-25T00:05:36.273Z"
},
"check_out": {
"user": "outro caboco",
"moment": "2019-09-25T00:09:56.506Z"
}
},
"hours": 0.09,
"value": 0.23
}
]
Current query:
collection.aggregate([
{
$match: {
'period.check_out': {
$exists: true
},
createdAt: {
$gte: new Date("2019-09-24")
$lt: new Date("2019-09-25")
}
}
},
{
$unwind: '$parking'
},
{
$match: {
'parking._id': Types.ObjectId(parking_id)
}
},
{
$group: {
_id: Types.ObjectId(parking_id),
total: {
$sum: '$value'
}
}
}
])
That way he selects the last 24 hours
Based on the documents presented above, I need only the _id "1" and "2" documents to be selected. Thus resulting
Try replacing your $match with the following considering '2019-09-26' is the current date:
{
$match: {
'period.check_out': {
$exists: true
},
createdAt: {
$gte: new Date('2019-09-26'),
$lte: new Date(),
}
}
},
What you are matching is: $gte: 2019-09-24T00:00:00.000Z, $lte: 2019-09-25T00:00:00.000Z. And I don't think that would be the current date.
Instead you need to match is '2019-09-24T00:00:00.000Z' - current time.

Categories

Resources