How to define an object in NodeJS schema? - node.js

I currently have a schema like this:
const postSchema = mongoose.Schema({
title: String,
message: String,
name: String,
creator: String,
tags: [String],
selectedFile: String,
likes: { type: [String], default: [] },
createdAt: {
type: Date,
default: new Date(),
},
})
One of the problem that I anticipate is that as the number of users grow, searching the likes array will become inefficient. Is there a way to store the likes array instead as an Object (key would be userId and value could be true) so that finding someone in the Object would become more efficient.
I am also open to hearing any other ideas that you might have.
Thanks!

I want to suggest populate() for this. From that, you can manage a large no. of user information without a problem. You can create a new schema as likes and add the id of the likes document as an id with the populate. Check the below example.
const likeSchema = mongoose.Schema({
type: [String],
default: [] },
});
const Like = mongoose.model("Like", likeSchema);
Then create the postschema like below.
const postSchema = mongoose.Schema({
title: String,
message: String,
name: String,
creator: String,
tags: [String],
selectedFile: String,
likes: {
type: mongoose.Schema.Types.String,
ref: 'Like',
},
createdAt: {
type: Date,
default: new Date(),
},
})
const Post = mongoose.model("Post", postSchema);
You can easily get all the data inside a likes document by populating when running a query like below.
const posts = await Post.findById({creator_id}).populate("likes");
//Below code will print the data of the first element of the type array of relevant likes document.
console.log(posts.likes.type[0]);
Check the populate and population sections of the mongoose documentation to learn more.

Related

How to dynamically populate array of objects in mongoose?

I am trying to dynamically populate array of objects in mongoose. On my user model I want an array that contains all posts that user made. The problem is that I have multiple types of posts.
Different post models:
const ImagePost = mongoose.model('ImagePost', new Schema({ url: String }))
const TextPost = mongoose.model('TextPost', new Schema({ text: String }))
My user model looks like this:
const userSchema = new Schema({
userName: {
type: String,
required: true
},
posts: [{
postId: {
type: Schema.Types.ObjectId,
required: true,
refPath: "postModel"
},
postModel: {
type: String,
required: true,
enum: ['ImagePost', 'TextPost']
}
}]
})
const User = mongoose.model('User', userSchema)
How can I get the user from my database and automatically populate the posts the user made?
The whey I think it should work is this but for some reason it doesn't do anything:
User.findById('5d302c7caf1b8906ccb611b6').populate('posts.postId')
Changing your refPath from postModel to posts.postModel may solve your problem.

Mongoose populate ObjectID from multiple possible collections

I have a mongoose model that looks something like this
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: 'article',
index:true,
},
});
But 'item' could be referenced from multiple collections. Is it possible to do something like this?
var LogSchema = new Schema({
item: {
type: ObjectId,
ref: ['article','image'],
index:true,
},
});
The idea being that 'item' could be a document from the 'article' collection OR the 'image' collection.
Is this possible or do i need to manually populate?
Question is old, but maybe someone else still looks for similar issues :)
I found in Mongoose Github issues this:
mongoose 4.x supports using refPath instead of ref:
var schema = new Schema({
name:String,
others: [{ value: {type:mongoose.Types.ObjectId, refPath: 'others.kind' } }, kind: String }]
})
In #CadeEmbery case it would be:
var logSchema = new Schema({
item: {type: mongoose.Types.ObjectId, refPath: 'kind' } },
kind: String
})
But I did't try it yet...
First of all some basics
The ref option says mongoose which collection to get data for when you use populate().
The ref option is not mandatory, when you do not set it up, populate() require you to give dynamically a ref to him using the model option.
#example
populate({ path: 'conversation', model: Conversation }).
Here you say to mongoose that the collection behind the ObjectId is Conversation.
It is not possible to gives populate or Schema an array of refs.
Some others Stackoverflow people asked about it.
Soluce 1: Populate both (Manual)
Try to populate one, if you have no data, populate the second.
Soluce 2: Change your schema
Create two link, and set one of them.
var LogSchema = new Schema({
itemLink1: {
type: ObjectId,
ref: 'image',
index: true,
},
itemLink2: {
type: ObjectId,
ref: 'article',
index: true,
},
});
LogSchema.find({})
.populate('itemLink1')
.populate('itemLink2')
.exec()
Dynamic References via refPath
Mongoose can also populate from multiple collections based on the value of a property in the document. Let's say you're building a schema for storing comments. A user may comment on either a blog post or a product.
body: { type: String, required: true },
on: {
type: Schema.Types.ObjectId,
required: true,
// Instead of a hardcoded model name in `ref`, `refPath` means Mongoose
// will look at the `onModel` property to find the right model.
refPath: 'onModel'
},
onModel: {
type: String,
required: true,
enum: ['BlogPost', 'Product']
}
});
const Product = mongoose.model('Product', new Schema({ name: String }));
const BlogPost = mongoose.model('BlogPost', new Schema({ title: String }));
const Comment = mongoose.model('Comment', commentSchema);

