I am using mongoose and have two schemas, user and community:
UserSchema:
const UserSchema = new Schema({
name: String,
communities: [{ type: Schema.Types.ObjectId, ref: CommunityModel }],
}, { timestamps: true });
}
Community:
const CommunitySchema = new Schema({
name: String,
users: [
{ type: Schema.Types.ObjectId, ref: "User" }
]
}, { timestamps: true });
When user joins a community, I want to know when it happened, so I'd like to add a joinedAt or createdAt to each community in the UserSchema. How could I achieve it if it is a reference? Is it possible? And if not, what could be the alternative?
Thanks!
This is the correct answer:
AI solved it for me, here is the correct example:
import * as mongoose from 'mongoose';
const Schema = mongoose.Schema;
interface User {
name: string;
email: string;
communities: Community[];
}
interface Community {
name: string;
description: string;
users: User[];
}
const userSchema = new Schema({
name: String,
email: String,
}, {
timestamps: true,
});
const communitySchema = new Schema({
name: String,
description: String,
}, {
timestamps: true,
});
// Define the user_communities table using the communitySchema
const userCommunitySchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
community: { type: Schema.Types.ObjectId, ref: 'Community' },
joined_on: Date,
}, {
timestamps: true,
});
// Use the userCommunitySchema to create the UserCommunity model
const UserCommunity = mongoose.model('UserCommunity', userCommunitySchema);
// Use the userSchema to create the User model, and define a virtual property
// for accessing the user's communities
userSchema.virtual('communities', {
ref: 'Community',
localField: '_id',
foreignField: 'user',
justOne: false,
});
const User = mongoose.model<User>('User', userSchema);
// Use the communitySchema to create the Community model, and define a virtual property
// for accessing the community's users
communitySchema.virtual('users', {
ref: 'User',
localField: '_id',
foreignField: 'community',
justOne: false,
});
const Community = mongoose.model<Community>('Community', communitySchema);
The userSchema and communitySchema are then used to create the User and Community models, respectively. For the User model, a virtual property called communities is defined using the virtual method. This virtual property is used to specify how to populate the user.communities property when querying the database. The communitySchema also defines a users virtual property, which is used to populate the community.users property when querying.
Related
everyone.
So I have this "blog" app where users can create posts with images.
How my app works is that it loads different posts by userID.
So I have a relation bewtean user and post by the user and post _id, however I also want to save username into the post schema and created ralation that way. Is it possible to do such thing ?
This is my User schema
import mongoose from "mongoose";
import mongooseUniqueValidator from "mongoose-unique-validator";
const Schema = mongoose.Schema;
const validator = mongooseUniqueValidator;
const user_Schema = new Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true, minlength: 3 },
user_image: { type: String },
posts: [{ type: mongoose.Types.ObjectId, required: true, ref: 'Post' }], //relation bewtean post and user
},{ timestamps: true }
);
user_Schema.plugin(validator);
export const USER: mongoose.Model<any> = mongoose.model("User", user_Schema);
And this is my Post Schema:
import mongoose from "mongoose";
const Schema = mongoose.Schema;
const post_Schema = new Schema({
title: { type: String, required: true, },
description: { type: String, required: true, },
imageURL: { type: String },
creator_id: { type: mongoose.Types.ObjectId, required: true, ref: 'User' },
},
{ timestamps: true }
);
export const POST: mongoose.Model<any> = mongoose.model("Post", post_Schema);
However this is what I want the post to contain, I want the post to contain ID of the user who created it and the name of the user who created it.
However I do not know how to create it. So this is how I want my Post schema to look like, I want to be able to save both the user ID and username into the post.
const Schema = mongoose.Schema;
const post_Schema = new Schema({
title: { type: String, required: true, },
description: { type: String, required: true, },
imageURL: { type: String },
user: {
creator_id: { type: mongoose.Types.ObjectId, required: true, ref: 'User' }, //relation bewtean post and user
creator_name: { <soemthing here>, ref: 'User' }, //relation bewtean post and user
},
},
{ timestamps: true }
);
export const POST: mongoose.Model<any> = mongoose.model("Post", post_Schema);
If you want to retrieve the creator name using the ref is the correct approach, you just need to populate the Post documents when you are retrieving them with:
const posts = await Post.find({}).populate('creator').exec()
for (const post of posts) {
// Every post should contain the creator user properties
console.log(post.creator._id, post.creator.username)
}
Just make sure that your ref fields are of type mongoose.Schema.Types.ObjectId:
const post_Schema = new Schema(
{
title: { type: String, required: true },
description: { type: String, required: true },
imageURL: { type: String },
creator: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
},
{ timestamps: true }
);
export const POST: mongoose.Model<any> = mongoose.model('Post', post_Schema);
I have a project with the following document flow.
Users -> Accounts -> Projects
Users
Users have specific roles
Accounts
CRUD conditioned by User role
Specific users will have access to individual accounts. I was thinking to add an array userGroup with user id's?
Projects
Nested under Accounts related to a single accountToken ID
CRUD conditioned by User role
Specific users will have access to individual projects.
Here are example Schema models simplified for demo purposes.
UserSchema.js:
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Please add an email'],
unique: true,
},
role: {
type: String,
enum: ['admin', 'user'],
default: 'user'
}
});
module.exports = mongoose.model('User', UserSchema);
AccountSchema.js:
const AccountSchema = new mongoose.Schema({
accountName: {
type: String,
},
accountToken: {
type: String
}
});
module.exports = mongoose.model('Account', AccountSchema);
ProjectSchema.js:
const ProjectSchema = new mongoose.Schema({
projectName: {
type: String,
},
projectType: String,
projectToken: String
})
module.exports = mongoose.model('Project', ProjectSchema);
I am stuck on the best way to setup nested or sub-document Schema Relations and the best way to relate the data between each other. Any guidance and suggestions would be a huge help! Thanks!!!
Try this;
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Please add an email'],
unique: true,
},
role: {
type: String,
enum: ['admin', 'user'],
default: 'user'
}
});
module.exports = mongoose.model('User', UserSchema);
const AccountSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
accountName: {
type: String,
},
accountToken: {
type: String
}
});
module.exports = mongoose.model('Account', AccountSchema);
const ProjectSchema = new mongoose.Schema({
accountTokenId:{
type: String
},
projectName: {
type: String,
},
projectType: String,
projectToken: String
})
module.exports = mongoose.model('Project', ProjectSchema);
I am trying to just return the populated profile document from the User Document. But I can't seem to code in the logic correctly...What is the way to go about this? I only want to select the profile field to return from the user. I thought of maybe trying .select("profile).populate({ path: "profile"}), but that didn't work either.
User.findOne({ _id: req.user._id })
.populate({ path: "profile"}) <---
.then(user => {
res.json(user);
})
.catch(() => res.json({ Error: "User Account type is not valid" }));
User Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
profile: {
type: Schema.Types.ObjectId,
refPath: "profile_type"
},
profile_type: {
type: String,
enum: ["PartnerAdminProfile", "DistrictAdminProfile", "MentorProfile"]
},
});
module.exports = User = mongoose.model("User", UserSchema);
Profile Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const PartnerAdminProfileSchema = new Schema({ <----EDIT redefined schema name
user: {
type: Schema.Types.ObjectId,
ref: "User"
},
profile_picture: {
type: Schema.Types.ObjectId,
ref: "Image",
date: {
type: Date,
default: Date.now
}
}
});
module.exports = PartnerAdminProfile = mongoose.model("PartnerAdminProfile", PartnerAdminProfilechema);
expected output:
profile: {
user: "6546556654564",
profile_picture :{url:https://test.jpg}
}
This is a MERN app, hosted on github, and it works perfectly on localhost. Unfortunately, it does not work on Heroku.
The issue is the API request, it must return an object and populate an array of OIDs (see Department Model). API request is working. I'm getting the data from MLab, but it doesn't populate... instead returns: "surveys":[]
API File
router.get('/department_data/:d_oid', function(req, res) {
Department.findOne({_id: req.params.d_oid}).populate("surveys").exec(function(err,doc){
if(err) throw(err)
res.send(doc)
})
});
Department Model
**Department Model**
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var departmentSchema = new Schema({
department_name: {
type: String,
trim: true,
required: true
},
surveys: [{
type: Schema.Types.ObjectId,
ref: 'Surveys'
}],
participants: [{
type: String
}],
create_date: {
type: Date,
default: Date.now
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'Created_By'
},
});
departmentSchema.index({ department_name: 1, created_by: 1}, {unique: true});
const Department = mongoose.model('Departments', departmentSchema);
module.exports = Department;
Survey Model
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var surveySchema = new Schema({
survey_name: {
type: String,
trim: true,
required: true
},
questions: [{
type: Schema.Types.ObjectId,
ref: 'Questions'
}],
created_date: {
type: Date,
default: Date.now
}
});
const Survey = mongoose.model('Surveys', surveySchema);
module.exports = Survey;
Solved.
The problem was in the database: the ref OIDs got scrambled with a previous update, so when it was trying to populate, Mongoose couldn't find any matching OIDs.
Solution: we had to purge and re-seed. When the correct OID references exist, this code works as expected in localhost & Heroku.
**I have answered below. In short you need to require the Model in the module in which you wish to populate, even though you do not refer to it directly.
I am hitting a strange problem with mongoose when populating just one particular array of IDs.
I have three models, User, Company and Widgets.
When I return the company populated with the users all is fine using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
However when I try to replace populate 'users' with 'widgets' I get the following error:
{
"message": "Schema hasn't been registered for model \"widget\".\nUse mongoose.model(name, schema)",
"name": "MissingSchemaError"
}
Here are the models:
USER:
const UserSchema = new Schema({
name: String,
email: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
company: {
type: Schema.Types.ObjectId,
ref: 'company'
}
});
const User = mongoose.model("user", UserSchema);
COMPANY:
const CompanySchema = new Schema({
name: String,
URL: {
type: String,
unique: true
},
users: [{
type: Schema.Types.ObjectId,
ref: 'user'
}],
widgets: [{
type: Schema.Types.ObjectId,
ref: 'widget'
}]
});
const Company = mongoose.model('company', CompanySchema);
WIDGET:
const WidgetSchema = new Schema({
title: {
type: String,
required: true
},
maker: String
});
const Widget = mongoose.model('widget', WidgetSchema);
I have manually inspected the _ids in the widget array of the company model and they are all correct.
OK, so this was a lack of understanding on my behalf.
In the module where I was using:
Company.findOne({ name: 'xyz' })
.populate('users')
.exec(function(err, company) {
if (err) return res.send(err)
res.send(company)
})
I had imported the User model for other uses in the module. However, as I was not directly referring to Widget I had not imported it. Having done some more research I found that you need to import a model when populating even though not referring to it directly.
Let me know if best to delete whole thread or leave for reference.