User specific calculated attibute in MEAN.js MongoDB query - javascript

I'm building an Restful API using Mean.js.
I have an Article Model:
var ArticleSchema = new Schema({
title: { ... },
content: {...},
...
}
And an User Model, which has an array of the Articles the user bookmarked.
var UserSchema = new Schema({
name: { ... },
...
bookmarks: [{ type: Schema.ObjectId, ref: 'Article' }]
}
I want to attach to the Articles objects retrieved from the DB a calculated boolean attibute 'Bookmarked', indicating if the logged user has bookmarked the article.
I'll also have to repeat the logic to show which articles the user has already read, and which ones are new to him, and maybe an attribute to rate or like/dislike the article. All of them involving the same problem.
How can I do this? How the best way?
I can do this on the DB level or Express level. I'm trying to send to the user the field already calculated in the JSON he'll receive.
I'm open to any solutions, and willing to change the model, relationships or the logic if necessary.

I will suggest something like this;
var ArticleSchema = new Schema({
title: { ... },
content: {...},
...
bookmarkedBy : [{ type: Schema.ObjectId, ref: 'User' }]
readBy : [{ type: Schema.ObjectId, ref: 'User' }]
likedBy : [{ type: Schema.ObjectId, ref: 'User' }]
dislikedBy : [{ type: Schema.ObjectId, ref: 'User' }]
}
var UserSchema = new Schema({
name: { ... },
...
bookmarks: [{ type: Schema.ObjectId, ref: 'Article' }]
likes: [{ type: Schema.ObjectId, ref: 'Article' }]
dilikes: [{ type: Schema.ObjectId, ref: 'Article' }]
bookmarks: [{ type: Schema.ObjectId, ref: 'Article' }]
}

Related

How to implement a deep populate using the Mongoose, to properly output a M:N database relation?

I am trying to create a basic social media website, with post having different comments, likes, comments also having likes. If the logged in user is the one that made the comment or post, he could be able to delete the post.
So I have to use the deep populate method of mongoose, but the issue I am having is, when the content of the comment is showing, then the name of the user that made that comment is missing.
Post Schema is as follows
content: {
type: String,
required: true
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
// include the array of ids of all comments in this post schema itself
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Like'
}
]
},{
timestamps: true
});
Like Schema is as follows -
const likeSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId
},
// this defines the object id of the liked object
likeable: {
type: mongoose.Schema.ObjectId,
require: true,
refPath: 'onModel'
},
// this field is used for defining the type of the liked object since this is a dynamic reference
onModel: {
type: String,
required: true,
enum: ['Post', 'Comment']
}
}, {
timestamps: true
});
User Schema is as follows -
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: {
type: String
}
}, {
timestamps: true
});
Comment Schema is as follows -
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
// comment belongs to a user
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Like'
}
]
},{
timestamps: true
});
And this is the populate function, on the front end I am trying to display all possible posts with all possible likes and comments, with all the comments also having likes. And obviously the name of the user that made the comment -
let posts = await Post.find({})
.sort('-createdAt')
.populate('user')
.populate({
path: 'comments',
populate: {
path: 'user'
},
populate: {
path: 'likes'
}
}).populate('comments')
.populate('likes');
But in the front end, I am not able to display the User Name that made a particular comment.
Please tell the error.

this.array.length is not working as default value for a property in Mongoose Schema

I have the following mongoose schema
const groupSchema = new mongoose.Schema({
club:{
type: mongoose.Schema.Types.ObjectId,
ref: 'Club',
required:true,
},
group:[{
team:{
type: mongoose.Schema.Types.ObjectId,
ref: 'Team',
required:true,
},
wins:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'Game',
default:[],
}],
points:{
type: Number,
default: function(){
return this.wins.length
}
},
}],
},{timestamps:true})
The issue that I am having is with the default value of the points attribute. It should return this.wins.lenght, but it allows returns 0.
When I tested it with this._id, it confirmed that I am referring to the right object as it was equal to the id of subdocument, yet it does not work with this.wins.length
any suggestions?

how to save data to the array in mongoose

