Adding Array To Mongo via AJAX Call and Mongoose - javascript

I'm trying to update a document in a mongo database with information from a form, with all the form going into a field which is an array. At the moment I can't get it to update a document, only create a new one, but more pressingly I can't get the information from the form into the array.
Here is my schema:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const WorkoutSchema = new Schema({
day: {
type: Date,
default: Date.now
},
exercises: [
{
type: String,
trim: true,
required: "Exercise type is required"
},
{
name: String,
trim: true,
required: "Exercise name is required"
},
{
duration: Number
},
{
weight: Number
},
{
reps: Number
},
{
sets: Number
},
{
duration: Number
},
{
distance: Number
}
]
});
const Workout = mongoose.model("Workout", WorkoutSchema);
module.exports = Workout;
And here is my API route. I've included the results of console.logs below it so you can see the information that is getting passed.
app.put("/api/workouts/:id", (req, res) => {
console.log("api body: " + JSON.stringify(req.body));
console.log("body is " + typeof req.body);
var body = JSON.stringify(req.body);
// body = body.split("{")[1];
// body = body.split("}")[0];
// body = "["+body+"]";
console.log(body);
Workout.create({exercises: `${body}`})
.then(Workout => {
res.json(Workout);
})
.catch(err => {
res.json(err);
});
});
api body: {"type":"resistance","name":"Test Press","weight":100,"sets":5,"reps":6,"duration":10}
body is object
{"type":"resistance","name":"Test Press","weight":100,"sets":5,"reps":6,"duration":10}
In the database I get exercises as an array with one element - the above object - instead of a series of key/value pairs. I've tried a lot of things, but this is as close as I get to what I'm trying to do.
Can anyone see where I've gone wrong?

This turned out to be a basic syntax error which came about because one of my keys was "type". The issue is in the syntax of the exercises array, the model should look like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const WorkoutSchema = new Schema({
day: {
type: Date,
default: Date.now
},
exercises: [{
type: {
type: String,
trim: true,
required: "Exercise type is required"
},
name: {
type: String,
trim: true,
required: "Exercise name is required"
},
duration: {
type: Number,
required: "Duration is required"
},
weight: {
type: Number
},
reps: {
type: Number
},
sets: {
type: Number
},
distance: {
type: Number
}
}]
});
const Workout = mongoose.model("Workout", WorkoutSchema);
module.exports = Workout;

Related

How to query a mongo document using mongoose?

MongoDB documents contain an array of objects and what is the best way to query those documents if I want to find and remove an object from an array with some specific value;
Here is an example of the document schema
const mongoose = require("mongoose");
const LibrarySchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
},
books: [
{
type: new mongoose.Schema({
bookName: {
type: String,
required: true,
},
chapterReading: {
type: Number,
default: 1,
required: true,
},
}),
},
],
});
const Library = mongoose.model("Library", LibrarySchema);
exports.Library = Library;
If I want to find and remove a book with some bookName
Use $pull
Example :
Library.update({}, { $pull: { books: { bookName: "Yourbookname" } } })

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,
},
},

How do you save certain items for specific users in mongodb?

I'm working on a job tracker app.
User creates an account, saving the user in a mongodb collection.
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
const JobSchema = new mongoose.Schema({
position: {
type: String,
required: true
},
company: {
type: String,
required: true
},
status: {
type: String,
default: "applied"
},
date: {
type: Date,
default: Date.now
}
});
When a user adds a job, how would you store (.post) and retrieve (.get) that data to correspond to that specific user only?
Is there a way to save the users "_id" to the jobs added, and searchById to get the jobs?
It depends what exactly you want to achieve meaning what type of relationships your models will have. Lets say your users will have multiple jobs the best approach would be to store an array of ObjectIds. The refoption tells mongoose which collections to search during population of the array
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
jobs: [{type:Schema.Types.ObjecId,ref: 'Job'}]
});
and then when you query the database you chain populate('jobs') after the query.
You can read more on the subject here
For example,
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', UserSchema);
async function updateUser() {
let user = await User.findOne({"name": "a-unique-user-name-in-the-db"})
let userId = user._id
let newEmail = "asdf#asdf.com"
let updated = await User.updateOne(
{ "_id": userId },
{
$set: {
"email": newEmail,
}
}
)
if (updated) {
console.log("updated")
}
}
updateUser();

How to validate Array of Objects in Mongoose

