Mongoose: Join Operation, populate isn't possible - javascript

I'm currently struggling with a project of mine.
I've got a collection called "Games" and one "Participation".
When a user loggs in, he/she should be able to see the games and the individual participation status.
Therefore, I want to join these two collections but I can't use .populate() because I can't enter the neccessary Participation ID in the Games collection due to the fact, that I don't have the participation ID at the time I create a game. (So I would need to save the participation, remember the created ID and insert THIS id in the games collection afterwards)
The other way round would be a good solution (to populate Games in Participation) but initially, there are no participations in these collection, until a user clicks "Participate" or "Not Participate".
Basically I need a SQL query like that:
select * from games g, participation p where g.gamesId = p.gamesId AND p.username = [...]
Is there any possibility to achieve this?
Otherwise I would need to save every participation as a "Game", having the dependent username and his/her participation status in it.
Wouldn't prefer this solution at all.
Thanks in advance!
In case it helps:
Here are my two collections:
Game:
var gameSchema = mongoose.Schema(
{
isHome: { type: Boolean, required: true },
time: { type: String, required: true, max: 100 },
uzeit: { type: String, required: true, max: 100 },
clubId: { type: mongoose.Types.ObjectId, required: true },
enemy: { type: String, required: true, max: 100 },
place: { type: String, required: true, max: 100 },
(participation: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Teilnahme' }])
});
Participation:
var participationSchema = mongoose.Schema(
{
playerAvailable: { type: Boolean, required: true },
clubId: { type: mongoose.Types.ObjectId, required: true },
gameId: { type: mongoose.Types.ObjectId, required: true, ref: 'Game' },
memberName: { type: String, required: true },
}
);

Related

Mongoose: Populate or reference Id on Schema?

I am wondering which is better in terms of performance.
Let's say i have this Menu Schema
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
}
And i have this Plate Schema
{
name: { type: String, unique: true, required: true },
}
If i want to get all the plates from the Menu, which option is better?
Associate the Menu with plates like this:
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
plates: [
type: Schema.Types.ObjectId,
ref: 'plate'
]
}
Or like this:
{
name: { type: String, unique: true, required: true },
menu: {
type: Schema.Types.ObjectId,
ref: 'plate'
}
}
With the first option, i would use Menu.findById(id).populate('plates') to get the menu and all the plates associted with it. But with the second option, i would use Plate.find({menu: menu_id})
So, which is faster considering that i could have any amount of plates?
And if they are the same, is there a better way of associate Plate and Menu and get plates from Menu?
In terms of the performance, the second one would be faster because the first one would do the $lookup behind the scene (when doing populate), which is actually additional database call.
I would suggest the third option, which would be the best in your use case. That is embedding the plate documents inside the menu document.
{
restaurant: { type: String, unique: true, required: true },
color: { type: String, required: true },
plates: [
{
name: { type: String }
}
]
}
This way, menu document will contain all the information you needed and you can fetch everything with one database call.
Note: There is a limitation of 16MB for each document in the MongoDB. You can for sure store thousands or tens of thousands plates in each menu without a problem. But if you need to store much more in each menu, then you should go with some of your initial setups.

Nodejs app with mongo database tables schema

I want to do a big project API where people can login with Google, send the token and when logged in do some actions saved to Mongo DB.
The problem is the tables structure, or schema. So, I want to do a good app scalable.
I have some tables like Users (with users loggin information) and I want user save his tasks, memories, works and more for him self, and when logged in in another device, get this information and modify.
Do I need to do every table for every user or use same table filtered by user?
For example, I have now this model for product:
const ProductoSchema = Schema({
nameOfProduct: {
type: String,
require: [ true, 'Required name' ],
unique: true
},
state: {
type: Boolean,
default: true,
required: true
},
userOwner: {
type: Schema.Types.ObjectId,
ref: 'Usuario',
required: true
},
priece: {
type: Number,
default: 0
},
category: {
type: Schema.Types.ObjectId,
ref: 'Category',
required: true
},
description: {
type: String
},
disponibility: {
type: Boolean,
default: true
},
img: {
type: String
},
});
Is this correct or this is not scalabe and I need to do all tables for every user?

How can I display the username of a forum?

I want to find out how I can display the username of a user. For example user admin posts a forum, then I would see Created By: admin on the forum page, instead I can only fish out the ID.
I don't know a much about mongoose and I need someone who is familiar with it.
My Forum Model:
You see I have only ref: 'user' and this is grabbing the ObjectId("") from the User.
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
my userModel:
const UserSchema = mongoose.Schema({
userID: {
type: String,
required: true,
},
userName: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
isAdministrator: {
type: Boolean,
deafult: false,
},
});
Front-end :
As you can see only in {forum.user} there I can see the id from the user but I want his name not the id
<footer className="blockquote-footer">
Created by:{forum.user}
Created on:{forum.published_on.substring(0,300)}
</footer>
Since you are using 'ref' to reference the user table. When you are fetching a forum document, use the populate() function in mongoose to get all the user details as a sub-object to the forum document.
example: forumShcema.find({_id:<forum_id>}).populate('user').exec()

Adding up review totals in mongoose

