Its been a tough time for me trying to figure this out.
The problem I'm trying to solve is this:
I have USERS who has list of COMPANIES,
this COMPNAY has list of PROJECTS etc.
I'm trying to create a PROJECT under a COMPANY and a COMPANY under a USER using referencing.
This USER is going to be populated from form fields likewise COMPANY PROJECT
USERS SCHEMA
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//============= User Schema============//
const userSchema = new Schema({
name: String,
email: String,
//referencing user's company/companies
company: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'company',
},
],
},{
timestamps: true
});
const Users = mongoose.model('user', userSchema);
module.exports = Users;
COMPANY SCHEMA
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//======== Company schema ========//
const compSchema = new Schema({
company_name: String,
//referencing user schema
user: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
},
],
//referencing projects
projects: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'project',
},
],
},{
timestamps: true
});
const Company = mongoose.model('company', compSchema);
module.exports = Company;
PROJECT SCHEMA
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//PROJECT SCHEMA
const projectSchema = new Schema({
project_title: String,
//referencing company schema
company: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'company',
}
],
},{
timestamps: true
});
const Projects = mongoose.model('project', projectSchema);
module.exports = Projects;
CONTROLLERS
const Users = require('./userSchema');
const Company = require('./companySchema');
const Projects = require('./projectSchema');
//create user
exports.createUser = async (req, res) => {
const data = {
name: req.body.name,
email: req.body.email,
}
Users.create(data, (err, done) => {
if(err) return err;
res.json({UserData: done});
return;
});
}
exports.getAllUsers = async(req, res) => {
}
//create company
exports.createCompany = async (req, res) => {
const CompanyData = {
company_name: req.body.company_name,
}
Company.create(companyData, (err, done) => {
if(err) return err;
res.json({CompanyDetails: done});
return;
});
}
exports.getllCompany = async (req, res) => {
}
//create project
exports.createProject = async (req, res) => {
const projectData = {
project_title: req.body.project_title,
}
Projects.create(projectData, (err, done) => {
if(err) return err;
res.json({ProjectDetails: done});
return;
});
}
exports.getAllProjects = async (req, res) => {
}
I understand that populate can be used to push documents to children. Any assistance offered will be highly appreciated
Store reference
Firstly, while creating a Company inside createCompany controller, you should add user _id in users array like this:
const CompanyData = {
company_name: req.body.company_name,
users: [req.body.userId] // Lets assume, user _id is coming in request body
}
Company.create(companyData, (err, done) => {
// Your code
});
Then, when you create a Project, add _id of Company you just created, in createProject controller like this:
const projectData = {
project_title: req.body.project_title,
company: [req.body.comapnyId] // Again assuming, company _id is coming in request body
}
Projects.create(projectData, (err, done) => {
// Your code
});
Update reference
Also, as I see in your schemas, you are trying to keep it bidirectional, like user's _ids in company doc & company's _ids in user doc.
To achieve this, if you create a user, you should update the corresponding company with pushing(addToSet actually) new user _id in user array.
Example:
To create a user in controller, run two scripts synchronously one-by-one:
Create a user with company _id and other details.
const data = {
name: req.body.name,
email: req.body.email,
company: [req.body.companyId]
}
Users.create(data, (err, done) => {
// Your code
});
Update company doc(whose _id is saved in above user) with _id of newly created user.
const updateCompany = { "$addToSet": { user: user._id } }; // Get `user._id` from above synchronous script
Company.update({_id: req.body.companyId}, data, (err, done) => { // Updated filter
// Your code
});
Related
I am trying to add an item to a MongoDB array with RESTAPI through Axios. I thought it would look similar to the push method but I have no idea how to do that.
my Model is of a person:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: [],
missions: []
})
const personModel = mongoose.model('Person', PersonSchema);
I want to add a mission to the mission array of a person.
and for example, in order to add a new Person, I use NodeJS and API:
(api.js)
router.post('/api/people', (req, res) => {
const personToAdd = req.body;
const newPersonPost = new personModel(personToAdd);
newPersonPost.save((e) => {
if (e) {
console.log("error");
}
});
res.json({
msg: 'Received'
})
});
and in the client side I use Axios:
axios({
url: 'http://localhost:8080/api/people',
method: 'POST',
data: dataToUpdate
})
.then(() => {
console.log('axios sent info to server');
}).catch((e) => {
console.log('error' + e);
})
Thank you so much!
express
router.post('updating mission endpoint url', async (req, res) =>
try {
const query = { /* content */}; /* write a query to retrieve the concerned user by using a unique identifier */
let person = await personModel.findOne(query);
person.missions.push(req.body.mission);
personModel.save();
} catch (err) {
console.log(err);
}
});
client
In the client side you just have to put the mission you want to add in data like you did above with the right endpoint url and you should add a unique identifier for the user you want to add mission to.
[] will not assign array type to your variable.
Change your schema file with the following:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: { type: String },
password: { type: String },
friends: { type: Array },
missions: { type: Array }
})
Update the db model entity file with following
First method:
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: {type : Array},
missions: {type : Array}
})
const personModel = mongoose.model('Person', PersonSchema);
Second Method :
const Schema = mongoose.Schema;
const PersonSchema = new Schema({
name: String,
password: String,
friends: [{ type: String }],
missions: [{ type: String }]
})
const personModel = mongoose.model('Person', PersonSchema);
You can update the array object as per your requirements.
You just want to be using the $push update operator, very simple, like so:
db.collection.updateOne(
{
_id: user._id
},
{
"$push": {
"missions": {
mission: newMission
}
}
})
Mongo Playground
Client.js
const mongoose = require("mongoose");
var Schema = mongoose.Schema;
const clientSchema = new mongoose.Schema(
{
name: { type: String, required: true, default: "" },
}, {
timestamps: true
}
);
module.exports = mongoose.model("Client", clientSchema);
User.js
const mongoose = require("mongoose");
var Schema = mongoose.Schema;
const userSchema = new mongoose.Schema({
name: { type: String, required: true, default: "" },
clients: [{
client: {
type: Schema.Types.ObjectId,
ref: "Client",
default: null
},
user_group: {
type: Number
default: null
}
}]
}, { timestamps: true });
module.exports = mongoose.model("User", userSchema);
auth.js (Where trying to populate Clients)
const express = require("express");
const router = express.Router();
const User = require("../models/User");
const Client = require("../models/Client");
router.post("/users", (req, res) => {
let params = req.body;
let total_client = [];
User.findOne({
email: params.email
})
.populate({
path: "clients.client",
model: Client
})
.exec((err, user) => {
console.log(user);
res.send(user);
});
});
module.exports = router;
Please check the above code. I have given code examples of my two models user.js and client.js. In user schema, I have referenced client inside an array object. While querying user, the client is not population. Please help me to get this thing done. Thanks in advance.
The following expects you to provide a name in the json body of your post request (your example uses email which does not exist in the user model). Also, your model is already defining the ref: Client and so you can simplify your request to just include the path clients.client.
router.post("/users", async (req, res) => {
const { name } = req.body;
const user = await User.findOne({ name: name }).populate('clients.client').exec();
res.send(user);
});
Solved this problem just adding an extra parameter in module export of client.js file
module.exports = mongoose.model("Client", clientSchema, "client");
I want to query and response for API. I have 2 collections "Account" and "User". "User" has "id" field and "Account" has "user_id" field - both of us is one. One "User" has one or many Account and it has "id" field in "Account" collection. I want to query "id" from "User" collection and expect the result detail of this User and 1 field "accounts_id" into it.
This is all my expected result to look like:
https://sample-accounts-api.herokuapp.com/
I tried to collect "accounts_id" but I can not know how to query and add it to "User" detail.
This is my collect "accounts_id":
exports.findAllAccountIDforUser = (req, res) => {
Account
.find({user_id: req.param('user_id')})
.then(result => {
let a = result.map(data => {
return data.id;
})
res.send({'accounts_id':a});
})
}
My "User" Schema:
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
const Account = require('../model/account');
var UserSchema = new mongoose.Schema({
id: {type: Number, unique: true},
name: String,
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
My "Account" Schema:
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var AccountSchema = new mongoose.Schema({
id: {type: Number},
user_id: {type: Number, ref: 'User'},
name: String,
balance: Number
});
const Account = mongoose.model('Account', AccountSchema);
module.exports = Account;
using async-await you can do it in 4 lines.
get user
get all accounts related to that user
assign user.account_ids=accounts
send response.
exports.findAllAccountIDforUser = async(req, res) => {
let user=await User.findOne({id:req.param('user_id')});
let accounts= await Account.find({user_id: req.param('user_id')}).select(' _id id);
user.account_ids=accounts;
res.send(user);
}
another way is using promises like you are already doing it.
exports.findAllAccountIDforUser = (req, res) => {
User
.findOne({id:req.param('user_id')})
.then(user=>{
Account
.find({user_id: req.param('user_id')})
.then(result => {
let a = result.map(data => {
return data.id;
})
user.account_ids=a;
res.send(user);
})
})
}
I have answer for thís:
exports.findAlldetail = (req, res) => {
User
.aggregate([
{
$match: {
id: +req.param('id')}
},
{
$lookup: {
from: "accounts",
localField: "id",
foreignField: "user_id",
as: "account_ids"
}
},
{
$project: {
_id: 0,
id: 1,
name: 1,
"account_ids.id": 1
}
}
])
.then(result => {
result.account_ids = result.map(data => {
data.account_ids = data.account_ids.map(b => b.id);
})
res.send({"attributes":result});
})
}
I'm building my first mean stack app. It's a review site that contains three models: User, Review, and Company.
When I make a review, I want the new review to be saved to the 'review' collection, and for that review to be connected by reference to the company being reviewed and the user who wrote the review. I also want the user to hold a reference to the review, and the company to hold a reference to all the reviews it has. Here are my models:
Review
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const reviewSchema = new Schema ({
companyName: String,
companyId: { type: Schema.Types.ObjectId, ref: 'Company'},
starRating: Number,
subject: String,
commentBody: String,
createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
});
const Review = mongoose.model("Review", reviewSchema);
module.exports = Review;
Company
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const companySchema = new Schema ({
companyName: String,
about: String,
basedIn: String,
materialOrigins: [String],
productRange: [String],
category: String,
reviews: [ {type: Schema.Types.ObjectId, ref: 'Review'} ],
socialRating: Number,
environmentalRating: Number,
priceRange: Number
});
const Company = mongoose.model("Company", companySchema);
module.exports = Company;
User
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema ({
email: String,
firstName: String,
lastName: String,
password: String,
image: Object,
aboutText: String,
reviews: [ { type: Schema.Types.ObjectId, ref: "Review" } ]
// comments: { type: Schema.Types.ObjectId, ref: 'Comment' }
});
const User = mongoose.model("User", userSchema);
module.exports = User;
This is my current route, which currently saves the review to the collection and attaches the user. However, the user doesn't get the review.
route
router.post('/:category/:company', (req, res) => {
var subject = req.body.subject;
var commentBody = req.body.commentBody;
var starRating = req.body.starRating;
var userId = req.body.userId;
if(!subject || !commentBody || !starRating) {
res.status(400).json({ message: "Subject, comment body, and star rating are required." });
return;
}
var newReview = Review({
starRating,
subject,
commentBody,
userId
});
User.findById(userId, {
}, (err, user) => {
if (err) {
return res.send(err);
} else {
console.log("checking out user in route", user);
user.reviews.push(newReview);
user.save();
newReview.save((err, review) => {
if (err) {
return res.status(400).json({ message: err });
} else {
res.status(200).json({ message: 'Review saved', review });
}
});
}
});
I haven't tried adding the company in because I'm trying to do one thing at a time. I've been looking at 'populate', but all of the documentation seems to only use two models at once. Is it possible to do three at once? Or am I overcomplicating this?
Apologies if this is all overcomplicated. I'm fairly new to MongoDB and MEAN stack in general. Thanks for your help.
Ok, I did it, for any people landing on this page wondering the same thing in the future.
Here's my route:
router.post('/:category/:company', (req, res, next) => {
var companyName;
var companyId;
var subject = req.body.subject;
var commentBody = req.body.commentBody;
var starRating = req.body.starRating;
var createdBy = req.body.createdBy;
if(!subject || !commentBody || !starRating) {
res.status(400).json({ message: "Subject, comment body, and star rating are required." });
return;
}
var newReview = Review({
starRating,
subject,
commentBody,
createdBy
});
//I need the companyId and companyInfo for later use in my review save. I'm calling the company with the name I have from my params, and setting the id and name with the received data from Mongo.
Company.findOne({"companyName": req.params.company}, (err, company) => {
if (err) {
return res.status(400).json({ message: err });
} else {
this.companyName = company.companyName;
this.companyId = company.id;
}
});
newReview.save((err, review) => {
//Push the review id to the user
if (err) {
return res.status(400).json({ message: err });
} else { User.findByIdAndUpdate({_id: createdBy },{$push: {reviews: review.id} }, (err) => {
if (err) {
console.log("There was an error pushing review to user");
next(err);
//Push the review id to the company
} else { Company.findOneAndUpdate({ "companyName": req.params.company}, {$push: {reviews: review.id}}, (err, company) => {
if (err) {
console.log("There was an error pushing review to company");
next(err);
} else {
//Updates the review by setting companyId and companyName properties to review for Mongo
Review.update({_id: review.id}, {$set: {companyId: this.companyId, companyName: this.companyName}}, (err, changes) => {
if(err) {
return res.status(400).json({message : err});
} else {
console.log("updating review successfully with company info", changes);
}
});
console.log ("Review successfully saved");
res.json({
review: review,
});
}
});
}
});
}
});
});
If anyone has feedback on how this could be done better/more efficiently, let me know. Cheers.
My goal is to build a simple news feed in node.js with the help of mongodb and redis. It similar like twitter
So the scenario is pretty straight forward, once User A follow User B. Later on User's A News feed (Home page) will be shown User B's Activity like what he posted.
Schema for User
const UserSchema = new Schema({
email: { type: String, unique: true, lowercase: true},
});
const followSchema = new Schema(
{
user: { type: Schema.Types.ObjectId, required: true, ref: 'User' },
target: { type: Schema.Types.ObjectId, required: true, ref: 'User' },
});
Currently the design of my user's schema is pretty simple, when I follow another user, I will just create the Follow Schema Object
and there is another schema, which is post schema
/* This is similar like the Tweet */
var PostSchema = new Schema({
// Own by the user
creator: { type: Schema.Types.ObjectId, ref: 'User' }
body: String,
});
This schema is for user to post anything, similar like twitter posting.
Let say I have followed bunch of users
{
user: 'me',
target: 'draco'
},
{
user: 'me',
target: 'donald'
},
{
user: 'me',
target: 'joker'
}
and let say one of my followers, post something. How do i present it to my current news feed?
/* I'm following Joker */
app.post('/follow', (req, res, next) => {
let follow = new Follow();
follow.user = "me";
follow.target = "joker";
// Do i need to use redis to subscribe to him?
follow.save();
})
/* Joker posted something */
app.post('/tweet',(req, res, next) => {
let post = new Post();
post.creator = "joker";
post.body = "Hello my name is joker"
post.save();
// Do i need to publish it using redis so that I will get his activity?
});
Here's my attempt
app.get('/feed', function(req, res, next) {
// Where is the redis part?
User.findOne({ _id: req.user._id }, function(err, foundUser) {
// this is pretty much my attempt :(
})
})
When should I use redis to actually do the pub and sub? so that I could take the content of one of my followers and show it on my timeline?
I have built a social network which has a news feed, too. Here is how I did it.
Basically, you have 2 methods to built a newsfeed:
Fanout on write (push) method
Fanout on read (pull) method
Fanout on write
First, you will need another collection:
const Newsfeed = new mongoose.model('newsfeed', {
owner: {type: mongoose.Types.ObjectId, required: true},
post: {type: mongoose.Types.ObjectId, required: true}
});
When a user post something:
Get n follower
Push (fanout) this post to n follower
When a user get a feed:
Get from Newsfeed collection
Example:
router.post('/tweet', async (req, res, next) => {
let post = await Post.create({});
let follows = await Follow.find({target: req.user.id}).exec();
let newFeeds = follows.map(follow => {
return {
user: follow.user,
post: post.id
}
});
await Newsfeed.insertMany(newFeeds);
});
router.get('/feed', async (req, res, next) => {
let feeds = await Newsfeed.find({user: req.user.id}).exec();
});
Fanout on read
When a user post something:
Save
When a user get feed
Get n following
Get posts from n following
Example
router.post('/tweet', async (req, res, next) {
await Post.save({});
});
router.get('/feeds', async (req, res, next) {
let follows = await Follow.find({user: req.user.id}.exec();
let followings = follows.map(follow => follow.target);
let feeds = await Post.find({user: followings}).exec();
});
You don't need Redis or pub/sub to implement a newsfeed. However, in order to improve the performance, you may need Redis to implement some kind of cache for this.
For more information or advance technique, you may want to take a look at this.
User Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const userSchema = new Schema({
name:{type:String},
email: { type: String, unique: true, lowercase: true},
},{
collection: 'User'
});
var User = module.exports = mongoose.model('User', userSchema);
Follow Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var followSchema = new Schema(
{
follow_id: { type: Schema.Types.ObjectId, required: true, ref: 'User' },
leader_id: { type: Schema.Types.ObjectId, required: true, ref: 'User' }
},{
collection:'Follow'
});
var Follow = module.exports = mongoose.model('Follow', followSchema);
Post Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var postSchema = new Schema({
creator: { type: Schema.Types.ObjectId, ref: 'User' }
body: {type: String , required:true},
created_at :{type:Date , default:Date.now}
},{
collection:'Post'
});
var Post = module.exports = mongoose.model('Post', postSchema);
Now Suppose you have 3 users in User collection :
{ _id: ObjectID('5a2ac68d1413751391111111') ,name:'John' , email:'john#gmail.com'}
{ _id: ObjectID('5a2ac68d1413751392222222') ,name:'Morgan' , email:'morgan#yahoo.com'}
{ _id: ObjectID('5a2ac68d1413751393333333') ,name:'Emily' , email:'emily#outlook.com'}
Now John Follows Morgan and Emily :
so in Follow collection there are two records
1) follow_id = John's ID and leader_id = Morgan's ID
2) follow_id = John's ID and leader_id = Emily's ID
{
_id: ObjectID('5a2ac68d141375139999999'),
follow_id : ObjectID('5a2ac68d1413751391111111'),
leader_id : ObjectID('5a2ac68d1413751392222222')
},
{
_id: ObjectID('5a2ac68d1413751393333333'),
follow_id : ObjectID('5a2ac68d1413751391111111'),
leader_id : ObjectID('5a2ac68d1413751393333333')
}
Now if you want to get User's Following :
app.get('/following/:user_id',function(req,res){
var userid=req.params.user_id;
Follow.find({follow_id:mongoose.mongo.ObjectID(userid)})
.populate('leader_id')
.exec(function(err,followings){
if(!err && followings){
return res.json({followings:followings});
}
});
});
for getting User's Followers :
app.get('/followers/:user_id',function(req,res){
var userid=req.params.user_id;
Follow.find({leader_id:mongoose.mongo.ObjectID(userid)})
.populate('follow_id')
.exec(function(err,followers){
if(!err && followers){
return res.json({followers:followers});
}
});
});
npm install redis
in your app.js :
var redis = require('redis');
var client = redis.createClient();
When one user create post :
app.post('/create_post',function(req,res){
var creator=new mongoose.mongo.ObjectID(req.body.creator);
var postbody=req.body.body;
async.waterfall([
function(callback){
// find followers of post creator
Follow.find({leader_id:creator})
.select({ "follow_id": 1,"leader_id":0,"_id": 0})
.exec(function(err,followers){
if(!err && followers){
callback(null,followers);
}
});
},
function(followers, callback){
// saving the post
var post=new Post({
creator: creator,
body: postbody
});
post.save(function(err,post){
if(!err && post){
// adding newly created post id to redis by key userid , value is postid
for(var i=0;i<followers.length;i++){
client.sadd([followers[i].follow_id,post.id]);
}
callback(null,post);
}
});
}
], function (err, result) {
if(!err && result){
return res.json({status:"success",message:"POST created"});
}
});
});
Now For Getting User NewsFeed :
1) first get array of postid from redis key of userid
2) loop through postid and get post from mongo
Function for get newsfeed by userid :
app.get('/newsfeed/:user_id',function(req,res){
var userid=req.params.user_id;
client.smembers(userid,function(err, reply) {
if(!err && reply){
console.log(reply);
if(reply.length>0){
var posts=[];
for(var i=0;i<reply.length;i++){
Post.findOne({_id:new mongoose.mongo.ObjectID(reply[i])}).populate('creator').exec(function(err,post){
posts.push(post);
});
}
return res.json({newsfeed:posts});
}else{
// No News Available in NewsFeed
}
}
});
});
Here we have use redis to store [userid,array of postids] for newsfeed ,
but if you dont want to use redis than just use below Newsfeed Model and store user_id and post_id for newly created post and then display it.
NewsFeed Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var newsFeedSchema = new Schema({
user_id: {type: Schema.Types.ObjectId, refer:'User' , required:true}
post_id: {type: Schema.Types.ObjectId, refer:'Post' , required:true},
},{
collection:'NewsFeed'
});
var NewsFeed = module.exports = mongoose.model('NewsFeed', newsFeedSchema);
Helpful link for Redis : https://www.sitepoint.com/using-redis-node-js/
for Async : https://caolan.github.io/async/docs.html#