I have a users model with a ref to company. My data is as follows:
{
"name":"justin",
"notes":"hello notes",
"company":
{"name":"acme inc"}
}
Is it possible to save my data as a single call or would I need to save my company model first, then save my user afterward?
var UserSchema = new Schema({
name: {
type: String,
trim: true,
default: ''
},
notes:{
type:String
},
company: {
type: Schema.ObjectId,
ref: 'Company'
})
In a NoSQL database everything is document oriented so you wouldn't typically do what you are trying to do. You will need to manage the "foreign key" relationship yourself, in code.
Related
I'm using Express and Mongoose to do some database actions. When trying to populate a path where the key includes a whitespace, it basically get's ignored.
MongoDB Model:
const OrgCrimeSchema = new Schema({
gracePeriod: { type: Date, default: Date.now() },
Technical: {
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
},
'Social Engineering': { // This one causes issues
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
},
});
Find + Populate:
const now = Date.now();
const allOrgCrimes = await OrgCrime.find({ gracePeriod: { $lte: now } })
.populate('Technical.owner', 'name')
.populate('Social Engineering.owner', 'name');
console.log(allOrgCrimes['Social Engineering'][0].owner);
//5fca3b4a86e77b5c8e58b683
console.log(allOrgCrimes['Technical'][0].owner);
// { name: 'npc_alice', _id: 5fae6d7ee60018434108369c }
I assume the path is not being populated because of a white space in the key. I've tried both dot notation and typing {path: 'Social Engineering', select: 'name -id'}, without luck.
Is there any way around this without having to rewrite the schema structure?
In short, there is no way, if we wanted to populate multiple paths at the same time there is this way
for exmaple
Story
.find(...)
.populate('book author') // space delimited path names
.exec()
you can see space delimited path names.
When you pass a key with space,mongoose consider it as populate multiple
As poined out by Mohammad Yaser Ahmadi, there is no solution to this issue today because of populate assumes you're populating multiple instead of trying to read a path name with a space in between. How I 'solved' this was to rewrite the model to this:
const OrgCrimeSchema = new Schema({
gracePeriod: { type: Date, default: Date.now() },
roles: [{
role: String,
description: String,
difficulty: Number,
owner: { type: Schema.Types.ObjectId, ref: 'User' },
}]
})
And then the populate actually get's easier:
.populate('roles.owner', 'name')
As a front-end developer, I would like to have some isomorphic object for two mongoose models.
Let's say I have a user profile:
const profileSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
// example of difference between model schemas
__user: { type: ObjectId, ref: 'Users' },
}
And I would like to create a list of Contact, where each contact will have some of the same keys:
const contactSchema = new Schema({
firstName: { type: String },
lastName: { type: String },
__profile: {
type: ObjectId,
ref: 'Profiles',
unique: true,
},
comment: { type: String },
}
NOTE: Contact could be the both:
as a reference to the Profile
and as independent record in DB / document.
==============================
My question: which is the best way to, organize models on such a way, so
contact could be a reference to the profile
when similar Profile key, like firstName will be updated, contact firstName will be updated too
AVOID of next ref
await Contact.findById(SOME_ID).populate('__profile');
// result
{
firstName: '',
lastName: '',
__profile: {
firstName: 'Chuck',
lastName: 'Norris',
}
}
Desired result - keep contact "isomorphic" like:
{
firstName: 'Chuck', // the key value from profile
lastName: 'Norris', // the key value from profile
__profile: SOME_PROFILE_ID,
}
Is this possible?
P.S: in my app, I'm using refs and started to use discriminators approaches.
I'd approach this task either:
1) put all data inside 1 collection (e.g. Profile):
// Profile model
{
firstName: 'Chuck',
lastName: 'Norris',
contacts: [{
type: ObjectId,
ref: 'Profile',
unique: true,
}],
...all other properties
}
that way you will be able to store just contacts (e.g. when I want to add just a contact) and profiles with much more info.
2) or will use discriminators to create a base class (e.g. Contact) and build Profile model upon it:
const options = { discriminatorKey: 'userType' };
const Contact = mongoose.model('Contact', new mongoose.Schema({
firstName: String,
lastName: String
},
options)
)
const Profile = Contact.discriminator(
'Profile',
new mongoose.Schema(
{
contacts: [{
type: ObjectId,
ref: 'Contact',
unique: true,
}],
comments: []
},
options
)
);
that way you will be able to save Contacts and Profiles inside 1 collection and reference base class (Contact) for contacts inside Profile
Hope that helps!
In my case, complete usage of Mongoose discriminators did not give me an advantage, because discriminators give you an opportunity to:
They enable you to have multiple models with overlapping schemas on
top of the same underlying MongoDB collection.
As a result, by using discriminators approach, I will reive one collection
of:
profiles
And there will be a mix of users and contact profiles.
==============================
So I decided to use two approaches:
create BesaSchema for the profile
take advantage of Mongoose Subdocuments
RESULT:
// keys which are same for both user Profile and Contact
const Schema = require('mongoose').Schema;
const util = require('util');
function BaseProfileSchema(...args) {
Schema.apply(this, args);
this.add({
firstName: { type: String },
lastName: { type: String },
});
}
util.inherits(BaseProfileSchema, Schema);
// user Profile Model
const profileSchema = new BaseProfileSchema({
__user: {
type: String,
ref: 'users',
required: true,
unique: true,
},
});
const Profile = mongoose.model('profiles', profileSchema);
// Contact with profile as subdocument
const contactProfileSchema = new BaseProfileSchema();
const contactSchema = new Schema({
// Associations
__refProfile: {
type: Schema.Types.ObjectId,
ref: 'profiles',
index: {
unique: true,
sparse: true,
},
},
profile: contactProfileSchema,
});
const Contact = mongoose.model('contacts', contactSchema);
As a result, I'm having DB with next collections:
users
profiles
contacts
Both profiles and contacts.profile are IDENTICAL because I'm extending the base shared schema.
Moreover:
inside Contact I'm having different keys for real referenced profile (__refProfile which can NOT be edit by others) and contact.profile
profile inside connection can be edited ONLY when the contact was edited by itself
P.S: happy codding 👨💻🍻
this is what I have and it works:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
This is what I want to achieve:
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component",
amount: {type: Integer}
}]
})
Is it possible in MongoDB, if not what is the best workaround?
Thank you :-)
This schema work because of an element or filed name is provided
var comboSchema = new Schema({
components: [{
type: Schema.Types.ObjectId,
ref: "Component"
}]
})
Now you made a single mistak you want to create schema name without name in object with two different filed
Right way to create schema like this is to make other variable inside of array which contain type of filed
var comboSchema = new Schema({
components: [{
id: {
type: Schema.Types.ObjectId,
ref: "Component"
},
amount: { //If you want to make every component amount
type: Number
}
}]
})
Or
var comboSchema = new Schema({
amount: { type: Number },
//If you want to make only single amount on multiple components
components: [{
componentId: {
type: Schema.Types.ObjectId,
ref: "Component"
}
}]
})
But in both case you can't populate directly. You need to use aggregation for that to get data for embedded documents.
I am having difficulty coming up with schemas for a school app.
In particular, I am trying to model the relationship between the different kinds of users (e.g. instructors, teaching assistants, and students) with the courses and tutorials that they belong to.
Here are my requirements:
each course will have one to many tutorials;
each course will be taught by one to many instructors;
each course will have one to many students;
each tutorial will have one to many teaching assistants;
each instructor will teach one to many courses;
each teaching assistant may have one to many tutorials in one to many course;
each student will be enrolled in one to many courses;
each student may belong to one tutorial in the course that they are enrolled in;
So far, the following are my schemas for the user, course, and tutorial collections.
var CourseSchema = new mongoose.Schema({
name: { type: String, required: true },
code: { type: String, required: true },
tutorials: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tutorial' }], // 1
instructors: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }], // 2
students: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 3
});
var TutorialSchema = new mongoose.Schema({
number: { type: String, required: true },
teachingAsst: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 4
});
var UserSchema = new mongoose.Schema({
email: { type: String, lowercase: true },
password: String,
name: {
first: { type: String, lowercase: true },
last: { type: String, lowercase: true }
},
roles: [String] // instrutor, teachingAsst, student
};
The problem lies with my requirements 5 to 8 -- which is more so the relationship from the User to the other models. What could be a good way to model these relationships?
One way, I thought of doing it e.g. req 5 was to add a field to the User schema
instructor: {
courses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }]
}
But the problem happens when I do e.g. req 6. similarly because it will complicate the queries (e.g. "find all the tutorials in a course that the user is a teaching assistant in").
teachingAsst: {
courses: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Course' }]
tutorials: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Tutorial' }]
}
In your case, Design is many to many relations. So you have two approach for your problem.
Reference Document
Embedded Document
Embedded approach will have duplicate data which is difficult to update and delete where as the read operation will be much efficient due to single query.
In case of the Reference Approach, your data will be demoralized. So, update and delete operation will be easy where as the read operation will have multiple hits on the database.
So, based on the your application requirement you should have to decide the appropriate approach.
So I have practically worked with relational databases only.
Now I'd like to do my first real project with mongo.
I need a role-based access to my app.
In a relational DB, I'd probably put a column 'role' in my 'user' table, having 'role' being referenced by a table of roles.
Maybe this is not quite right for a NOSQL db?
In other words, is it best practice to store the Role just as a string?
E.g.
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: String }
});
or would it be something like:
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: Role }
});
(if that's possible at all)
you could have another collection called authority that holds the different authoritys and their names as a string like :
{
"_id": ObjectId("51644d0884969b4512"),
"authority": "ROLE_USER",
}
and then your user-object has a field with a refference
{
"_id": ObjectId("5123432969d434553"),
"password": "notSave",
"name":"user0",
"role:ObjectId("51644d0884969b4512")
}
and your code would be sth like
user = db.user.find({name:"user0"});
userRole = db.authority.find({"_id":user.id})
but if you wanted to get all user with role admin for example, then you need to write iterative code
i made the experience that this can get very messy, especially while iterating over more collections with abstract querys, even with ensured indexes the runtime got longer and longer because sth like table-joins in a single query seems to be not possible, or i did not find out
nevertheless i like mongodb, maybe it helps you
nevertheless you can and you better avoid assosiated documents in other collections and store it in the same document
and as a string
var UserSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
role: { type: String }
});
MongoDB and "joins"