I have a user model and a pet model, using Mongoose.
user model :
var userSchema = mongoose.Schema({
email : String,
password : String,
pets : [Pet.schema]
});
module.exports = mongoose.model('User', userSchema);
pet model :
var petSchema = mongoose.Schema({
name : String
});
module.exports = mongoose.model('Pet', petSchema);
I'd like to render all the pets of all users in a page. I tried to do :
app.get('/pets', function(req, res) {
Pet.find({}, function(err, pets){
res.render('pets.ejs', {"pets":pets});
});
});
But I only get an empty array [ ].
How could I get every Pets ?
More generally, I will have to use Pet model independently from the User. Should I change the way my models are constructed ?
Thanks for your help !
It looks like you're trying to embed the Pet sub document and use as if it weren't embedded.
First, if you're doing embedded, you need to define the schema for Pet before you use it (create it first). If you're embedding, you can't do a find on the Model for pets that way.
Mongoose Subdocuments
If, instead, you want to store a reference to a Pet inside a User, you need to store its ObjectId.
pets : [{ type: Schema.Types.ObjectId, ref: "Pet"}]
You would then likely use populate to fill the pets property at run time.
Related
I have these specific schemas:
const PersonSchema = new Schema({
Name: String,
})
const StudentSchema = new Schema({
Class: String,
Professor: String
})
const ProfessorSchema = new Schema({
Class: String,
Students: Number
})
I produce the following models, note that the Student and Professor are extended from the Person model:
mongoose.model("Person", PersonSchema, "objects" )
Person.discriminator("Professor", ProfessorSchema, "objects")
Person.discriminator("Student", StudentSchema, "objects")
I store all of them in the same collection "objects" and when I call the find() method from any of the models I get all the objects in the collection and not just from the specific model. How can I just retrieve from the collection one specific model?
The way to solve this problem of getting all the documents from all models when asking just a specific one is to provide some specific arguments arguments when constructing the model.
Instead of just providing the collection name in the model:
Person.discriminator("Professor", ProfessorSchema, "objects")
You should provide a conjunct of arguments that specify the collection and the type attribute:
const baseOptions = {
collection: "objects",
discriminatorKey: '__type'
}
const Professor = Person.Model.discriminator("Professor", ProfessorSchema, baseOptions)
The default discriminatoryKey now has the value equal to the model name instead of its default value that is the collection name. When you use the find() method it only retrieves the type connected with the model.
I'm trying to build some sort of a social media app using node.js and mongoDB.
I have a mongoose schema for 'User', and when i render some user page on the app, it needs to also show all of his posts/images/list of friends and etc...
right now i have a mongoose schema for 'UserPost' and also for 'Image', and when i save an image for example, it has a field which keeps the username of the user who uploaded it, so when i render the user page it finds all of his images.
It is the first time i'm dealing with db's so i heard that i might have to use a reference data instead of embedded data.
can someone explain to how should i organize the data model for my app?
It's very handful to use mongoose population for handling db references
Define your schemas like these:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var userSchema = Schema({
name : String,
posts : [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});
var postSchema = Schema({
title : String,
images : [{ url: String, filename: String }]
});
var User = mongoose.model('User', userSchema);
var Post = mongoose.model('Post', postSchema);
According to mongoose population docs, you can get all your data:
User.findOne().populate('posts').exec(function(error, user) {
console.log(user.posts) // there are populated posts objects inside array
})
Not sure, is it a good idea to use separated collection for image uploads, it's simpier to embed it inside Post (or User), but you may always add { type: Schema.Types.ObjectId, ref: 'Image' } for images
MongoDB is a NoSql DBMS. It means, you schould not create references between data fields, because the performance coming from NoSql will be killed: https://en.wikipedia.org/wiki/NoSQL
But, if you are really thinking you need references, checkout this: http://docs.mongodb.org/master/reference/database-references/
You can reference to a data document in the mongoDB by the _id ("Page"=>"createdBy" = "User"=>"_id" for example).
It depends of what kind of data you want to embed. MongoDB has object size limits according to the storage engine you use. Thus you should predict or estimate the the size of the object you want to embed.
See more about limits here: http://docs.mongodb.org/master/reference/limits/
See more about references here: http://docs.mongodb.org/master/reference/database-references/
In my application I have a User Collection. Many of my other collections have an Author (an author contains ONLY the user._id and the user.name), for example my Post Collection. Since I normally only need the _id and the name to display e.g. my posts on the UI.
This works fine, and seems like a good approach, since now everytime I deal with posts I don`t have to load the whole user Object from the database - I can only load my post.author.userId/post.author.name.
Now my problem: A user changes his or her name. Obviously all my Author Objects scattered around in my database still have the old author.
Questions:
is my approuch solid, or should I only reference the userId everywhere I need it?
If I'd go for this solution I'd remove my Author Model and would need to make a User database call everytime I want to display the current Users`s name.
If I leave my Author as is, what would be a good way to implement a solution for situations like the user.name change?
I could write a service which checks every model which has Authors of the current user._id and updates them of course, but this sounds very tedious. Although I'm not sure there's a better solution.
Any pro tipps on how I should deal with problems like this in the future?
Yes, sometime database are good to recorded at modular style. But You shouldn't do separating collection for user/author such as
At that time if you use mongoose as driver you can use populate to get user schema data.
Example, I modeling user, author, post that.
var UserSchema = new mongoose.Schema({
type: { type: String, default: "user", enum: ["user", "author"], required: true },
name: { type: String },
// Author specific values
joinedAt: { type: Date }
});
var User = mongoose.model("User", UserSchema);
var PostSchema = new mongoose.Schema({
author: { type: mongoose.Scheam.Types.ObjectId, ref: "User" },
content: { type: String }
});
var Post = mongoose.model("Post", PostSchema);
In this style, Post are separated model and have to save like that. Something like if you want to query a post including author's name, you can use populate at mongoose.
Post.findOne().populate("author").exce(function(err, post) {
if(err)
// do error handling
if(post){
console.log(post.author.type) // author
}
});
One solution is save only id in Author collection, using Ref on the User collection, and populate each time to get user's name from the User collection.
var User = {
name: String,
//other fields
}
var Author = {
userId: {
type: String,
ref: "User"
}
}
Another solution is when updating name in User collection, update all names in Author collection.
I think first solution will be better.
here is my code for models.js where I keep models
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var GroupSchema = new Schema({
title : String
, elements : [ElementSchema]
, author : String
});
var ElementSchema = new Schema({
date_added : Date
, text : String
, author : String
});
mongoose.model('Group', GroupSchema);
exports.Group = function(db) {return db.model('Group');};
mongoose.model('Element', ElementSchema);
exports.Element = function(db) { return db.model('Element');
};
To me it looks pretty clear, but when I do
function post_element(req, res, next) {
Group.findOne({_id: req.body.group}, function(err, group) {
new_element = new Element({author: req.body.author,
date_added: new Date()});
new_element.save();
group.elements.push(new_element);
group.save();
res.send(new_element);
return next();
})
}
I don't understand why when I go in Mongo I have two collections one called Groups with nested groups (so it looks fine) and the other collection is called Elements.
Why? Shouldn't it be called just Group ?
I don't understand, a good chap that please explain it to me?
Thanks,
g
When you execute this line:
new_element.save();
you're saving the newly created element to the Elements collection. Don't call save on the element and I think you'll get the behavior you're looking for.
Its because of the following line:
mongoose.model('Element', ElementSchema);
This registers a model in mongoose and when you register a model, it will create its own collection inside mongo. All you have to do is get rid of this line and you will see it disappear.
On another note, its much cleaner and easier to setup your files to only export one model per file using the following to export the model:
module.exports = mongoose.model('Group', GroupSchema);
Hope this helps!
I use mongoose in my node.js app, and basically have the following models:
// Define Car model
CarSchema = new Schema({
brand : String,
type: String,
maxSpeed : Number
});
mongoose.model('Car', CarSchema);
// Define User model
UserSchema = new Schema({
lastname : String,
firstname : String,
cars : [CarSchema]
});
mongoose.model('User', UserSchema);
I'm really new to NoSQL and I really want to give it a try but I first need to study if this really fits my needs.
With the above models, will I be able to create a query listing all the Users who have a particular type of car among their personal cars ?
I don't how to do it in mongoose. But in mongodb it possible. So, in mongodb shell query will looks like this:
db.users.find({"cars.type":"sport"})
Above query return all users thats have car with type 'sport' in their nested collection of cars.
Mongodb dot notation documentation.