add query conditions using mongoose model - javascript

using Node.js, Mongoose Schema and MongoDB,
the user model Schema is
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name: { type: String, trim: true }
, address: { type: String }
, birth: { type: Date }
, tlf: { type: String }
, email: { type: String, lowercase: true }
});
module.exports = mongoose.model('User', userSchema);
and the function to query users with conditions (var cond), select (var sel), options (var opts) is
findAllUsers = function (req, res, next) {
var cond = {
name : req.query.nm
};
var sel = 'name address';
var opts = {
skip: req.query.sk, limit: req.query.lim, sort: req.query.srt
};
User.find(cond, sel, opts).lean().exec(function (err, users) {
if (err) next(err);
var body = {};
body.skip = req.query.sk;
body.limit = req.query.lim;
body.users= users;
User.count(cond).exec(function (err, total) {
if (err) next(err);
body.total = total;
res.json(body);
});
});
}
and then, what is the best way to create a conditions with regexp, like, or... ?

The equivalent to "like" in MongoDB is the regex operator and would be implemented like this:
db.collection.find({ "address": { "$regex": "via" } });
Keep in mind though, that just the same as the equivalent like "%via%" statement, this does need to scan every document in the collection (or at best index) in order to match the string that is not "anchored" to the start of the string.

Related

Node.js and Mongoose - map populated array inside object

I have 2 schemas - "Category" and "Tag".
Each Category has an array of tags linked to it.
What I'm trying to achieve is: get category by id with its tags populated but instead of returning the category with array of tag objects I would like to map the tags to an array of tags names:
This approach below is not working, The returned Category has still an array of tag objects instead of tag names
const getById = async (req, res, next) => {
const id = req.params.id;
let category;
let tags;
try {
category = await Category.findById(id)
.populate('tags');
if (!category) throw new HttpError("Could not find category with the provided id", 400);
tags = category.tags.map(tag => tag.name);
category.tags = tags;
} catch (err) {
return next(err);
}
const statusCode = 200;
res.status(statusCode).json({
category,
code: statusCode,
message: "Success"
});
}
const {Schema} = mongoose;
const categorySchema = new Schema({
name: {
type: String,
required: [true, 'Enter name'],
trim: true,
unique: true,
set: name => capitalizeFirstLetter(name)
},
dateCreated: {type: Date, default: new Date(), get: (dateCreated) => moment(dateCreated)},
tags: [{type: mongoose.Types.ObjectId, ref: 'Tag'}],
});
module.exports = mongoose.model("Category", categorySchema);
const {Schema} = mongoose;
const tagSchema = new Schema({
name: {
type: String,
required: [true, 'Enter name'],
trim: true,
unique: true,
set: name => capitalizeFirstLetter(name)
},
dateCreated: {type: Date, default: new Date(), get: (dateCreated) => moment(dateCreated)}
});
module.exports = mongoose.model("Tag", tagSchema);
Found the solution:
const getById = async (req, res, next) => {
const id = req.params.id;
let category;
let tags;
try {
category = await Category.findById(id)
.populate('tags').lean();
if (!category) throw new HttpError("Could not find category with the provided id", 400);
tags = category.tags.map(tag => tag.name);
category.tags = tags;
} catch (err) {
return next(err);
}
const statusCode = 200;
res.status(statusCode).json({
category,
code: statusCode,
message: "Success"
});
}

How could I reference a model to my User model with Express/Mongoose

