Inserting data to MongoDB from one schema module to another - node.js

I'm using MongoDB with Mongoose, I have to save user data to users collection which contains username, password and company data, *Company data will be having Company name, Address, and website URL
Now what I've done is I've created the different schema for company and users
I need to store user data along with the company ID after installing it into the company collection, for ex.
{
username: "Lorem Ipsum",
password: "Dolar Sit",
company: "5c73afcf9a3bde1a40da5184"
}
something like this (Note: That random string is ID of the company data), but I'll send everything in 1 object like below
{
username: "Lorem Ipsum",
password: "Dolar Sit",
company: {
company_name: "Blah Blah",
company_address: "Blah Blah Blah",
company_website: "BlahBlah.com",
}
}
My user Schema (user.js) is :
const mongoose = require('mongoose');
const Company = require('./company');
Schema = mongoose.Schema
// User Schema
const userSchema = mongoose.Schema({
username:{
type: String,
required: true
},
password:{
type: String,
required: true
},
company:{
type: [{ type: Schema.Types.ObjectId, ref: 'Company' }],
required: true
},
created_on:{
type: Date,
default: Date.now
},
updated_on:{
type: Date,
default: Date.now
}
});
const User = module.exports = mongoose.model('Users', userSchema);
// Add User
module.exports.addUser = (user, callback) => {
User.create(user, callback);
}
And Company Schema is (company.js) :
const mongoose = require('mongoose');
// Book Schema
const companySchema = mongoose.Schema({
company_name:{
type: String
},
target_address:{
type: String
},
target_website:{
type: String
},
created_on:{
type: Date,
default: Date.now
},
updated_on:{
type: Date,
default: Date.now
}
});
const Company = module.exports = mongoose.model('Company', companySchema);
// Add Company
module.exports.addCompany = (comp, callback) => {
Company.create(comp, callback);
}
Now My question is if I run Users.addUser function, and pass the above json to it, it should save/create user along with the company. and company ID should be saved in company property of user collection. if I do get users then it should return User data with company data fetched using that ID saved in the database
How to do that?
If I run the above files and try to insert data to it,
It'll show the below error
'Cast to Array failed for value "{ company_name: \'Moshi Moshi\', company_address: \'Bengaluru\' }" at path "company"',
name: 'CastError',
stringValue:
'"{ company_name: \'Moshi Moshi\', company_address: \'Bengaluru\' }"',
kind: 'Array',
value: [Object],
path: 'company',
reason: [MongooseError] } },
_message: 'Users validation failed',
name: 'ValidationError' }
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
E:\MoshiMoshi\Projects\Maaamamia\blackhole-rest\models\users.js:17
type: [{ type: ObjectId, ref: 'Company' }],
^
ReferenceError: ObjectId is not defined
how to achive that functionality?

first of all in your user schema company should not be an array because an user will have only single company Id according to your requirement.
//incorrect
company:{
type: [{ type: Schema.Types.ObjectId, ref: 'Company' }],
required: true
},
//correct
company:{
type: Schema.Types.ObjectId,
ref: 'Company',
required: true
},
And second while adding user you are passing entire company object to the company attribute.
first you should add company and then store _id of saved company object into company attribute of user then save user object.
let yourJsonObject={
username: "Lorem Ipsum",
password: "Dolar Sit",
company: {
company_name: "Blah Blah",
company_address: "Blah Blah Blah",
company_website: "BlahBlah.com",
}
}
let companyObject=yourJsonObject.company;//your company object
let userObject= yourJsonObject //your user object
let savedCompanyObject= //call add company method of your
//model with companyObject data
userObject.company = savedCompanyObject._id;
//now pass userObject to your addUser method of user model

Related

Mongoose - Reference a field from another table

I have two tables: Post and User and I want to reference the User name in the Post table.
I can reference the ID but I also need the name.
User model:
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
})
module.exports = mongoose.model("User", userSchema)
Post model:
const { ObjectId } = mongoose.Schema.Types
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
author: {
type: ObjectId,
ref: "User",
},
})
module.exports = mongoose.model("Post", postSchema)
In the doc they said:
Note: ObjectId, Number, String, and Buffer are valid for use as refs.
However, you should use ObjectId unless you are an advanced user and
have a good reason for doing so.
However I couldn't find an example on how I can do this.

Mongoose query compare ObjectId

