how to insert multiple JSON objects to one property of a schema? - node.js

I have a requirement to store multiple JSON objects in a property of schema.
take this example...
const Schema = require("mongoose").Schema;
const Student= Schema({
student_id: String,
name:String
attendance:[
{
date: Date,
Status: String
}
]
});
I need to insert attendance of individual student which looks like this..
student_id: student_001,
name:'Joe'
attendance:[
{
date: 24-10-2018,
status: 'Present'
},
{
date: 25-10-2018,
status: 'Absent'
},
//list goes on
]
I am using NodeJs as Backend, EJS template as front end and mongodb database. Date and Status comes when user submits data from front end. So I am having hard time writing my post request. Any types of comments / suggestions / change of model structure are welcome. Thank you.

You can create a separate attendance Schema.
const Schema = require("mongoose").Schema;
const AttendanceSchema = new Schema({
date: Date,
status: String
});
const StudentSchema = new Schema({
student_id: String,
name:String
attendance:[AttendanceSchema]
});
const Student = mongoose.model('Student', StudentSchema);
Add a new Student.
let newStudent = new Student({
student_id: student_001,
name:'Joe'
});
newStudent.save();
Update attendance:
let att1 = {
date: 24-10-2018,
status: 'Present'
};
// Here 'id' is id of particular student.
Student.update({ _id: id }, { $push: { attendance: att1 } })
.then(() => console.log("Success"))
.catch(err => console.log(err));
At some point later:
let att2 = {
date: 25-10-2018,
status: 'Absent'
};
// Here 'id' is id of particular student.
Student.update({ _id: id }, { $push: { attendance: att2 } })
.then(() => console.log("Success"))
.catch(err => console.log(err));

I suggest you to change the model structure to be normalized.
This will improve your experience in future statistics querying.
Also, one more suggestion - do not use string indentifiers in mongoDB, this can cause a headache in maintaining their uniqueness. Mongo has automated _id property assigning to each document, you could use it if you need to indentify any object.
Considering my suggestions - the code will look like:
const Schema = require("mongoose").Schema;
const Student = Schema({
name: String
});
const Attendance = Schema({
date: Date,
status: String,
student_id: {
type: Schema.Types.ObjectId,
ref: 'Student'
}
})
Then, you could simply create attendance records assigned to the student :
const attendance = new AttendanceModel({
date: new Date('05/20/2018'),
status: "present",
student_id: "somestudentid"
});

Related

How do I add expenses for only specific users?

I have created two models in my app- one for User (_id, email, username, password) and one for Expense (_id, date, detail, amount, category). For the users, I have finished the authentication with jwt.
I want logged-in users to be able to add/remove expenses and not show their expenses to other users but I don't know how I can implement that. I am not asking for code- I would be grateful if you could roughly tell me what I need to do. Thanks!
//expense schema
const expenseSchema = new mongoose.Schema(
{
date: Date,
detail: String,
amount: Number,
category: String
}
)
//controller for adding expenses
const addExpenseController = (req, res) => {
const expense = new Expense({
"date": new Date(),
"amount": req.body.amount,
"detail": req.body.detail,
"category": "expense"
});
expense.save();
res.send('expense added');
};
You should define a ref property in the expense schema pointing at the User model (change the value of the ref attribute to equal the model name given to the users):
const expenseSchema = new mongoose.Schema(
{
...
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}
)
Then, on creation, specify the user by setting the value of its _id.
You can either store it in the session or pass it in the body, depending on your implementation:
const addExpenseController = async (req, res) => {
try {
const expense = new Expense({
date: new Date(),
amount: req.body.amount,
detail: req.body.detail,
category: 'expense',
user: req.session.user_id, // or req.body.user_id
});
await expense.save();
res.send('expense added');
} catch (err) {
res.send('server error');
}
};

Is it possible to populate data with respect to other fields not _id in mongoose