I am creating a website that helps you find doctors. I would like the doctors to have a field in their model that has an overall rating, friendliness rating, cleanliness rating, satisfaction rating, and responsiveness rating. All of these ratings will be a number calculated by adding up all of their review (another model) and populating these fields using a sum calculation. My question is, how do I accomplish this when all of my reviews are a separate model that is virtually populated on the doctor's model in a controller? Is there some way to add up all of the reviews for a specific doctor ID and then calculate averages on the ratings and then update the doctor with this information?
Here is my doctor model:
const prothesistSchema = mongoose.Schema(
{
name: {
type: String,
required: [true, 'A user must have a name'],
},
email: {
type: String,
required: [true, 'A user must have an email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please provide a valid email'],
},
ratingsAverage: {
type: Number,
default: 4.5,
min: [1, 'Rating must be above 1.0'],
max: [5, 'Rating must be below 5.0'],
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
prothesistSchema.virtual('reviews', {
ref: 'Review',
foreignField: 'prosthetistID',
localField: '_id',
});
const Prothesist = mongoose.model('Prothesist', prothesistSchema);
Here are the reviews I am trying to calculate averages off of:
const reviewSchema = new mongoose.Schema(
{
review: {
type: String,
required: [true, 'A review must have a review!!!'],
},
rating: {
type: Number,
min: 0.5,
max: 5,
},
friendliness: {
type: Number,
min: 0.5,
max: 5,
},
cleanliness: {
type: Number,
min: 0.5,
max: 5,
},
satisfaction: {
type: Number,
min: 0.5,
max: 5,
},
responsiveness: {
type: Number,
min: 0.5,
max: 5,
},
createdAt: {
type: Date,
default: Date.now,
},
prosthetistID: {
type: mongoose.Schema.ObjectId,
ref: 'Prothesist',
required: [true, 'Review must belong to a prosthetist!'],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
require: [true, 'Each review must have an associated user!'],
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
reviewSchema.pre(/^find/, function (next) {
const query = [
// { path: 'tour', select: 'name' },
{ path: 'user', select: 'name photo activityLevel age amputationInfo' },
];
this.populate(query);
next();
});
const Review = mongoose.model('Review', reviewSchema);
2 approaches here:
The first one would be keeping a running score and keep editing that instead of calculating from the beginning. I am assuming this is what big sites (like Yelp or Google) are doing, since it's way more scalable.
The second and most obvious approach would be, iterating over all the reviews every time a review gets added to calculate new numbers.
Let's dissect the first one:
save a running score for every field under every doctor's listing
trigger your updateRatings function on every addition to the reviews
this function should:
Get current score
Multiply it by the reviews.length
Add the currentReviewRating
Divide by review.length+1 to get the new average
Save that as a new average to use later
The second one would be very straight forward:
let avg = avg([...reviews.field]) => ( sum(arr) / length(arr) );

Can't save JavaScript objects with Mongoose

I'm doing a MMO Real-Time Browser game, and I'm storing data using Mongoose (MongoDB).
First of all, I'll show you the structure of my object:
var playerSchema = new Schema({
name: { type: String, required: true, trim: true, index: { unique: true } },
resources: {
wood: { type: Number, required: true, default: 500},
stone: { type: Number, required: true, default: 300},
iron: { type: Number, required: true, default: 0},
cereal: { type: Number, required: true, default: 0}
},
resourcesPerHour: {
woodPerHour: { type: Number, required: true, default: 40},
stonePerHour: { type: Number, required: true, default: 20},
ironPerHour: { type: Number, required: true, default: 0},
},
res: {type: Array, required:true, default: []},
buildings: { type: Array, required: true, default: []},
researches: { type: Array, required: true, default: []}
});
As you can see, res, buildings, and researches are arrays. I'm gonna show you one of them (they all have the same structure):
var buildingSchema = new Schema({
id: {type: String, requried: true},
name: { type: String, required: true, trim: true, index: { unique: true } },
level: { type: Number, required: true, default: 0},
scalingValue: {type: Number, required: true, default: 2},
costs: {
wood: { type: Number, required: true, default:0},
stone: { type:Number, required:true, default:0},
iron: {type:Number, required:true, default:0},
cereal: {type:Number, required:true, default:0}
}
});
OK, imagine I have a player, with all data initializated. When I try to update something, I can only update information out of that lists. Look this example:
player.findOne({name:req.session.name}, function(err, doc){
doc.resources.wood -= 200;
doc.buildings[id%100].costs.wood *= 2;
doc.save(function(err){
if(err)console.log(err);
});
}
When I look the model on database, it only stored the resources.wood, not the building[i].costs.wood. I don't know why it's failing, but all objects inside the arrays are created using a new variable, where variable is a Schema (like buildingSchema).
One more thing, I added a console.log(doc.buildings[i].costs.wood); just before the doc.save() and it's ok. So it means all the data is modified fine, but in the doc.saveit only save the 'not-in-list' data.
EDIT: console.log(err); doesn't print nothing, so it means the save function worked.
When you use the Array type in your schema (same as the Mixed type in the docs), you need to explicitly mark the field as modified or Mongoose won't save any changes you make to it.
doc.markModified('buildings');
doc.save(function(err){
if(err)console.log(err);
});
The better approach may be to declare buildings as containing an array of buildingSchema:
buildings: [buildingSchema],
That way Mongoose will be able to track the changes you make to the buildings array automatically.
https://groups.google.com/forum/#!topic/mongoose-orm/5m7ZPkhFyrc
Subdocuments that are not inside of an array are not supported by mongoose. My recommendation is to drop mongoose completely and just use direct queries to the db. You'll find the node native driver is quite easy to use.

Categories