In MongoDB, I have a One-To_many reference relationship.
A has many B.
A has a property called B_ids, so I can retrieve all the B instances owned by a a particular A instance.
My question is: looking to an instance of B, how can I retrieve the A instance that owns it?
Thanks!
In order to do that you can try this:
var personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
in this way you can retrieve A from B using populate.
Story.find().populate('author')
Example borrowed from mongoose populate website.
Related
Suppose I have two schemas on MongoDB:
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
email: String,
things: [{ type: Schema.Types.ObjectId, ref: 'Thing' }]
});
const thingSchema = Schema({
_id: Schema.Types.ObjectId,
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
Every time a user logs in, I would like to show the things that they have posted, as well as the fans that are following each of the things. I am able to use populate and select to get to that:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: "things",
select: ["title", "fans"]
}),
However, I am only getting the id of each fan, and not the fan's name and email. I can't quite figure out how to use populate to reference the person collection again.
The outcome I am trying to achieve is that:
the user object would have an array of things
the thing object would have an array of fans
the fan object would have two values - name and email of the fan
You can do nested population with:
const user = await personModel
.findOne({ _id: req.user._id })
.populate({
path: 'things',
select: ['title', 'fans'],
populate: { path: 'fans' },
})
.exec();
This is example from mongoose docs about populate:
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
const storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
});
So, person has list of stories and when we fetch persons we can include stories by using populate('stories'). So far so good.
But in order for that to work, when creating Story, we need to add storyId to stories list in Person. I am coming from SQL background, where that does not need to be done, it would find related stories automatically based on authorId on Story.
So the question is, can it be done in same way here, without need to update stories property on Person?
I found a solution, its called virtual property:
AuthorSchema.virtual('posts', {
ref: 'BlogPost',
localField: '_id',
foreignField: 'author'
});
This way I can populate posts in query without saving postIds in Author/Person schema.
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 would like to setup a "like" system in my app. User should be able to like either Posts or Comments (Comments of a Post of course). How should I design this?
Users
const userSchema = new Schema({
id: { type: String, required: true },
username: { type: String, required: true },
password: { type: String, required: true },
});
Posts
const postSchema = new Schema({
content: { type: String, required: true },
authorId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }
});
Comments
const commentSchema = new Schema({
content: { type: String, required: true },
authorId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
postId: { type: mongoose.Schema.Types.ObjectId, ref: "Post", required: true },
});
Likes
const likeSchema = new Schema({
content: { type: String, required: false },
authorId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
postId: { type: mongoose.Schema.Types.ObjectId, ref: "Post", required: function() { return this.commentId? false : true } },
commentId: { type: mongoose.Schema.Types.ObjectId, ref: "Comment", required: function() { return this.postId? false : true } }
});
I'm coming from relational databases, and maybe my design is completely wrong for nosql. My main interrogation is about Likes, I have no idea how to accept likes on Posts OR Comments.
I would prefer a separate collection:
User:
id:
...
Post:
id:
userId:
...
Comment:
id:
userId:
postId:
Like:
id:
userId:
postId:
commentId:
The second one storing an array will lead you cyclic dependencies in the backend. Especially, when you use NodeJS and strict to flow.
MongoDB is powerful at storing documents. Documents hold the relations.
I would model it in the way your data is being accessed. I do recommend playing around with the powerful aggregation framework and array operators to experience the possibilities. What I would explore is the following
User:
id:
name:
picture:
...
Posts:
id:
authorid:
content:
total_views:
tags: array of String
likes: array of Likes {[
liked_by: user_id
],...}
comments: array of Comments {[
author_id: ...
comment: ...
reactions: array of Comments {[],...}
likes: array of Likes {[
liked_by: user_id
],...}
],...}
Will this model scale? Documents can hold 16MB of data. 16MB in textual format is HUGE.
PS please think again on storing username/password in the database. This is a whole other discussion. Look into the topics of authentication, authorisation, OAuth, hashing/salting etc.
post={
...keys,
likes:[likeSchema],
comments:[CommentSchema]
}
this is i prefer, even if you want to store recursive comments just use
commentschema={
id:unique commet id
text:...
user_id:who wrote this comment
parent_id: to which this comment belongs to!
depth: comment depth as your wish (mostly 2)
}
parent id will be null for a comment posted directly on post
parent id will be comment_id of the comment to which this comment posted for. if its a recursive comment.
hope you get it.
Since, the question is about schema for like a comment or post. I'll focus on likes.
Build a schema like this. Here targetId will be postId or commentId.
const likeSchema = new Schema({
content: { type: String, required: false },
authorId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
targetId: { type: mongoose.Schema.Types.ObjectId, ref: "Post", required: function() { return this.commentId? false : true } }
});
Some points you need to consider:
Store likes of posts in post collection
Store likes of comments in comments collection
You need to build a mechanism to calculate likes and store in that collection
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.