I have two models, one being my User model and the other being my Course model. I would like to have it so when a User (Teacher) creates a course, it assigns that course to them and vice versa. Here are my models to explain better:
Course Schema/Model:
var CourseSchema = new Schema({
courseID: {
type: Number,
unique: true
},
courseName: String,
courseDesc: {
type: String,
default: "No course description provided."
},
coursePicture: {
type: String,
required: false
},
teacher: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
],
students: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
}
]
})
User Schema/Model:
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
courses: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Course'
}
],
password: String
});
Basically, I want to have it so on my frontend, I could do things like course.teacher.firstName or user.courses. My schemas are in two different files, but I believe that is fine. It's like assigning a user a post when they create it. I don't know how I could do this, as I've tried multiple things.
Right now, I currently have this for creating a course.
// Creates a new course
router.post('/create', function (req, res) {
Course.create({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id,
students: req.body.students
},
function (err, course) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
});
});
I have already referenced the User model in the controller where that code ^ belongs as so var User = require('../user/User');
I believe that is needed to pull this off. If you have any questions, please let me know as I'm not the best at explaining things like this.
Hope someone can help me out!
Thanks.
// Creates a new course
router.post('/create', function (req, res) {
Course.create({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id, // find this user
students: req.body.students,
attendance: req.body.attendance
},
function (err, course) {
User.findById(req.body.id, function(err, user) {
user.update({
$push: {
courses: course._id
}
}, function(err) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
})
})
});
});
This is an issue of database design. There should only be one place where information about a course is stored, the Courses table, and the Users table should know nothing about courses. There should be a table the relates a course to a user: a UserCourseRelations table.
I would strongly avoid the approach of storing an array of courseIds that a user is related in the user table as this is unnecessary coupling and so is not good database design. Also, it'll bog down reads to your Users table as those arrays grow on every row.
Here's how I would approach this. Note that some of this code uses ES6 syntax. The following code is untested, but should work. Take a look:
Create CourseSchema and CourseModel
var CourseSchema = new mongoose.Schema({
courseID: {
type: Number,
unique: true
},
courseName: String,
courseDesc: {
type: String,
default: "No course description provided."
},
teacherId: {
type: mongoose.Schema.Types.ObjectId,
}
coursePicture: {
type: String,
required: false
},
students: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
}
]
})
CourseSchema.statics.createNew = function(data, callback) {
// do some verification here
// insert the new course
return new this(data).save((err, dbCourse) => {
if (err) {
return callback(err)
}
UserCourseRelationSchema.insertNew('teacher', userId, courseID, (err, dbUserCourseRelation) => {
if (err) {
return callback(err)
}
// done. return the new course
callback(null, dbCourse)
})
})
CourseSchema.statics.getByIds = function(courseIDs, callback) {
// find all of the courses where the courseID is in the courseIDs array
// see https://docs.mongodb.com/manual/reference/operator/query/in/
this.find({courseID: {$in: courseIDs}}, (err, courses) => {
if (err) {
// something went wrong
return callback(err)
}
callback(null, courses)
})
}
}
let CourseModel mongoose.model('courses', CourseSchema);
Create UserCourseRelationSchema and UserCourseRelationModel that relates a course to a user and vice versa
var UserCourseRelationSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
courseID: {
type: Number,
required: true,
},
type: {
type: String,
enum: ['teacher', 'student'],
required: true,
},
});
UserCourseRelationSchema.statics.createNew = function(type, courseID, userId, callback) {
// do some verification here. I suggest making sure this relation doesn't already exist
// insert the new course
return new this({
courseID: courseID,
userId: userId,
type: type,
}).save((err, dbUserCourseRelation) => {
if (err) {
return callback(err)
}
// return the new relation
callback(null, dbRelation)
})
}
UserCourseRelationSchema.statics.getTeacherRelationCourseIdsByUserId = function(userId, callback) {
let query = this.find({userId: userId, type: 'teacher'})
query.distinct('courseID') // get an array of only the distinct courseIDs
query.exec((err, courseIDs) => {
if (err) {
// something went wrong
return callback(err)
}
callback(null, courseIDs)
})
}
let UserCourseRelationModel = mongoose.model('user_course_relations', UserCourseRelationSchema);
Create UserSchema and UserModel
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
password: String
});
UserSchema.statics.getAllCoursesById = function(userId, callback) {
// get the relations for the courses the user is a teacher of
UserCourseRelationModel.getTeacherRelationCourseIdsByUserId(userId, (err, courseIDs) => {
// get the courses by the returned coursIDs
CourseModel.getByIds(courseIDs, (err, courses) => {
if (err) {
// something went wrong
return callback(err)
}
callback(nul, courses)
})
})
}
let UserModel = mongoose.model('users', UserSchema);
// -- create the router
// Creates a new course
router.post('/create', function (req, res) {
CourseModel.createNew({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id,
students: req.body.students
}, function (err, course) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
});
});
// -- done
I also suggest using promises if possible as it makes all of this logic much simpler.

$addToSet to an array but it gives me null

so basically I've a wish list and I've bunch of products that I want to add inside the the wish list products array using a put request (I'm using postman btw).
This is the wish list schema, and yes I know that the document's name in the db is "whishlist"....I hate typos
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
var whishList = new Schema({
title: {type: String, default: "Cool whishlist"},
products:[{type: ObjectId, ref:'Product'}]
});
module.exports = mongoose.model('WhishList', whishList);
This is the products schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var product = new Schema({
title: String,
price: Number,
likes: {type: Number, default: 0}
});
module.exports = mongoose.model('Product', product);
and now this is the code that I am trying to run
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/swag-shop');
var Product = require('./model/product');
var wishList = require('./model/wishlist');
app.put('/wishlist/product/add', function(request, response){
Product.find({_id: request.body.productId}, function(err, product){
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
}else{
wishList.update({_id: request.body.wishlistId},{$addToSet: {products: product._id}}, function(err, wishlist){
if(err){
response.status(500).send({err: "could not add item to wishlist /update/"});
}else{
response.send(wishlist);
}
});
}
});
I really can't see where is the problem I tried deleting the document and posting it again but I had the same problem.
Thanks in advance
The issue is that the result from Product.find() is an array of Mongoose documents if the query matches any documents in the collection instead of a single document which you want.
Thus the expression {$addToSet: {products: product._id}} resolves to {$addToSet: {products: undefined}} because product is an array and product._id is undefined. Take this simple example
var product = [{ '_id': 1 }];
console.log(product._id) // logs undefined
To remedy this problem, you can either access the only element in the array as
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product[0]._id} },
function(err, wishlist) { ... }
);
Or use the findOne() method which returns a single document when querying the product:
Product.findOne({ '_id': request.body.productId }, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});
The findById() method is also useful in this case i.e.
Product.findById(request.body.productId, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});

