Mongoose unique value per day - node.js

Im trying to configure mongoose schema unique param. I need to allow write to DB no more than one unique author per one day period.
Schema.index( { author: 1, created: 1 } , { unique: true} ) not works, here I can't enter time period.
What better way to decide this case?
const Report = new Schema({
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'DiscordUserList',
required: true
},
reports: [{ reportNum: { type: Number }, text: { type: String }, date: { type: Date, default: Date.now } }],
questionsDone: [{ questionNum: { type: Number }, done: { type: Boolean }, date: { type: Date, default: Date.now } }],
created: {
type: Date,
default: Date.now
}
}, { strict: false })
Report.plugin(mongoosePaginate)
const reportListSchema = mongoose.model('ReportList', Report)
module.exports = reportListSchema

You could do an update operation with $setOnInsert and upsert option i.e if the update() with upsert: true had found a matching document, then MongoDB performs an update, applying the $set operation but ignoring the $setOnInsert operation. Thus your typical update would be as follows:
import moment from 'moment'
const start = moment().startOf('day').toDate() // set to 12:00 am today
const end = moment().endOf('day').toDate() // set to 23:59 pm today
ReportList.findOneAndUpdate(
{
'author': author_id,
'created': { '$gte': start, '$lte': end }
},
{
'$set': { 'author': author_id },
'$setOnInsert': { 'created': moment().toDate() }
},
{ 'upsert': true, 'new': true },
(err, report) => {
console.log(report)
}
)
or using the setDefaultsOnInsert option which when this and upsert are true, mongoose will apply the defaults specified in the model's schema if a new document is created:
ReportList.findOneAndUpdate(
{
'author': author_id,
'created': { '$gte': start, '$lte': end }
},
{ '$set': { 'author': author_id } },
{
'upsert': true,
'new': true,
'setDefaultsOnInsert': true
},
(err, report) => {
console.log(report)
}
)

Related

Searching for all properties of a document using aggregation and project with change in date format in mongoose

My database model looks like
{
email: { type: String, required: true },
name: { type: String, required: true },
projectId: { type: String, required: true },
createdAt: { type: Date, required: false },
updatedAt: { type: Date, required: false },
};
createdAt is of format 2021-10-07T11:16:44.988Z and I wants at the time of fetching data from database date format should be 2022-02-25. so I wrote query to database like this:
const { id } = req.params;
const participantList = await ParticipantModel.aggregate([
{ $match: { projectId: id } },
{ $limit: limitInt },
{ $skip: skipInt },
{
$project: {
Date: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },
},
},
]);
I got result as below:-
{ _id: 6218e52f43ec044180e69b84, Date: '2022-02-25' },
{ _id: 6218e5f543ec044180e69b97, Date: '2022-02-25' },
]
Expected result
[
{ _id: 6218e52f43ec044180e69b84,email:'xyz#gmail.com', name:'xyz',projectId:"01A", Date: '2022-02-25',updatedAt: 2022-02-25T14:18:23.708Z},
{ _id: 6218e5f543ec044180e69b97, email:'abc#gmail.com', name:'abc',projectId:"01B",Date: '2022-02-25' , updatedAt: 2022-02-25T14:21:41.313Z,},
]
How to solve this? Thank you for your help.
In $project guide of mongodb docs, set value to 1 each field if you want to show or 0 to hidden:
const { id } = req.params;
const participantList = await ParticipantModel.aggregate([
{$match:{projectId:id},},{$limit:limitInt},{$skip:skipInt},{$project:{
Date: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } },
email: 1, name: 1, projectId: 1, updatedAt: 1
}}

how to insert into deeply nested element API nodejs mongoose