I'm trying to fetch some documents from my db. In each document, there is a field called 'owner' which is an ObjectId of a user. I want to fetch all of the documents of a specific user. I have the user id and when I'm trying to do something like this:
exports.getBoxes = function(req, res) {
const { user } = res.locals;
const query = db.Box.find();
query.where('owner').equals(user._id);
query.exec(function(err, boxes) {
console.log(boxes);
});
}
I get an empty array. I saw in my db and there are many boxes that corresponds to this query. What's wrong with it?
UPDATE
Here is my schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const timestamps = require('mongoose-timestamps');
const BoxSchema = new Schema({
description: {
type: String,
trim: true
},
producer: {
type: String,
trim: true
},
cycle: {
type: String,
trim: true
},
owner: {
type: Schema.ObjectId,
ref: 'Supplier'
},
event: {
type: Schema.ObjectId,
ref: 'Event'
},
type: {
type: String,
enum: []
},
creditTerms: {
type: String,
enum: ['Cash', '30 Days', '60 Days', '90 Days', '120 Days']
},
bids: [{
type: Schema.ObjectId,
ref: 'Bid'
}],
looking: [{
type: Schema.ObjectId,
ref: 'User'
}],
sold: Boolean,
paid: Boolean,
delivered: Boolean,
sealed: Boolean,
initialPrice: Number,
value: Number,
cts: Number,
ppc: Number,
finalPrice: Number
});
BoxSchema.plugin(timestamps);
module.exports = mongoose.model('Box', BoxSchema);
And here is an example of documents that I try to fetch:
https://i.gyazo.com/38f2d16d6831b831adb3cc448ef74d01.png
Okay guys I managed to solve this problem. The problem was that the owner field in the box schema referenced a Supplier object, not a User object. So I solved it like so:
const { user } = res.locals;
return db.Supplier.findOne({ userId: user._id })
.populate('boxes').exec(function(err, supplier) {
if(err || !supplier) return res.sendStatus(404);
res.json(supplier.boxes);
});

MongoDB complex associations

I am trying to figure out how to structure my mongoose models. I have a User model, a Task model, and Project model. Projects will have users, and each user will have tasks for that specific project. The way I have this set up is the User model has a schema reference to Project model and the Task model has a scheme reference to a user. How can I do this so that when I render the information retrieved, each project will show its relevant members and each members will have their relevant tasks for that particular project. I also have an admin property of the User model which is just a boolean set default to false. The purpose of this is so that when a user created a team, the Admin property will be set to True allowing the admin to set tasks for users in the project created by admin. The problem with this is, after a team create by the user, if the admin is set to true, the ternary condition on my front end that enables a form input to show based on the boolean value of the 'admin' property will show up for all projects, even for ones the user is not an admin of.
I am using React for the rendering.
//Tasks Models
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TodoSchema = new Schema({
task: {
type: String
},
userTasks: [{
type: Schema.Types.ObjectId,
ref: "User"
}]
});
var Task = mongoose.model('Task', TodoSchema);
module.exports = Task;
//Users model
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: {
type: String,
trim: true,
required: "First Name is Required"
},
username: {
type: String,
trim: true,
required: "Username is Required"
},
skills: {
type: String,
trim : true
},
avatar: {
type: String
},
SQLid: {
type: Number
},
userCreated: {
type: Date,
default: Date.now
},
lastUpdated: {
type: Date
},
userAdmin: {
default: false
},
adminTeams: [{
type: Schema.Types.ObjectId,
ref: "Team"
}],
team: [{
type: Schema.Types.ObjectId,
ref: "Team"
}],
task: [{
type: Schema.Types.ObjectId,
ref: "Task"
}]
});
var User = mongoose.model("User", UserSchema);
// Export the model so the server can use it
module.exports = User;
//Projects model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TeamSchema = new Schema({
teamname: {
type: String,
trim: true,
required: "Team Name is Required"
},
description: {
type: String
},
tech: {
type: String
},
teamMembers: [{
type: Schema.Types.ObjectId,
ref: "User"
}]
});
var Team = mongoose.model('Team', TeamSchema);
module.exports = Team;
Once a User creates a team/project, they become an admin of that created team. Admins have the authority to assign task to Users, and Users can belong to many teams. I am thinking about moving the admin boolean to the Projects/Team model and giving that property the _id of the User once they create a team and then Ill use those as keys to match and use a ternary to render a form if the project they are viewing is one they created. However, I am still confused on how I can associate each user with a task, and users can belong to many teams, so I need the tasks that users have to be in the correct Project/Team section.
A lay of what I am talking about
//Projects Page (the div below is just one project out of many listed on the projects page
<div>
Project/Team 1
User Name -> User Task
User Name -> User Task
User Name -> User Task
...
</div>
<div>
Project/Team 2
User Name -> User Task
User Name -> User Task
User Name -> User Task
...
</div>
In general you should review the mongodb documentation on associations
When you're dealing with mongodb you may want to look into holding more associated information on your documents then you would in an sql environment. Theres a good top level answer here about designing your data to use mongodb well.
Your admin problem sounds like a coding issue that will not be solved through your db. I'm hard pressed to see why your form would need a ternary at all if it's displaying on a boolean field, but can't provide any further help then:
if (user.admin) {//form}
without snippets or information on your front end architecture or the ternary in question . It sounds like you may be overcomplicating things for yourself.
edit for your update: I would change your model so projects store the user who created them.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TeamSchema = new Schema({
teamname: {
type: String,
trim: true,
required: "Team Name is Required"
},
description: {
type: String
},
tech: {
type: String
},
teamMembers: [{
type: Schema.Types.ObjectId,
ref: "User"
}],
// now holds the 'admin' of the team. you can keep the admin naming
//if you like
creator: [{
type: Schema.Types.ObjectId,
ref: "User"
}]
});
var Team = mongoose.model('Team', TeamSchema);
module.exports = Team;
also your user model could use some naming improvements, also an admin field on the user column is often used for application admins like yourself, as opposed to an admin inside a project.
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: {
type: String,
trim: true,
required: "First Name is Required"
},
username: {
type: String,
trim: true,
required: "Username is Required"
},
skills: {
type: String,
trim : true
},
avatar: {
type: String
},
SQLid: {
type: Number
},
// you might want to look up timestamps createdAt and updatedAt
// instead of this field
userCreated: {
type: Date,
default: Date.now
},
lastUpdated: {
type: Date
},
userAdmin: {
default: false
},
// this could be removed as its is now stored on the Team schema
adminTeams: [{
type: Schema.Types.ObjectId,
ref: "Team"
}],
// this should be plural as it represents a one to many
teams: [{
type: Schema.Types.ObjectId,
ref: "Team"
}],
// tasks is removed because you can find them from the Task schema
});
var User = mongoose.model("User", UserSchema);
// Export the model so the server can use it
module.exports = User;
Since your task model needs to be team specific it can look like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TodoSchema = new Schema({
task: {
type: String
},
// I would suggest an 'assignedTo', 'owner', or 'taskOwner' naming
// as user may be a bit non-descriptive.
user: [{
type: Schema.Types.ObjectId,
ref: "User"
}],
team: [{
type: Schema.Types.ObjectId,
ref: "Team"
}]
});
var Task = mongoose.model('Task', TodoSchema);
module.exports = Task;
There are a lot of ways to load the information you want with the schema, but given this slight restructuring you could get all the information you need like this:
var tasks;
Team.findOne({'id': the_team_id}, function(err, team) {
if (err) return handleError(err)
Task.find({'team': team}).populate('user').exec(function(err, foundTasks) {
tasks = foundTasks
})
})
You can now check your admin by doing something along this line:
team.creator.id === user.id
mogoose populate