I need to validate the array of objects in my schema
Schema:
user: [{
name: String,
Age: String,
Contact: Number
}]
How to validate name, age and contact.
I assume your user array is inside another schema.
Let's say we have a Course model with users like this:
const mongoose = require("mongoose");
const courseSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
users: [
{
name: { type: String, required: [true, "Name is required"] },
age: { type: Number, required: [true, "Age is required"] },
contact: { type: Number, required: [true, "Contact is required"] }
}
]
});
const Course = mongoose.model("Post", courseSchema);
module.exports = Course;
To validate this in a post route you can use mongoose model validateSync method:
const Course = require("../models/course");
router.post("/course", async (req, res) => {
const { name, users } = req.body;
const course = new Course({ name, users });
const validationErrors = course.validateSync();
if (validationErrors) {
res.status(400).send(validationErrors.message);
} else {
const result = await course.save();
res.send(result);
}
});
When we send a requset body without required fields like age and contact:
(you can also transform validationErrors.errors for more useful error messages.)
{
"name": "course 1",
"users": [{"name": "name 1"}, {"name": "name 2", "age": 22, "contact": 2222}]
}
The result will be like this:
Post validation failed: users.0.contact: Contact is required, users.0.age: Age is required
It will be similar to the usual validation but inside an array, you need to make a validator function as so:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//the custom validation that will get applied to the features attribute.
var notEmpty = function(features){
if(features.length === 0){return false}
else {return true};
}
var CarSchema = new Schema({
name: {
type: String,
required: true,
},
features: [{
type: Schema.ObjectId,
ref: Feature
required: true; //this will prevent a Car model from being saved without a features array.
validate: [notEmpty, 'Please add at least one feature in the features array'] //this adds custom validation through the function check of notEmpty.
}]
});
var FeatureSchema = new Schema({
name: {
type: String,
required: true //this will prevent a Feature model from being saved without a name attribute.
}
});
mongoose.model('Car', CarSchema);
mongoose.model('Feature', FeatureSchema);
By using type key/property:
var breakfastSchema = new Schema({
eggs: {
type: Number,
min: [6, 'Too few eggs'],
max: 12
},
bacon: {
type: Number,
required: [true, 'Why no bacon?']
},
drink: {
type: String,
enum: ['Coffee', 'Tea'],
required: function() {
return this.bacon > 3;
}
}
});

Can't get populate() to fill out array in Mongoose

Let me begin by saying I know that this seems to be a frequently asked question and I've spent a couple of days trying to figure it out but no answer seems to work so I'm trying on here.
I have two models, User and Chapter: a Chapter can have have many members (Users). When I do router.get('/chapters') I want to see an array of all the Users associated with a Chapter as a property along with the other Chapter properties, like so:
[
{
"leads": [],
"members": [
{"_id":"someString1","firstName":"...", "lastName":"..."},
{"_id":"someString2","firstName":"...", "lastName":"..."},
],
"date": "2018-12-12T15:24:45.877Z",
"_id": "5c11283d7d13687e60c186b3",
"country": "5c11283d7d13687e60c185d6",
"city": "Buckridgestad",
"twitterURL": "qui",
"bannerPic": "http://lorempixel.com/640/480/people",
"__v": 0
}
]
But what I'm getting is this:
[
{
"leads": [],
"members": [],
"date": "2018-12-12T15:24:45.877Z",
"_id": "5c11283d7d13687e60c186b3",
"country": "5c11283d7d13687e60c185d6",
"city": "Buckridgestad",
"twitterURL": "qui",
"bannerPic": "http://lorempixel.com/640/480/people",
"__v": 0
}
]
These are my Schemas:
Chapter
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const ChapterSchema = new Schema({
country: {
type: Schema.Types.ObjectId,
ref: "countries"
},
city: {
type: String,
required: true
},
leads: [
{
type: Schema.Types.ObjectId,
ref: "users"
}
],
members: [
{
type: Schema.Types.ObjectId,
ref: "users"
}
],
twitterURL: {
type: String,
required: true
},
bannerPic: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
module.exports = Chapter = mongoose.model("chapters", ChapterSchema);
User
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
username: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
organisation: {
type: String,
required: true
},
chapter: {
type: Schema.Types.ObjectId,
ref: "chapters"
},
email: {
type: String,
required: true
},
admin: {
type: Boolean,
default: false
},
lead: {
type: Boolean,
default: false
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now()
}
});
module.exports = User = mongoose.model("users", UserSchema);
Like I said, when I call the endpoint, I want it to return me all the Chapters with all the Users as a populated property.
I've tried a lot of variations of .populate() but to know luck. The closest I got was going through the early levels of callback hell which I know isn't necessary with today's tech, but nothing is working!
My routes/api/chapters.js
// #route GET api/chapters
// #desc Get all Chapters
// #access Public
router.get("/", (req, res) => {
Chapter.find()
.populate("members")
.then(chapters => {
return res.json(chapters);
})
.catch(err =>
res.status(404).json({ nochaptersfound: "No Chapters found" })
);
});
I can get it to work the other way around:
My routes/api/users.js
// #route GET api/users
// #desc Return all users
// #access Public
router.get("/", (req, res) => {
User.find()
.populate("chapter")
.exec()
.then(users => res.status(200).json(users))
.catch(err => console.log(err));
Returns a user with the populated Chapter, but I can't populate the chapter.members array
Any help would be greatly appreciated!
Thanks!!
From your comment, I believe you are not actually storing users in your chapters. What you are doing is this:
User.create({..., chapter: id})...
And assuming chapter now has a user. Its not the way it works with mongoose, so if you want to actually save in both place, you will need to do it yourself. You are thinking about this as if it were a relational database
You will need to do something like:
const user = await User.create({..., chapter: id})
const chapter = await Chapter.findOne({ _id: id })
chapter.members.push(user)
chapter.save()
If your populate wasn't working, you'd not get an empty array, you'd get an array with ids. Your current populate query is fine, you just don't have any data to populate
With promises, it would look like this:
var userPromise = User.create({..., chapter: id}).exec()
var chapterPromise = Chapter.findOne({ _id: id }).exec()
Promise.all([userPromise, chapterPromise]).then((user, chapter) => {
chapter.members.push(user)
return chapter.save()
}).then(chapter => {
// send response
})
If you need 10 chapters with 10 to 50 users, I'd create 50 users, then push all of them into the chapters and save the chapter.

Categories

Resources