All of the time populating data booking_unique_id is giving me null.
Here is the schema:
const chat = require('../models/chat.model')
const booking_details = new Schema({
booking_unique_id:{type:Object,ref:chat,field:'chat_screen_id'}
});
const chat_details = new Schema({
...
receiver_public_name:{type:String}
chat_screen_id:{type:Object}
});
Booking.find({booking_status:'e'}).populate('booking_unique_id'))
Currently it is not supporting in ref populate, there are issues in Mongoose Issue-3225, and Issue-1888,
For the alternative they have published populate-virtuals,
chat schema
const chat_details = new Schema({
...
receiver_public_name: { type: String }
chat_screen_id: { type: Object }
});
booking schema
const chat = require('../models/chat.model');
const booking_details = new Schema({
booking_unique_id: { type: Object }
});
booking virtual
booking_details.virtual('bookings', {
ref: chat, // The model to use
localField: 'booking_unique_id', // Find booking where `localField`
foreignField: 'chat_screen_id', // is equal to `foreignField`
// Query options, see /mongoose-query-options
// options: { sort: { name: -1 }, limit: 5 }
});
booking model
const Booking = mongoose.model('Booking', booking_details);
booking find query with populate
Booking.find({ booking_status: 'e' }).populate('bookings').exec(function(error, result) {
console.log(result);
});