how to find and show by user in a simple node.js app?

I trying to make a simple app in node and I have this as model:
var mongoose = require("mongoose");
var jobs = new mongoose.Schema({
jobNumber: Number,
jobField: String,
jobTitle: String,
jobCity: String,
jobArea: String,
jobAddress: String,
jobPhone: String,
jobInsurance: String,
jobSalary: Number,
jobAccommodation: String,
jobDescription: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
created: {
type: Date,
default: Date.now
}
});
//EXPORT
module.exports = mongoose.model("jobs", jobs);
and for router i have this:
//SHOW OWNED JOBS
router.get("/myjobs", function (req, res) {
jobs.find({},function (err, myjobs) {
if (err) {
res.send(err);
} else {
res.render("filteredJobs", {
datas: myjobs
});
}
});
});
I can find some data by jobs.find{salaryJob : "900"} but if I want to find data by parameters of author how does it work?
I try several time like this but it doesn't work!
//SHOW OWNED JOBS
router.get("/myjobs", function (req, res) {
jobs.find({author.username : "blackcat"},function (err, myjobs) {
if (err) {
res.send(err);
} else {
res.render("filteredJobs", {
datas: myjobs
});
}
});
});
thank you
right answer is:
{'author.username': String(req.user.username) } }
the problem was diferent between author.username and req.user.username, we should put a string here, I change the req.user.username to string with String(); function.

NodeJS put array in MongoDB doc.validate is not a function

I am trying to push array of objects into MongoDB through NodeJS.
So my schema
var UserSchema = new mongoose.Schema({
id: mongoose.Schema.ObjectId,
added: {
type: Date,
default: Date.now()
},
displayName: String,
login: String,
email: String,
phone: String,
password: String,
salt: String,
role: Number,
hasPremium: Boolean,
avatar: Buffer,
description: String,
additional: [{
name: String,
data: String
}],
cart: [{
exposition: Object,
offer: Object,
state: Number,
history: [{
date: Date,
state: Number,
modifier: Number
}]
}],
balance: Number,
topUps: [{
orderNumber: String,
sum: Number,
added: Date,
paid: Date
}],
lock: Boolean
});
My save controller
module.exports = function (passport) {
passport.use('signup', new LocalStrategy({
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, username, password, done) {
findOrCreateUser = function () {
// find a user in Mongo with provided username
UserModel.findOne({'login': username}, function (err, user) {
// In case of any error, return using the done method
if (err) {
console.log('Error in SignUp: ' + err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: ' + username);
return done(null, false, req.flash('message', 'User Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = new UserModel();
// set the user's local credentials
newUser.displayName = req.param('displayName');
newUser.login = username;
newUser.password = createHash(password);
newUser.email = req.param('email');
newUser.phone = req.param('phone');
newUser.role = req.param('role');
newUser.description = req.param('description');
if (req.param('avatar')) {
var avatar = new Buffer(req.param('avatar')).toString('base64');
newUser.avatar = new Buffer(avatar, 'base64');
}
var adds = req.param('additional');
console.log(adds);
if (adds) {
newUser.additional = [];
for (var i = 0; i < adds.length; i++) {
newUser.additional[i] = {};
newUser.additional[i].name = adds[i].name;
newUser.additional[i].data = adds[i].data;
}
}
console.log(newUser.additional);
// save the user
newUser.save(function (err) {
if (err) {
console.log('Error in Saving user: ' + err);
throw err;
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function (password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
}
So, when I run this code I got strange error
TypeError: doc.validate is not a function
Even if I had no validation in my scheme.
What am I doing wrong?
Thank you
OMG, I don't know what happened but when I did it like
newUser.additional = req.param('additional');
Insted of this part of code
if (adds) {
newUser.additional = [];
for (var i = 0; i < adds.length; i++) {
newUser.additional[i] = {};
newUser.additional[i].name = adds[i].name;
newUser.additional[i].data = adds[i].data;
}
}
It worked perfectly. There is still magic for me...

Categories