So I'm working on a project in node and Express, and while I've gotten a bit of experience playing around with it already and through a few other frameworks, I'm still trying to figure out the best way to connect two of them together.
I have set up models and databases using POSTGRES to hold the ids and connecting tables.
Here is what I have so far:
GET User
app.get('/api/users/:id', function(req, res) {
User.findOne({ where: { phone: req.params.id }}).then(function(user) {
if (!user) {
//////// Create a user
var newUser = User.build({ phone: req.params.id});
newUser.save().then(function(user) {
var json = userJson(user);
res.json(json)
}).catch(function(err) {
res.json(err);
});
} else {
//////// Existing user
var json = userJson(user);
json.events = []
Event.findAll({ where: { user_id: user.id }}).then(function(events) {
for( var i in events) {
json.events.push({
id: events[i].dataValues['id'],
hosting: events[i].dataValues['hosting'],
attending: attending[i].dataValues['attending'],
invites: events[i].dataValues['invites']
})
}
res.json(json)
})
}
})
});
GET Events
app.get('/api/events/:id', function(req, res) {
Event.findOne({ where: { id: req.params.id }}).then(function(event) {
// console.log('-------------',event);
var item = {
id: event.dataValues['id'],
event_name: event.dataValues['event_name'],
event_at: event.dataValues['event_at'],
address: event.dataValues['address'],
description: event.dataValues['description'],
image: event.dataValues['image'],
categories: []
}
res.json(item);
})
});
POST Events
app.post('/api/events', function(req, res) {
var newEvent = Event.build({
user_id: req.body.user_id,
event_name: req.body.event_name,
event_at: req.body.event_at,
address: req.body.address,
description: req.body.description,
image: req.body.image
});
newEvent.save().then(function(event) {
res.redirect('/api/events/'+event.id);
}).catch(function(err) {
// console.log(err);
return res.json({error: 'Error adding new event'});
})
});
Model: event_invite.js
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('event_hostings', {
user_id : DataTypes.INTEGER,
event_id : DataTypes.INTEGER
}
);
}
I know that there is a way to connect the two together, but I'm trying to figure out the best way to interconnect the two when a USER creates an EVENT, and able to have it connect back to the user and recognize them as the creator.
Any advice or pointing me in the right direction would be greatly appreciated! Thank you in advance!
Related
I am working on a blogging application (click the link to see the GitHub repo) with Express, EJS and MongoDB.
I have Posts that are grouped into Categories, each in its own collection.
I run into a problem trying to filter posts by category. To obtain the post by category url, I turn the category name into a slug and use it this way:
Posted in <%= post.category.cat_name %>
In the public routes file I have:
const express = require('express');
const postsController = require('../../controllers/front-end/posts');
// Express router
const router = express.Router();
// Get Posts
router.get('/', postsController.getPosts);
// Get Single Post
router.get('/:id', postsController.getSinglePost);
// Get Posts by Category
router.get('/:catname', postsController.getPostsByCategory);
module.exports = router;
The Post model:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
short_description: {
type: String,
required: true
},
full_text: {
type: String,
required: true
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
},
post_image: {
type: String,
required: false
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Post', postSchema);
The Category model:
const mongoose = require('mongoose');
const categorySchema = new mongoose.Schema({
cat_name: {
type: String,
required: true
},
updated_at: {
type: Date,
default: Date.now()
},
created_at: {
type: Date,
default: Date.now()
}
});
module.exports = mongoose.model('Category', categorySchema);
In the Posts controller I turn the slug back into category name to filter posts by category name:
exports.getPostsByCategory = (req, res, next) => {
function titleize(slug) {
var words = slug.split("-");
return words.map(function(word) {
//return word;
return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
}).join(' ');
}
const postCategory = titleize(req.params.catname);
const posts = Post.find({ cat_name: postCategory }, (err, posts) => {
console.log('Category: ', postCategory);
if(err){
console.log('Error: ', err);
} else {
res.render('default/index', {
moment: moment,
layout: 'default/layout',
website_name: 'MEAN Blog',
page_heading: 'XPress News',
page_subheading: 'A MEAN Stack Blogging Application',
posts: posts.reverse(),
});
}
}).populate('category');
};
The line console.log('Category: ', postCategory) outputs Category: Favicon.ico instead of the category name.
What am I doing wrong?
if I read this correctly, res is expect json pairs.
I am guessing your post.reverse() does not output in json format.
first of all - look at the moment where you ask DB - you need to await the answer, so you should use Promise.than() or async/await in your routes ...
Another one from request you get STRING as parameter - but in mongo schema you have Object...
So you should receive smth like "CastError: Cast to ObjectId failed...",
it depends on your vision you can: first select category from category.db => so you receive category Object after that you can search the posts using this object ..., or you can first populate results of posts by category (obtain plain category fields) and make further search...
category in your post-schema is a $ref to the category-schema, which is why it holds an objectId. In order to reference and actually query your category-schema while using .find(), you need to populate it first:
Post.
find({}).
populate({
path: 'category',
match: { cat_name: postCategory}
}).
exec((err, posts) => {
// ...
});
The mongoose documentation for $ref/populate() is a bit hidden here, in case you want know more about it.
Thanks to Lilian Baxan, here is the right getPostsByCategory method in controllers\front-end\posts.js:
const Category = require('../../models/categories');
//more code here
exports.getPostsByCategory = async (req, res, next) => {
function titleize(slug) {
var words = slug.split("-");
return words.map(function(word) {
//return word;
return word.charAt(0).toUpperCase() + word.substring(1).toLowerCase();
}).join(' ');
}
const postCategory = titleize(req.params.catname);
const singleCategory = await Category.findOne({cat_name:postCategory})
const posts = await Post.find({ category : singleCategory }, (err, posts) => {
if (err) {
console.log('Error: ', err);
} else {
res.render('default/index', {
moment: moment,
layout: 'default/layout',
website_name: 'MEAN Blog',
page_heading: 'XPress News',
page_subheading: 'A MEAN Stack Blogging Application',
posts: posts.reverse(),
});
}
}).populate('category');
};
Baiscally making a node.js, mongodb add friends functionality where having the option of list user to add in friends list, sent friends request, accept friends request, delete friends request, block friends request.
Register Collection
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let Register = new Schema(
First_Name:{
type: String,
required: true
},
Last_Name: {
type: String
},
Email: {
type: String,
unique: true,
lowercase: true,
required: true
},
Friends:[{type: String}],
});
module.exports = mongoose.model('Register', Register);
Friends Collection
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
var ObjectId = require('mongodb').ObjectID;
let Friends = new Schema({
Requester: {
type: ObjectId,
required: true
},
Recipients: [{Recipient:{type:ObjectId},Status:{type:Number}}],
});
module.exports = mongoose.model('Friends', Friends);
Inside Node.js Post API
var Register = require('../models/register.model');
var Friends =require('../models/friends.model');
router.post('/getdata',function(req,res)
{
let Email="example#example.com";
Register.findOne({ Email : Emails }, function(err, user) {
Friends.findOne({ Requester :user._id }, function(err, user1) {
Register.find({$and:[{Friends:{$nin:[user._id]}},{_id:{$ne:user1.Recipients.Recipient}}]},function(err, user2) {
console.log("user2",user2);
//Here User2 data is not coming
//How to get data so can able to list user that is not added yet in FriendList
//Mainly user1.Recipients.Recipient this is not working because //Recipients is array so how can match all data with array, if i am //using loop then find return data scope ends on inside find closing //braces only.
//Any suggestion
});
});
});
So if I have it correct, you want to do the following:
Find a registration based on a given email
Find the friends related to this user
Find registrations that are not yet in the friend list of the user
Also, given what you've typed, I'm assuming A can be the friend of B, but that doesn't mean B is the friend of A.
While the data structure you currently have may not be optimal for this, I'll show you the proper queries for this:
var Register = require('../models/register.model');
var Friends =require('../models/friends.model');
router.post('/getdata',function(req,res) {
const email = "example#example.com";
Register.findOne({ Email: email }, function(err, user) {
if (err) {
console.error(err);
return;
}
Friends.findOne({ Requester: user._id }, function(err, friend) {
if (err) {
console.error(err);
return;
}
const reciptientIds = friend.Recipients.map(function (recipient) {
return recipient.Recipient.toString();
});
Register.find({Friends: { $ne: user._id }, {_id: { $nin: recipientIds }}, function(err, notFriendedUsers) {
if (err) {
console.error(err);
return;
}
console.log(notFriendedUsers);
});
});
});
});
P.S. This "callback hell" can be easily reduced using promises or await/defer
Finally able to solve it, below is the solution
var Register = require('../models/register.model');
var Friends =require('../models/friends.model');
router.post('/getdata',function(req,res)
{
let Emails="example#example.com";
Register.findOne({$and:[{ Email : Emails}] }, function(err, user) {
if (err) {
console.error(err);
return;
}
Friends
.findOne({ Requester: user._id },
{ _id: 0} )
.sort({ Recipients: 1 })
.select( 'Recipients' )
.exec(function(err, docs){
docs = docs.Recipients.map(function(doc) {
return doc.Recipient; });
if(err){
res.json(err)
} else {
console.log(docs,"docs");
Register.find({$and:[{Friends: { $ne: user._id }},{_id: { $nin: docs }},{_id:{$ne:user._id}}]}, function(err, notFriendedUsers) {
if (err) {
console.error(err);
return;
}
console.log(notFriendedUsers);
});
}
})
});
var mongoose = require("mongoose"),
campground = require("./models/campground"),
comment = require("./models/comment");
var data = [{
name: "Offside Lake",
image: "https://images.unsplash.com/photo-1504280390367-361c6d9f38f4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "Whatever evrr"
},
{
name: "Reality Check",
image: "https://images.unsplash.com/photo-1517824806704-9040b037703b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "wabdiwyu"
},
{
name: "Wawu Land",
image: "https://images.unsplash.com/photo-1508873696983-2dfd5898f08b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "Just be feeling Wawu"
}
];
var text = {
text: "Hullabaloo",
author: "Olalaa"
};
campground.comments = new Array();
function seedDB() {
campground.deleteMany({}, function(err) {
if (err) {
console.log(err);
} else {
console.log("removed");
data.forEach(function(camp) {
campground.create(camp, function(err, camp) {
if (err) {
console.log(err);
} else {
console.log("Successfully added");
comment.create(text, function(err, comment) {
if (err) {
console.log(err);
} else {
campground.comments.push(comment);
campground.save();
console.log("comment added");
}
});
}
});
});
}
});
}
I have two mongoose models campground and comment. Inside the campground schema, I have the comments associative array in the campground schema. I am trying to add comments to my comments array but I am getting the error - campground.save is not a function. Even tried campground.markModified("comment") then campground.save(), getting the same error
//my campground schema
var mongoose = require("mongoose");
var campSchema = new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}]
});
module.exports = mongoose.model("Camp", campSchema);
//my comment schema
var mongoose = require("mongoose");
var commentSchema = mongoose.Schema({
text: String,
author: String
})
module.exports = mongoose.model("comment", commentSchema);
If I understand what you are trying to do, you are trying to create a campground and place the comments inside.
If that is so, then the code may look something like this (placed everything in one file):
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test', {useNewUrlParser: true});
var data = [
{
name: "Offside Lake",
image: "https://images.unsplash.com/photo-1504280390367-361c6d9f38f4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "Whatever evrr"
}, {
name: "Reality Check",
image: "https://images.unsplash.com/photo-1517824806704-9040b037703b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "wabdiwyu"
}, {
name: "Wawu Land",
image: "https://images.unsplash.com/photo-1508873696983-2dfd5898f08b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
description: "Just be feeling Wawu"
}
];
const comment = mongoose.model('comment', new mongoose.Schema({
text: String,
author: String
}));
const campground = mongoose.model('Camp', new mongoose.Schema({
name: String,
image: String,
description: String,
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: "comment"
}]
}));
var text = {
text: "Hullabaloo",
author: "Olalaa"
};
campground.deleteMany({}, function(error) {
if (error) {
console.error(error);
return;
}
console.log("Removed");
data.forEach(function(camp) {
campground.create(camp, function(error, newCamp) {
if (error) {
console.error(error);
return;
}
console.log("Successfully added");
comment.create(text, function(err, newComment) {
if (err) {
console.error(err);
return;
}
newCamp.comments.push(newComment);
newCamp.save();
console.log("Comment added");
})
});
})
})
The problem was due to the fact that you kept the same name throughout and that might have confused you a bit.
What you wanted to do was camp.comments.push(comment) camp.save() instead of campground.comments.push(comment) and campground.save() respectively.
As a friendly advice:
Switch to using promises instead of callbacks, you may set yourself up for what is known as Callback hell
As much as possible try not to rely on the closure nature of JavaScript and keep naming your variables the same throughout. That leads to problems like what you are experiencing now
I have a User model that contains an array of customers. I want to delete a specific customer based on the customer _id. From what I've read in the Mongoose docs, I should use Model.deleteOne to delete a single document.
Here is my attempt
User Schema (it's been shortened for brevity):
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: {
type: String,
default: ''
},
password: {
type: String,
default: '',
},
registerDate: {
type: Date,
default: Date.now()
},
customer: [{
name: {
type: String,
default: '',
},
email: {
type: String,
default: 'No email name found'
},
fleet: [{
unitNumber: {
type: String,
default: 'N/A',
}
}]
}]
});
module.exports = mongoose.model('User', UserSchema);
Here is a look at the route and controller:
const express = require('express');
const router = express.Router();
const customer_controller = require('../../controllers/customers');
router.delete('/customers/:custid', customer_controller.customer_remove);
module.exports = router;
And finally the controller:
exports.customer_remove = (req, res) => {
const { params } = req;
const { custid } = params;
User.deleteOne({ 'customer._id': custid }, (err) => {
if (err)
throw err;
else
console.log(custid, 'is deleted');
});
};
From what I thought, User.deleteOne({ 'customer.id': custid }) would find the customer _id matching the custid that is passed in via the req.params. When I test this route in Postman, it deletes the entire User collection that the customer is found in, instead of just deleting the customer. Can I get a nudge in the right direction? I feel like I am close here (or not lol).
deleteOne operates at the document level, so your code will delete the first User document that contains a customer element with a matching _id.
Instead, you want update the user document(s) to remove a specific element from the customer array field using $pull. To remove the customer from all users:
User.updateMany({}, { $pull: { customer: { _id: custid } } }, (err) => { ...
Using Mongoose you can do this:
model.findOneAndUpdate({ 'customer._id': custid }, {$pull: { $pull: {
customer: { _id: custid } }}, {new: true}).lean();
Removing subdocs.
Each sub document has an _id by default. Mongoose document arrays have a special id method for searching a document array to find a document with a given _id.
Visit: https://mongoosejs.com/docs/subdocs.html
parent.children.id(_id).remove();
Use async-await, may be that will work.
exports.customer_remove = async (req, res) => {
const { params } = req;
const { custid } = params;
try {
await User.deleteOne({ 'customer._id': custid });
console.log(custid, 'is deleted');
} catch (err) {
throw err;
}
};
Here's my mongoose schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CartSchema = new Schema({
userID: String,
items: [{
itemID: String,
quantity: Number
}]
});
module.exports = mongoose.model('Cart', CartSchema);
Here's the node.js server which uses Express. It has /add-to-cart route which if triggered it should update user's cart with the information passed in req.body:
router.post('/add-to-cart', function(req, res, next) {
Cart.find({ userID: req.body.userID }).then(function(userCart){
console.log("TEST: "+JSON.stringify(userCart));
var myItem = {itemID: req.body.itemId, quantity: 1}
userCart.items.push(myItem);
res.send(userCart);
}).catch(next);
});
I printed to terminal userCart as you can see in my code and it returned me this:
[{
"_id":"58f7368b42987d4a46314421", // cart id
"userID":"58f7368a42987d4a46314420", // userid
"__v":0,
"items":[]
}]
When the server executes userCart.items.push(myItem); it returns this error:
Cannot read property 'push' of undefined
Why items is not defined if I've already defined its structure in mongoose?
As adeneo correctly pointed out, userCart is clearly an array but you need to use one of the update methods to push the document to the items array, would suggest Model.findOneAndUpdate() as in
router.post('/add-to-cart', function(req, res, next) {
Cart.findOneAndUpdate(
{ userID: req.body.userID },
{ $push: { items: { itemID: req.body.itemId, quantity: 1 } } },
{ new: true }
)
.exec()
.then(function(userCart) {
console.log("TEST: "+ JSON.stringify(userCart));
res.send(userCart);
})
.catch(next);
});
As adeneo pointed out userCart is an array since you are you are using the find method. But clearly you need to find just one document given by its userID so it advised to use findOne() instead.
Also you will need to save the document in order for the changes to actually reflect.
Have a look at the updated code below:
router.post('/add-to-cart', function(req, res, next) {
Cart.findOne({ userId: req.body.userID }, function(err, userCart){
if(err) return next(err);
console.log("TEST: "+JSON.stringify(userCart));
var myItem = {itemID: req.body.itemId, quantity: 1}
userCart.items.push(myItem);
userCart.save(function(err, usersCart) {
if(err) return next(err);
res.send(usersCart);
})
})
});
Hope this helped.
This can be solved using:
router.post('/add-to-cart', function(req, res, next) {
Cart.findOne({ userID: req.body.userID }).then(function(userCart){
console.log("TEST: "+JSON.stringify(userCart));
const a = req.body.items;
for(i=0;i<a.length;i++)
{
userCart.items.push(req.body.items[i]);
}
res.send(userCart);
}).catch(next);
});