How to create a document with the data from another collection?

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CompanySchema = new Schema(
{
companyName: {
type: String,
required: true,
unique: true
},
taxOffice: {
type: String
},
taxNumber: {
type: String
},
},
{
timestamps: true
}
);
const Company = mongoose.model('Company', CompanySchema);
module.exports = Company;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const DateSchema = new Schema({
name: {
type: String,
unique: true,
required: true
},
companies: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Company' }]
});
const Date = mongoose.model('Date', DateSchema, 'dates');
module.exports = Date;
const router = require('express').Router();
const Date = require('../models/date');
router.route('/').get((req, res) => {
Date.find()
.then(dates => res.json(dates))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/add').post((req, res) => {
const name = req.body.name;
const newDate = new Date({ name });
newDate
.save()
.then(() => res.json('Date added!'))
.catch(err => res.status(400).json('Error: ' + err));
});
module.exports = router;
I have 2 collections called Company and Date.
I inserted many data to Company Collection.
But I want that Company data(companies) to copied into Date Collection whenever I create a Date document.
I want to store company data as an array for each Date.
By the way don't know that my schema design is correct for the purpose. What should I do?
I want to have a Date document like:
{
name: "DECEMBER-2019",
companies: ['5e2076236664640d22515f7b', '5e2076236664640d22515f7a']
}
It sounds like you want to take an action whenever you create a new document in the Date collection. If you are using Atlas (MongoDB's fully managed database as a service), you can configure a Trigger to fire whenever a new document is inserted into the Date collection. Another option (regardless of whether you are using Atlas or not) is to use Change Streams to monitor changes in the Date collection. For more on how to configure these, see https://www.mongodb.com/blog/post/node-js-change-streams-and-triggers.
Assuming that only companies present at the time of Date Object creation should be added in Date's companies field, Then you can maintain a cache of companies ObjectIds at any point of time which will be updated for every delete/insert in Company Collection.
You can write your own function of creating a date object which will have parameter name which add the current companies ObjectIds.
Date.statics.getNewDateObject = function(name) {
let companyIds = await getCachedCompanyIds();
return new Date({name: name, companies: companyIds});
}
This will have a document like:
{
name: "TODAY'S DATE",
companies: ['5e2076236664640d22515f7b', '5e2076236664640d22515f7a']
}
If you want to populate the Date object with complete Company Information, You can use the populate method present in mongoose which populates the complete company information into the Date Object.
https://mongoosejs.com/docs/populate.html
I solved it:
router.route('/add').post((req, res) => {
const name = req.body.name;
Company.find().then(companies => {
const newDate = new Date({ name, companies });
newDate
.save()
.then(() => res.json('Date added!'))
.catch(err => res.status(400).json('Error: ' + err));
});
});

$match _id is not working in Mongoose aggregate function

As my title, $match _id is not working in Mongoose aggregate function.
Could somebody please help me?
Is this related to mongoose version?
I use 4.9.2.
I need to use aggregate because I will group by the result after processing the $match.
I already saw posts before, but manually casting didn't work for me!
Here is my schema:
var mongoose = require("mongoose");
var moment = require("moment");
var Schema = mongoose.Schema;
var AvgDailyCharging = new Schema({
_id : {
date: Date,
storeID: {
type: Schema.Types.ObjectId,
ref: 'store'
}
},
chargers: [{
transmitterID: {
type: Schema.Types.ObjectId,
ref: 'device'
},
minutes: Number
}],
});
mongoose.model('AvgDailyCharging', AvgDailyCharging);
And here is the query:
var Mongoose = require('mongoose');
var Model = require('../db/model');
var Query = require('../db/query');
var RESULT_LIMIT = 2000; // Limit the return data size
exports.getAvgDailyCharging = function(req, res) {
var id = new Mongoose.Types.ObjectId("58b43fdf0fd53910121ca6f4");
var query = new Query("AvgDailyCharging");
query.aggregate([
{
$match: {
"_id.storeID": id, //HELP!!!!!
"_id.date": { //match only by this works fine.
$gte: new Date(req.params.startTime),
$lt: new Date(req.params.endTime)
}
}
}
]).exec(function(error, data) {
if (error) {
res.send({result:'ERROR', message: error});
} else {
res.send(data);
}
});
}
Please help me!!!! I was stuck for several hours! Q_Q
When I was testing in mongoose version 4.4.4, both type casting and string didn't work. However, after I update it to the version 4.9.2, type casting is no needed, and directly using a string in $match _id works!
Update:2017-03-31
I think another problem in my scenario is my schema definition. Since this collection, say A, is created from another one, say B, using $group: { _id: { storeID: "$storeID" } } where the storeID field in collection B is of type ObjectId, then in collection A I find out that _id.store is actually a String not an ObjectId, so the best way is to change the schema I mentioned in the question to:
var AvgDailyCharging = new Schema({
_id : {
date: Date,
storeID: String
},
chargers: [{
transmitterID: {
type: Schema.Types.ObjectId,
ref: 'device'
},
minutes: Number
}],
});

Finding Mongoose subdocument by time updated

I have two Mongoose Schemas:
var ItemSchema = new Schema({
trade: {
type: Schema.Types.ObjectId,
ref: 'Trade'
}
});
var Item = mongoose.model('Item', ItemSchema);
and
var TradeSchema = new Schema({
expiresOn: {
type: Date
}
});
var Trade = mongoose.model('Trade', TradeSchema);
I am trying to use Item.find() to find a item if its trade date is less than the date the user passes in via the query string in the request. I'm using the following code:
if (req.query.expiresBefore) {
Item.find({
'trade.expiresOn': {
$lte: req.query.expiresBefore
}
}, function (err, trades) {
console.log(trades)
})
}
However, I am receiving an empty array on the console.log() call. Using $gte also returns an empty array (though my research tells me that I need to use $lte for my purposes). What do I need to do to find the item document by matching the property (expiresOn) of its child (Trade)?
Referring to this one Stackoverflow question, what you want to do is not possible.I am not sure why you set the schemas like this, but if you want to keep them as they are. I would suggest you make a little change like the following
var ItemSchema = new Schema({
trade: {
type: Schema.Types.ObjectId,
ref: 'Trade'
}
});
var Item = mongoose.model('Item', ItemSchema);
and
var TradeSchema = new Schema({
itemId: { //add this
type: Schema.Types.ObjectId,
ref: 'Item'
},
expiresOn: {
type: Date
}
});
var Trade = mongoose.model('Trade', TradeSchema);
if (req.query.expiresBefore) {
Trade.
find({
'expiresOn': {
$lte: req.query.expiresBefore
}
}).
populate('itemId').
exec(function (err, trades) {
console.log(trades)
});
}

Resources