I have 4 nested documents as follow:
//Nested sub document subControl
const SubControlSchema = new Schema({
subControlNo: {
type: String
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
//Nested sub document control
const ControlSubSchema = new Schema({
mainControl: {
type: String
},
subControls: [SubControlSchema],
controlDescription: {
type: String,
trim: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
//Nested sub document domain
const DomainSubSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
domainNo: {
type: String,
trim: true
},
domainName: {
type: String,
trim: true
},
domainDescription: {
type: String,
trim: true
},
controls: [ControlSubSchema],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
// framework Schema
const FrameworkSchema = new Schema({
name: {
type: String,
trim: true
},
description: {
type: String,
trim: true
},
regulator: {
type: Schema.Types.ObjectId,
ref: 'Regulator',
default: null
},
client: {
type: Schema.Types.ObjectId,
ref: 'Client',
default: null
},
domains: [DomainSubSchema],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Framework', FrameworkSchema);
I'm trying to post a control under the domain which is inside the framework, here's what I have been trying to do:
//Add new control under a specific domain and framework
router.post('/add/:frameworkId/:domainId', auth, async (req, res) => {
try {
const control = req.body.controls; //take the request from the body
const query = { _id: req.params.frameworkId, _id: req.params.domainId };//pushing into the framework model by taking the ID from URL
await Framework.updateOne(query, { $push: { domains: control } }).exec(); //push the query into the framework model
res.status(200).json({
success: true,
controls: control
});
} catch (error) {
res.status(400).json({
// error: 'Your request could not be processed. Please try again.'
error
});
}
});
Data posted in postman:
Link: http://localhost:3000/api/framework/add/6233277f411377367f8ad1c0/6233277f411377367f8ad1c1
{
"controls":
{
"mainControl": "1-5",
"subControls": [{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}],
"controlDescription": "controlDescriptionTest"
}
}
Response:
{
"success": true,
"controls": {
"mainControl": "1-5",
"subControls": [
{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}
],
"controlDescription": "controlDescriptionTest"
}
}
Problem: I'm not getting any new data in mongodb , any idea if I'm approaching this the correct way? I'm guessing the data is posted correctly and It's a problem with saving it to the database
Picture of my schema: I want to be able to add elements under the controls:
First if you want your code to insert and not update you should use insertOne and not updateOne, regarding an "update" operation I can see 2 potential "issues" here:
req.params.frameworkId and req.params.domainId come as string type. And I assume the _id field is type ObjectId and not string.
To fix this you just need to cast it to the proper type, like so:
import { ObjectId } from 'mongodb';
...
{ _id: new ObjectId(req.params.frameworkId) }
Both parameters are "querying" the same field (_id), unless this is intentional somehow if these values are different it will never find a document to match, this should be changed.
Lastly if you want to update an existing object if exists, and if not insert then you should use updateOne with the upsert option:
await Framework.updateOne(query, { $push: { domains: control } }, { upsert: true }).exec();

How to Update many elements in mongoose array which has embedded documents

I have this mongoose model
resourceId: {
type: String,
},
resourceName: {
type: String,
},
dateAndValue: [
{
date: { type: Date },
value: { type: Number },
},
],
project: {
type: mongoose.Schema.Types.ObjectId,
ref: 'project',
},
I want to update all value fileds of dateAndValue array elemets to "0" of a given resourceId, given project within a given date range!
await QuantumResourcesManpowerAdmin.updateMany(
{
project,
resourceId,
'dateAndValue.date': { $gte: startDate, $lte: endDate },
},
{
$set: {
'dateAndValue.$.value': 0,
},
},
{ upsert: true }
);
res.status(200).json({ success: true });
This is the code I used for it. It returns success but does not do any update.
All inputs are correct, something is wrong with only the updateMany query, all other functions work!
await QuantumResourcesManpowerAdmin.updateMany(
{
project,
resourceId,
'dateAndValue.date': { $gte: startDate, $lte: endDate },
},
{
$set: {
'dateAndValue.$[element].value': 0,
},
},
{arrayFilters:[{'element.date':{$gte: startDate, $lte: endDate }}] ,upsert: true }
);
res.status(200).json({ success: true });

nodejs / mongoDb: update many fields in one request

how can I update many orderStatus instead of only one?
request.body.type is by default string and contains only one type;
and when isCompleted for the type go true I want even for previous enum index isCompleted go true
is it possible or do I need to modify it in the front-end?
here is the code
const orderSchema = new mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
orderStatus: [
{
type: {
type: String,
enum: ["ordered", "packed", "shipped", "delivered"],
default: "ordered",
},
date: {
type: Date,
},
isCompleted: {
type: Boolean,
default: false,
},
},
],
}
exports.updateOrder = (req, res) => {
Order.updateOne(
{ _id: req.body.orderId, "orderStatus.type": req.body.type },
{
$set: {
"orderStatus.$": [
{ type: req.body.type, date: new Date(), isCompleted: true },
],
},
}
).exec((error, order) => {
Hey You can use updateMany() operation
db.collection.updateMany(
<query>,
{ $set: { status: "D" }, $inc: { quantity: 2 } },
...
)

Mongoose aggreagate with group not working

In my Node.JS API, it is possible to order and get menus. The structure of an ordered menu looks like the following (the main schema is the orderMenuSchema; menuItemSchema is for the subdocument-array with ordered items):
var menuItemSchema = mongoose.Schema({
itemId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
prepared: {
type: Boolean,
default: false
},
finished: {
type: Boolean,
default: false
},
timestamp: {
type: Date,
default: Date()
}
}, {_id: false})
var orderMenuSchema = mongoose.Schema({
orderId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
menuId: {
type: mongoose.Schema.Types.ObjectId,
required: true
},
items: {
type: [menuItemSchema],
required: true,
validate: menuItemsCheck
},
finished: {
type: Boolean,
default: false
},
timestamp: {
type: Date,
default: Date()
}
})
Example Data:
{
"_id":"5d2333a1841a0e4ef05873d0",
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"menuId":"5d189ffdbe02ef0b00b22370",
"items":[
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236d"
},
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236e"
},
{
"prepared":false,
"finished":false,
"timestamp":"2019-07-08T12:14:04.000Z",
"itemId":"5d189ffdbe02ef0b00b2236f"
}
],
"orderId":"5d2333a1841a0e4ef05873c3",
"__v":0
}
Whether an item is prepared or not is stored in the prepared field of the menuItem.
Each menu has multiple items to choose from, and the user is able to have only some items - that's why the orderMenuSchema has an array of subdocuments called "items" in which only the ordered items are stored.
Now I would like to get all unprepared menus, group them by the menuID
and then group them by the itemID - everything with a Mongoose
aggregation.
So, I think I need two groupings: The first one by the menuId, the second one by the itemId.
Furthermore, I would like to know how many of each item are unprepared - so after grouping by the menuId, I need to get a count of all unprepared items
Expected Output:
I thought of something like this:
{
"result":[
{
"menuID":"tastefulMenu123",
"items":[
{
"itemId":"noodlesoop123",
"unpreparedCount":13
},
{
"itemId":"tastyBurger123",
"unpreparedCount":2
},
{
"itemId":"icecoldIce123",
"unpreparedCount":20
}
]
}
]
}
There will be an array of subdocuments, one subdocument for each menuId. Each subdocument than has an array of items in which the itemID as well as the unpreparedCount are stored.
What I already tried (not working):
OrderMenu.aggregate([
{$unwind: "$items"},
{ $project: { prepared: 1, itemId: 1} },
{ $match: {
prepared: false,
timestamp: {
$gte: today,
$lt: tomorrow
}
}},
{ $group: {
_id: {menuId: '$menuId', itemId: '$itemId'},
count: { $sum: 1 }
}}
]).then(result => {
console.log(result)
return Promise.resolve(result)
}).catch(error => {
console.log(error)
return Promise.reject(500)
})
Any help would be appreciated!

Resources