Select specific field from documents in mongoose

Hi I have three Schemas with this field's
var interactionSchema = new Schema({
pollee: { type: ObjectId, ref: 'Pollee' },
answers: { type: [ObjectId], ref: 'Answer', autopopulate: true },
status: type: String
});
var PolleeSchema = new Schema({
firstName: String,
lastName: String,
gender: String,
user: { type: ObjectId, ref: 'User', required: true },
interactions: { type: [ObjectId], ref: 'Interaction', autopopulate: true }
});
var userSchema = new Schema({
email: String,
pollee: { type: Schema.Types.ObjectId, ref: 'Pollee', autopopulate: true }
});
I need to realize a query in Pollee and pupulate fields from user and interactions, but from interactions I only need the status field, I dont need answers and other ObjectID arrays that contain the Interactions colection.
I made this Query
Pollee.find(req.body.filters)
.select('id age birthday country device_register gender municipality state parish user interactions.status')
.populate('user','email createdAt')
.exec(function(err,pollees){
//Other
}
//....
This return interactions array but status field is not returned. If I write only interactions on the select, return interactions with all fields from interactions.
I try this
Pollee.find(req.body.filters)
.select('id age birthday country device_register gender municipality state parish user')
.populate('user','email createdAt')
.populate('interactions', 'status')
.exec(function(err,pollees){
//Other
}
//....
This return nothing from interactions
I need only Status field from interactions.
The problem here is the interactions field from Pollee Schema, this field its a Array of ObjectsId, and this field its autopopulated from Schema. I want to select only the status field from interactions
Any idea?
I see that you are using mongoose-autopopulate.
It is stated in their document that you can do like this:
var PolleeSchema = new Schema({
//...
interactions: { type: [ObjectId], ref: 'Interaction', autopopulate: { select: 'status' } }
});

using a key value other than Schema.Types.ObjectId in mongoose

I am creating a collection w/n mongo using mongoose to populate related users. I have clinicians and patients. I want patients to have an array of clinicians as they are enrolledwith. Later I'll load a dashboard for clincian based on patient user "enrolledWith: "
Likely clincians will provide their email to patients to be marked in rolled. emails are username.
Can I use username for the Schema.Types.username?
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
name: String,
phoneNumber: String,
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
patient: {
// role: Boolean,
enrolledWith: [
{
type: mongoose.Schema.username,
ref: 'User'
}
],
gameTotalScore: Number,
},
// clinician: {
// role: Boolean
// },
role: {
type: String,
required: true
},
});

Resources