Schema and subdocs in mongoose.js

Learning how to use mongoose, and am trying to design reliably-variable schemas. The app would post to different services (e.g. Twitter, Tumblr) and store them in one collection ("Posts"). There would be some commonalities (e.g. when it was published, or a short summary) but other fields (like post contents, a blog posts's accompanying scripts) would vary.
What's a good way to approach this? Is there a good way to bind together different collections to avoid this in the first place? References/subschemas? Use Schema.Types.Mixed, and reinforce consistency by extending the default methods with safety checks?
// Example pseudo-functioning schemas
const tweetSchema = new mongoose.Schema({
tweetUrl: {type: string, trim: true}
length: Number
});
const blogSchema = new mongoose.Schema({
title: String,
edits: [Date],
slug: { type: String, trim: true},
body: String
});
const postSchema = new mongoose.Schema({
published: Date,
summary: String,
type: String,
contents: blogSchema || tweetSchema
});
Maybe the discriminators could be better option for your case.
Discriminators are a schema inheritance mechanism. They enable you to have multiple models with overlapping schemas on top of the same underlying MongoDB collection.
Sample codes as below
var options = {discriminatorKey: 'contents'};
const postSchema = new mongoose.Schema({
published: Date,
summary: String,
type: String,
}, options);
var Post = mongoose.model('Post', postSchema);
const tweetSchema = new mongoose.Schema({
tweetUrl: {type: string, trim: true}
length: Number
}, options);
var Tweet = Post.discriminator('Tweet', tweetSchema);
const blogSchema = new mongoose.Schema({
title: String,
edits: [Date],
slug: { type: String, trim: true},
body: String
}, options);
var Blog = Post.discriminator('Blog', blogSchema );

Error while defining mongoose schemas

I am new to mongo and mongoose. I am trying to create 3 collections Users, Articles and Comments. I want the users documents should contain articles that users have saved. The articles object should have users and comments as embedded objects and comments should have embedded user objects.
I want this to be done using the ids of the individual objects so that I can reduce the loading time, but could not find a suitable way to do so using mongoose. Please suggest how should I proceed with the Schema implementation.
var UserSchema = new mongoose.Schema({
name: String,
email: String,
profilePicture: String,
password: String,
readingList: [articleSchema]
});
var commentsSchema = new mongoose.Schema({
content: String,
votes:{
up:[UserSchema],
down:[UserSchema]
},
comments:[commentsSchema],
timestamp:Date.now
});
var articleSchema = new mongoose.Schema({
title: String,
content: String,
image: String,
votes:{
up: [UserSchema],
down: [UserSchema]
},
comments:[commentsSchema],
timestamp: Date.now
});
What you have is failing because articleSchema isn't defined when you're using it in the UserSchema. Unfortunately, you can reverse the order of defining the schema because they're dependent on each other.
I haven't actually tried this, but based on some quick googling there is a way to create the Schema first and then add the properties.
var UserSchema = new mongoose.Schema();
var CommentsSchema = new mongoose.Schema();
var ArticleSchema = new mongoose.Schema();
UserSchema.add({
name: String,
email: String,
profilePicture: String,
password: String,
readingList: [ArticleSchema]
});
CommentsSchema.add({
content: String,
votes:{
up:[UserSchema],
down:[UserSchema]
},
comments:[CommentsSchema],
timestamp:Date.now
});
ArticleSchema.add({
title: String,
content: String,
image: String,
votes:{
up: [UserSchema],
down: [UserSchema]
},
comments:[CommentsSchema],
timestamp: Date.now
});

Mongoose casting array to string when using $set

I have my model:
var QuestionSchema = new Schema({
title: String,
question: String,
answers: [String],
set_id: String
});
And I update like so:
questionModel.update({
_id: id
}, {
$set: {
title: req.body.title,
question: req.body.question,
answers: req.body.answers
}
}, function (err, numAffected) {
});
I've checked req.body.answers and it is an array, however, it seems to get saved in the DB as foo,bar, as in, a string, not an array!
Any ideas?
answers: req.body.answers[0]
Was the eventual workaround, no idea why!? If anyone can shed any light on why it was coming from a form with inputs: name="answers[]" being passed as [[foo, bar]]...
I suspect that because you've used '[String]' instead of 'Array' in your schema definition, then when you go to update the model, the array is cast to a string, rather than being saved as an array. Try the below:
var QuestionSchema = new Schema({
title: String,
question: String,
answers: Array,
set_id: String
});
It also looks like you would only use brackets around a schema type where you are defining meta properties:
var animalSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true } // field level
});

Resources