Send nested object on GET - javascript

I have a very basic schema which has another object called Vehicle, inside
let rentSchema = new Schema({
code: {
type: Number
},
vehicle: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Vehicle'
},
ongoing: {
type: Boolean,
default: false
}
}, {collection: 'RentCollection'});
Find all in the controller
exports.getRent = function (req, res) {
// Find in the DB
rentSchema.find({}, function (err, rent) {
if (err) res.status(400).send(err);
res.json(rent);
});
};
The response comes as an array of Rents but Vehicle object is missing from the Object Rent. Why is that?
_id: "5e04c19d0a0a100f58bd64b5"
__v: 0
ongoing: false

Here is step by step explanations to make it work:
1-) First you need to create a model and export it like this:
rent.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let rentSchema = new Schema(
{
code: {
type: Number
},
vehicle: {
type: mongoose.Schema.Types.ObjectId,
ref: "Vehicle"
},
ongoing: {
type: Boolean,
default: false
}
},
{ collection: "RentCollection" }
);
module.exports = mongoose.model("Rent", rentSchema);
2-) Let's say you have this Vehicle model:
vehicle.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
let vehicleSchema = new Schema(
{
name: String
},
{ collection: "VehicleCollection" }
);
module.exports = mongoose.model("Vehicle", vehicleSchema);
3-) First let's create a vehicle like this in the VehicleCollection like this:
{
"_id": "5e0f465205515667746fd51a",
"name": "Vehicle 1",
"__v": 0
}
4-) Then let's create a rent document in RentCollection using this vehicle id like this:
{
"ongoing": false,
"_id": "5e0f46b805515667746fd51d",
"code": 1,
"vehicle": "5e0f465205515667746fd51a",
"__v": 0
}
5-) Now we can use the following code, to populate the vehicle with the rents.
const Rent = require("../models/rent"); //todo: change to path to the rent.js
exports.getRent = function(req, res) {
Rent.find({})
.populate("vehicle")
.exec(function(err, rent) {
if (err) {
res.status(500).send(err);
} else {
if (!rent) {
res.status(404).send("No rent found");
} else {
res.json(rent);
}
}
});
};
6-) The result will be:
[
{
"ongoing": false,
"_id": "5e0f46b805515667746fd51d",
"code": 1,
"vehicle": {
"_id": "5e0f465205515667746fd51a",
"name": "Vehicle 1",
"__v": 0
},
"__v": 0
}
]

You will have to use the populate method to populate a vehicle object.
From docs:
rentSchema.
findOne({}).
populate('vehicle').
exec(function (err, obj) {
if (err) return handleError(err);
console.log(obj);
});
Also in your current code, you havent setted up model:
RentCollection = mongoose.model('RentCollection', rentSchema);

Related

Problem with getting average rating from mongodb in node.js express

I have a node.js express back-end with a mongodb database on the cloud(mongodb atlas).
This is the Post object and I want to get the average grade from reviewerComments and userComments.
import mongoose from 'mongoose'
const postSchema = mongoose.Schema({
game: {
},
reviewerComments: [
{
author: String,
authorEmail: String,
avatar: String,
title: String,
text: String,
grade: Number,
date: String
}
],
userComments: [
{
author: String,
authorEmail: String,
text: String,
grade: Number,
date: String
}
]
}, {
timestamps: true
})
const Post = mongoose.model('Post', postSchema)
export default Post
I have a API route that calls this function
export const getAverageGrades = async (req, res) => {
const { id } = req.query
try {
const post = await Post.aggregate([
{
$match:
{
_id: mongoose.Types.ObjectId(`${id}`)
}
},
{
$unwind: "$reviewerComments"
},
{
$group:{
_id:{
"comment_id": "$reviewerComments.id",
"title": "$reviewerComments.title",
"grade": "$reviewerComments.grade",
},
avg_rating: {$avg:"reviewerComments.grade"}
}
},
{
$project:{
"id": "$_id.rating_id",
"title": "$_id.title",
"avg_rating": "$avg_rating" // avg_rating returns null
}
}
])
res.status(200).json(post);
} catch (error) {
console.log(error.message)
res.status(404).json({ message: error.message });
}
}
This function is returning a response where avg_rating is null, what am I doing wrong?

Push To Mongoose Subdocuments