I am building an application where user's can save many images along with its name. I want that information to be stored in mongoose in an array. How to do this?
Here is my mealSchema,
const MealSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
meals: [
{
mealImg: {
type: String,
},
mealName: {
type: String,
},
},
],
});
how to save data to this schema.
I want the result to be like this :
{ _id: 5fd662b596ac96247463fab8,
userId:"someid"
meals: [
{
_id:23242fff,
mealName:"meal1",
mealImg:"https://meal1.png"
},
_id:23242fff,
mealName:"meal2",
mealImg:"https://meal3.png"
},
_id:23242fff,
mealName:"meal3",
mealImg:"https://meal4.png"
},
] }
You can write smth like this:
Meal.insert({ userId: someId, meals: arrayOfMeals })
But this is not a good practice, because you can put unnecessary and incorrect information in the array. Such problems are solved by intermediate tables and links between them. I advise you to create another table, the scheme of which will be as follows:
const UsersMealsSchema = new mongoose.Schema({
userId: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
mealId: {type: mongoose.Schema.Types.ObjectId, ref: 'Meal'},
});
Then change your Meals shema:
const MealSchema = new mongoose.Schema({
id: {
type: string,
required: true,
}
mealImg: {
type: String,
required: true,
},
mealName: {
type: String,
required: true,
},
});

Mongoose populate subdocument in array

I have a Mongoose offer model explained below:
const OfferSchema = new Schema({
sections: [
{
title: String,
},
],
});
and order schema which has reference to to the first schema offer explained below:
const OrderSchema = new Schema({
offers: [
{
offer: { type: Schema.Types.ObjectId, ref: 'Offer' },
sections: [
{
section: { type: Schema.Types.ObjectId, ref: 'Offer.sections' }, // issue here
},
],
},
],
});
the problem that I can not populate sections here {section: { type: Schema.Types.ObjectId, ref: 'Offer.sections' }}
it gives me MissingSchemaError: Schema hasn't been registered for model "Offer.sections".
so is there any way to populate sections?
Unfortunately, Mongoose doesn't support this feature.
check the Github issue here
The alternative solution you can embed sections into the order schema

MongoDB - Mongoose: document structure/architecture

I have a relatively simple question, however, I keep getting stuck, over and over again. I think this is the fourth time I have tried to re-structure my documents to try and get the desired result.
I want to create a database in mongoose with the following structure:
A User has many groups, posts and comments. The groups, posts and comments all belong to a User. A post can belong to many groups, or none. And a comment belongs to a certain post.
Here is what I have so far:
UserSchema
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
username : String,
created: {type:Date, default: Date.now}
}
});
PostSchema
var PostSchema = new Schema({
url: String,
highlighted: String,
comment: String,
image: String,
timeStamp: String,
description: String,
title: String,
created: {type:Date, default: Date.now},
private: Boolean,
favorite: Boolean,
readlater: Boolean,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
comments: [{
text: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}],
groups: [{
name: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}]
});
groupSchema
var GroupSchema = Schema({
name : String,
created: {type:Date, default: Date.now},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
Although I may be wrong on this assumption, I think I have the post to user and comment to post relationship just the way it should be. Please correct me if I am wrong. The main problem I am having right now is with my group to post relationship. Currently, there is no relationship at all. As far as I can, it is almost as if the group belongs to the post, instead of the other way around. The only other way I can think to structure the group would be like so:
var GroupSchema = Schema({
name : String,
created: {type:Date, default: Date.now},
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
posts: [{
title: String,
Url: String,
comments: [{
text: String
}]
etc....
}]
});
The only problem I can see in the structure above is that the post will be required to be part of a group, which I want to be optional. So as far as I can tell, this will not work either.
If there is any advice you can provide in regards to how this should be structured, it would help me greatly.
Thank you in advance.
Your best bet is to embed your groups and comment schemas inside of your post document. For example,
var GroupSchema = Schema({
name: String,
created: { type:Date, default: Date.now },
});
var CommentSchema = Schema({
text: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
var PostSchema = new Schema({
url: String,
...,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
comments: [CommentSchema]
groups: [GroupSchema]
});
Now, if you are looking for posts with a particular group, you can build a query as follows. You can build more complicated queries than this however.
Post.elemMatch("groups", {name: {$in: ["group1", "group2"]}}).then(...)

Categories