Hello I am creating a series of groupings describing the roles certain users are taking within the context of helping a client. The object in the Prospect model is called caseworkers. In caseworkers is a series of arrays for the different types of roles done. The equation is to allow the user to push his info as a subdocument called CaseWorker. Basically creating an object with 6 arrays that users can push to. Ive tried a few things and settled on Subdocuments. Any help would be awesome.
Here is my code:
const mongoose = require("mongoose");
const CaseWorker = require("./CaseWorker");
const ProspectSchema = mongoose.Schema({
caseWorkers: {
originators: [CaseWorker.schema],
loanProcessors: [CaseWorker.schema],
documentProcessors: [CaseWorker.schema],
upsells: [CaseWorker.schema],
primaryReso: [CaseWorker.schema],
taxPreparers: [CaseWorker.schema],
secondaryReso: [CaseWorker.schema],
}
module.exports = mongoose.model("prospect", ProspectSchema);
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CaseWorkerSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
role: { type: String },
resoCred1: { type: String },
resoCred2: { type: String },
reminders: [
{
_id: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
userReminded: { type: mongoose.Schema.Types.ObjectId },
reminderDate: { type: Date },
reminderDueDate: { type: Date },
status: { type: String },
daysTilDue: { type: Number },
id: { type: String },
text: { type: String },
clientId: { type: mongoose.Schema.Types.ObjectId, ref: "Prospect" },
},
],
});
module.exports = mongoose.model("caseWorker", CaseWorkerSchema);
router.put("/:_id/caseWorkers/loanProcessors", auth, async (req, res) => {
const prospect = await Prospect.findByIdAndUpdate(req.params._id, {
"$push": {
"loanProcessors": {
"caseWorker": {
"name": req.body.name,
"email": req.body.email,
"role": req.body.role,
"resoCred1": req.body.resoCred1,
"resoCred2": req.body.resoCred2,
},
},
},
});
res.json(prospect);
console.log(prospect);
});
In your approach when updating the document you put caseWorker under loanProcessors but it's declared in the schema the other way around.
To update a nested object you have to use the dot notation to reference the field.
Don't forget to put the object key that represent the field as a string like this "caseWorkers.loanProcessors", because caseWorkers.loanProcessors is an invalid object key in javascript
"$push": {
"caseWorkers.loanProcessors": {
"name": req.body.name,
"email": req.body.email,
"role": req.body.role,
"resoCred1": req.body.resoCred1,
"resoCred2": req.body.resoCred2,
},
},

Information isn't being passed to an array via Mongoose, can't work out why

Apologies if this has been answered before, I have checked other answers and can't work it out from those.
I have a set of information that I would like placed into an array named "teamDetails". Here is the relevant /post item from server.js:
app.post('/create', (req, res) => {
console.log('Post command received');
console.log(req.body);
console.log(req.body.data.teamDetails[0]);
//We need to push the variable below, 'teamDetails', as an object into an array of the same name
var teamDetailsObj = {
// Modified for Postman
"teamName": req.body.data.teamDetails[0].teamName,
"teamNameShort": req.body.data.teamDetails[0].teamNameShort,
"teamfounded": req.body.data.teamDetails[0].teamFounded,
"teamHome": req.body.data.teamDetails[0].teamHome
};
console.log(teamDetails);
var newTeam = new Team({
"data.added": new Date(),
"data.entry": req.body.data.entry
});
newTeam.save().then((doc) => {
console.log("This is newTeam.data: " + newTeam.data);
console.log("This is teamDetailsObj: " + teamDetailsObj);
newTeam.data.teamDetails.push(teamDetailsObj);
var teamId = doc.id;
res.render('success.hbs', {teamId});
console.log("Team Added - " + teamId);
}, (e) => {
res.status(400).send(e);
});
});
Here is my team.js model:
var mongoose = require('mongoose');
var ObjectID = mongoose.Schema.Types.ObjectId;
var Mixed = mongoose.Schema.Types.Mixed;
var Schema = mongoose.Schema;
var Team = mongoose.model('Team', {
data: {
entry: {
type: String,
default: "USER.INPUT"
},
added: {
type: Date,
default: Date.Now
},
teamDetails: [
{
teamName: {
type: String,
trim: true,
required: true,
default: "First Team"
},
teamNameShort: {
type: String,
trim: true,
uppercase: true,
maxlength: 3,
required: true
},
teamFounded: {
type: Number,
maxlength: 4
},
teamHomeCity: {
type: String
}
}
]
}
});
module.exports = {Team};
Lastly, the sample data I'm trying to inject via Postman:
{
"data": {
"entry": "Postman.Entry",
"teamDetails": [
{
"teamName": "Test Teamname",
"teamNameShort": "TTN",
"teamFounded": "1986",
"teamHome": "London",
"players": [
{
"player1Name": "Test Player 1",
"player1Position": "Forward",
"player1Nationality": "GBR"
},
{
"player2Name": "Test Player 2",
"player2Position": "Defender",
"player2Nationality": "UKR"
},
{
"player3Name": "Test Player 3",
"player3Position": "Goaltender",
"player3Nationality": "IRL",
"captain": true
}
],
"coachingStaff": {
"headCoach": "Derp McHerpson",
"teamManager": "Plarp McFlarplarp"
}
}
]
}
}
(Disregard the players section, it's another kettle of fish)
As a result of using my code above, the resulting entry for teamDetails is just an empty array. I just can't get my code to push the teamDetailsObj into it.
Any help anyone can provide is appreciated.
It looks like you add teamObjDetails AFTER saving it with newTeam.save().then( ... )
I'm not a lot familiar with Mongoose but I don't see how could the team details could be present if not added before saving.
Let me know if it changes something !
A. G

How to remove an element from an array within Mongo

I'm completely stuck with Mongoose and remove method.
I have a page with comments and form with a button Delete. My goal is to delete only that comment which was clicked. Below is my MongoDB file (By the way I use method override of the express library to handle both request post and delete).
{
"_id": {
"$oid": "5a455cf460414f548f3d1afb"
},
"title": "Tets",
"body": "tes",
"user": {
"$oid": "5a440bae124b7e4626aeeb70"
},
"date": {
"$date": "2017-12-28T21:07:00.194Z"
},
"comments": [
{
"commentBody": "ets",
"commentUser": {
"$oid": "5a440bae124b7e4626aeeb70"
},
"_id": {
"$oid": "5a455cf660414f548f3d1afc"
},
"commentDate": {
"$date": "2017-12-28T21:07:02.143Z"
}
}
],
"allowComments": true,
"status": "public",
"__v": 1
}
my Schema
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
//Create Schema
const StorySchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
status: {
type: String,
default: 'public'
},
allowComments: {
type: Boolean,
default: true
},
comments: [{
commentBody: {
type: String,
required: true
},
commentDate: {
type: Date,
default: Date.now
},
commentUser: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}],
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
date: {
type: Date,
default: Date.now
}
});
mongoose.model('stories',StorySchema, 'stories');
And my JS file ,my post method works exactly how I wish but delete doesn't work at all (Cannot read property 'comments' of undefined)
router.post('/comment/:id' , (req , res) => {
Story.findOne({
_id: req.params.id
})
.then(story => {
const newComment = {
commentBody: req.body.commentBody,
commentUser: req.user.id
}
//Push to comments array
story.comments.unshift(newComment);
story.save()
.then(story => {
res.redirect(`/stories/show/${story.id}`)
})
});
})
router.delete('/comment/:id', (req, res) => {
Story.remove({
_id: req.body.id.comments
})
.then(() => {
req.flash('success_msg', 'Comments Removed!');
res.redirect('/dashboard');
})
});
here is my handlebars file with form
{{#each story.comments}}
<form action="/stories/comment/{{id}}?_method=DELETE" method="post" id="delete-form">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="btn red"><i class="fa fa-remove"></i> Delete</button>
</form>
{{/each}}
The error I got
TypeError: Cannot read property 'comments' of undefined
at router.delete (/Users/ar2z/Desktop/fierce-caverns-70427/routes/stories.js:197:20)
Help me please. I'm completely lost.
I actually encountered the same error recently. I have found you need to do this:
router.delete("/comments/:id", function(req,res){
var Model = require("myModel");
//Run a find and update query to delete the comment
Model.findOne({_id:req.params.id}, function(err,doc){
if(doc && !err){
doc.comments = doc.comments.filter(function(comment){
//This will filter out the comment you want to delete
return comment._id != req.body.commentId
})
}
res.redirect("/comments/")
})
}
My fault was from the beginning when I was trying work with array element the same way as i'm working with Mongo object
Story.remove({
_id: req.body.id.comments
})
Code above is not working for array element it's working with object but for delete element from array I use:
Story.update( { }, { $pull: { comments: { _id: req.params.id }}}, { multi: true } )
This code Remove Items from an Array of Documents
$pull MongoDb documentation

Merge collections to get average rating, but still get all of original collection before a rating has been given. Mongoose / nodejs

I have an admin that creates an item (a bourbon), that users can give a comment on and rating within that comment. I was able to aggregate the comments, but was not able to display newly-created bourbons, only those already seeded that already had ratings. i've tried to implement something similar to this thread: referenced thread, but i'm am not doing something correct.
I'm a noob, and mainly play in the frontend, and am very confused about how I should change this sample code into actual production code. I see what each function is doing, but still fuzzy on it.
Should I do an async.each and set the aggregate function as the iterator..? I know this is busted. I've tried a few things now. Keep getting a 500 error, nothing on the console.log. Any help for this noob, much appreciated.
Here are my schemas:
bourbon:
'use strict';
var mongoose = require('mongoose'),
BourbonSchema = null;
module.exports = mongoose.model('Bourbon', {
name: {type: String, required: true},
blog: {type: String, required: true},
photo: {type: String, required: true, default:'http://aries-wineny.com/wp-content/uploads/2014/09/woodford-reserve.jpg'},
location: {type: String, required: true},
distillery: {type: String, required: true},
avgRating: {type: Number}
});
var Bourbon = mongoose.model('Bourbon', BourbonSchema);
module.exports = Bourbon;
comments:
'use strict';
var mongoose = require('mongoose');
module.exports = mongoose.model('Comment', {
bourbonId: {type: mongoose.Schema.ObjectId, ref: 'Bourbon'},
userId: {type: mongoose.Schema.ObjectId, ref: 'User'},
text: {type: String, required: true},
createdAt: {type: Date, required: true, default: Date.now},
rating : {type: Number, required: true},
votes: {type: Number, default: 0}
});
this is my find / get in the controller, which I tried piecing together from the referenced link, but is slopp now:
'use strict';
var Bourbon = require('../../../models/bourbon'),
Comment = require('../../../models/comment'),
DataStore = require('nedb'),
db = new DataStore(),
async = require('async');
module.exports = {
description: 'Get Bourbons',
notes: 'Get Bourbons',
tags: ['bourbons'],
handler: function(request, reply){
async.waterfall(
[
function(comment,callback){
async.series(
[
function(callback){
Bourbon.find({},function(err,results){
async.eachLimit(results,10,function(result,callback){
db.insert({
'bourbonId': result._id.toString(),
'location' : result.location,
'blog' : result.blog,
'distillery': result.distillery,
'name': result.name,
'avgRating': 0
},callback);
},callback);
});
},
function(callback){
Comment.aggregate(
[
{'$group':{
'_id': '$bourbonId',
'avgRating':{
'$avg':'$rating'
}
}}
],
function(err,results){
async.eachLimit(results,10,function(result,callback){
db.update(
{'bourbonId': result._id.toString()},
{'$set':{
'avgRating': result.avgRating
}
},
callback
);
},callback);
}
);
}
],
function(err) {
if (err) callback(err);
db.find({},{'_id': 0},callback);
}
);
}
],
function(err,results){
reply({results: results});
console.log('LOOOOOOOOOOOOOOOOK',JSON.stringify(results, undefined, 4));
process.exit();
});
}
};
Seems you have more to learn about callbacks and sequencing. This is all the code inside your request handler that you need. Of course changing to send the response when you see what is happening.
async.series(
[
// List out exiting collection with 0 average
function(callback) {
Bourbon.find({},function(err,results){
async.eachLimit(results,10,function(result,callback){
var plain = result.toObject()
plain.bourbonId = plain._id.toString();
plain.avgRating = 0;
delete plain._id;
db.insert(plain,callback); // next each iteration
},callback); // move to next in series
});
},
// Get aggregate results and update the items you just wrote
function(callback) {
Comment.aggregate(
[
{ '$group': {
'_id': '$bourbonId',
'avgRating':{ '$avg':'$rating' }
}}
],
function(err,results) {
async.eachLimit(results,10,function(result,callback){
db.update(
{ 'bourbonId': result._id.toString() },
{'$set': {'avgRating': result.avgRating } },
callback // next each iteration
);
},callback); // next in series "last"
}
);
}
],
// This is called when both in the series are complete
function(err) {
if (err) callback(err);
db.find({},{'_id': 0},function(err,docs) {
console.log( docs );
});
}
);
The aim here is:
Put 0 values in a hash table ( here using nedb ) for all items
Get aggregation results from the other collection
Update the copy of all items with those items that actually have a value
When all is done then you read back your hash table
Full working example:
var async = require('async'),
mongoose = require('mongoose'),
DataStore = require('nedb'),
db = new DataStore(),
Schema = mongoose.Schema;
var userSchema = new Schema({
"name": String
});
var ratingSchema = new Schema({
"bourbonId": { "type": Schema.Types.ObjectId, "ref": "Bourbon" },
"userId": { "type": Schema.Types.ObjectId, "ref": "User" },
"rating": { "type": Number, "required": true }
});
var bourbonSchema = new Schema({
"name": { "type": String, "required": true },
"blog": { "type": String, "required": true },
"photo": { "type": String, "required": true },
"ratings": [{ "type": Schema.Types.ObjectId, "ref": "Rating" }],
"rating": { "type": Number }
});
var User = mongoose.model( "User", userSchema ),
Rating = mongoose.model( "Rating", ratingSchema ),
Bourbon = mongoose.model( "Bourbon", bourbonSchema );
mongoose.connect("mongodb://localhost/bourbon");
async.waterfall(
[
function(callback) {
async.each([User,Rating,Bourbon],function(model,callback) {
model.remove({},callback);
},
function(err) {
callback(err);
});
},
function(callback) {
Bourbon.create({
"name": 'test',
"blog": 'test',
"photo": 'test'
},callback);
},
function(bourbon,callback) {
Bourbon.create({
"name": 'another',
"blog": 'another',
"photo": 'another'
},callback);
},
function(bourbon,callback) {
User.create({ "name": 'ted' },function(err,user) {
if (err) callback(err);
Rating.create({
"bourbonId": bourbon,
"userId": user,
"rating": 5
},function(err,rating1) {
callback(err,user,bourbon,rating1)
});
});
},
function(user,bourbon,rating1,callback) {
Rating.create({
"bourbonId": bourbon,
"userId": user,
"rating": 7
},function(err,rating2) {
callback(err,bourbon,rating1,rating2);
});
},
function(bourbon,rating1,rating2,callback) {
Bourbon.findById(bourbon.id,function(err,bourbon) {
bourbon.ratings.push(rating1,rating2);
bourbon.save(function(err,bourbon) {
callback(err)
});
});
},
function(callback) {
async.series(
[
function(callback) {
Bourbon.find({},function(err,results) {
if (err) callback(err);
async.eachLimit(results,10,function(result,callback) {
var plain = result.toObject();
plain.bourbonId = plain._id.toString();
plain.avgRating = 0;
delete plain._id;
db.insert(plain,callback);
},callback);
});
},
function(callback) {
Rating.aggregate(
[
{ "$group": {
"_id": "$bourbonId",
"avgRating": { "$avg": "$rating" }
}}
],
function(err,results) {
if (err) callback(err);
async.eachLimit(results,10,function(result,callback) {
db.update(
{ "bourbonId": result._id.toString() },
{ "$set": { "avgRating": result.avgRating } },
callback
);
},callback);
}
);
}
],
function(err) {
if (err) callback(err);
db.find({},{ '_id': 0 },callback);
}
);
}
],
function(err,results) {
if (err) throw err;
console.log( results );
mongoose.disconnect();
}
);
Returns results as expected:
[ { name: 'test',
blog: 'test',
photo: 'test',
__v: 0,
ratings: [],
bourbonId: '54c17bea8aa5f8c9161f5b6e',
avgRating: 0 },
{ name: 'another',
blog: 'another',
photo: 'another',
__v: 1,
ratings: [ [Object], [Object] ],
bourbonId: '54c17bea8aa5f8c9161f5b6f',
avgRating: 6 } ]

Categories

Resources