My userSchema looks something like this:
var UserSchema = new Schema({
name: { type: ......},
email: { type..... },
test1: {
[
[0] { test: Array },
[1] { test: Array }
]
}
)};
With a few other objects not included here. In the object that's in test1 there is stored some data to which i want to add or somehow bind an array of comments. The problem is that I'm not sure which is the best way to implement this? If i should create a new commentSchema and somehow connect the comments with the object or how to do it. Can anyone give me some tips on how to implement it?
You can create your schema as follows:
var CommentSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
message: String,
});
var UserSchema = new Schema({
name: { type: ......},
email: { type..... },
...
test1: {
comments: [CommentSchema]
}
... OR
comments: [CommentSchema]
)};
or if you are trying to do something like posts with comments try
var PostSchema = new Schema({
comments: [CommentSchema]
postBody: String
})
var UserSchema = new Schema({
name: { type: ......},
email: { type..... },
...
posts: [PostSchema]
...
)};
Related
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
Say I have one model, Book, and another model, Genre. When I create the book, I'd like to be able to pass a Genre ID and have the model automatically fetch and embed the document. For example:
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: ObjectId,
required: true,
}
});
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
Then I'd like to be create a book as follows:
const Book = await Book.create({
title: 'Lord of the Rings',
author: 'J. R. R. Tolkien',
genre: '5d6ede6a0ba62570afcedd3a',
});
That would create a book and automatically embed the genre document from the given ID. Is there a way to do that from within the schema, or would I have to wrap it in additional logic?
You can use the pre-save mongoose middleware/hook to find the genre and set it as an embedded document. In mongoose pre-save hook, this will be the current document, you can read the value and set the value to this object before it is written to the database.
Note that, since this is a pre-save hook, it will be run only on Model.create() or document.save(). So it won't be run on Model.insertMany(). But it will be run when you update the document using document.save(). If you want to set the genre only on new documents, you will have to check for this.isNew property
const { Schema, Types } = mongoose
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
const Genre = mongoose.model('Genre', genreSchema)
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genreId: {
type: Schema.Types.ObjectId,
required: true,
},
genre: {
type: genreSchema,
},
});
bookSchema.pre('save', async function() {
if (this.isNew) { // this.isNew will be true on only new documents
this.genre = await Genre.findById(this.genreId) // `this` is the new book document
}
})
const Book = mongoose.model('Book', bookSchema)
/* Test book creation */
const genre = await Genre.create({
name: 'Fantasy'
})
const book = await Book.create({
title: 'Lord of the Rings',
author: 'J. R. R. Tolkien',
genreId: genre._id,
});
console.log(book)
you can use mixed schema type and document middleware to solve your problem.see my sample code below:
const genreSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
});
const Genre = mongoose.model('Genre', genreSchema);
const bookSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: Object,
required: true,
}
});
bookSchema.pre('save', async function () {
const genreID = mongoose.Types.ObjectId(this.genre);
this.genre = await Genre.findById(genreID);
});
const Book = mongoose.model('Book', bookSchema);
const newBook = new Book({ title: 'The book', author: 'xyz', genre: '5ef55c67be27fb2a08a1131c' });
newBook.save();
How do you know which genre ID to embed? Can you send this from your frontend?
If yes, then simply select the genre ID from you frontend and then pass it in your API's request body.
While in your backend:
router.route('/book')
.post((req, res) => {
Book.create({
title: req.body.title,
author: req.body.author,
genre: req.body.genre,
}, (err, product) => {
if (err) {
res.send(err);
} else {
res.json({success:true})
}
});
})
Do something like this to create a new book object in your Book collection.
If I understand your question correctly I think what you're looking for is populate. https://mongoosejs.com/docs/populate.html
It would change your schema to look like the following
const bookSchema = new Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
genre: {
type: Schema.Types.ObjectId,
ref: 'Genre',
required: true,
}
});
const genreSchema = new Schema({
name: {
type: String,
required: true,
},
});
When you get your book you can reference the genre by doing this
Book.find()
.populate('genre')
Hopefully, that answered your question!
Here is my problem I have two schemas one nested in another as an array. I am trying to push into the array and save the subdocument but it is not saving properly, I get an object that is saved but none of the fields are saved except for its _id. Do I have to save each model individually first? What is the issue here?
Here are my two schemas:
import mongoose from "mongoose";
import {contactSchema} from "./ContactSchema"
export const bigSchema = new mongoose.Schema({
testField: {
type: String,
required: true,
},
contacts: [{ contactSchema }],
}
});
export default mongoose.model("Big", bigSchema);
import mongoose from "mongoose";
export const contactSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
age: {
type: number,
required: false,
}
});
export default mongoose.model("Contact", contactSchema);
Here is my code that I use to push to the array of contacts.
public async saveContact(
testField: string,
name: string,
age: number,
) {
const newContact = new Contact({
name: name,
age: age,
});
console.log(newContact);
return UserContacts.findOneAndUpdate(
{
testField: testField,
},
{ $push: { contacts: newContact } },
{ new: true, upsert: true }
);
}
However when I check my database, this is what I see instead. There is an objectId but not what I expect to see, which is a list of Contact subdocuments inside my "Big" document
{
"_id" : ObjectId("XXXX"),
"testField" : "XXXXX",
"contacts" : [
{
"_id" : ObjectId("XXXXX")
}
],
"__v" : 0
}
export const bigSchema = new mongoose.Schema({
testField: {
type: String,
required: true,
},
contacts: [{ contactSchema }],
}
});
should be:
export const bigSchema = new mongoose.Schema({
testField: {
type: String,
required: true,
},
contacts: [contactSchema],
}
});
Try that and see what happens.
Edit: Also, if you intend for contacts to be an array of references to the Contact model, then you need to do this instead:
export const bigSchema = new mongoose.Schema({
testField: {
type: String,
required: true,
},
contacts: [{type: mongoose.Schema.Types.ObjectId, ref: 'Contact'}],
}
});}
This will make contacts an array of Contact IDs, so you're not duplicating any data, but just referencing the contacts in their collection. docs
I have 2 schemas:
const mongoose = require('mongoose');
const PinSchema = new mongoose.Schema({
title: String,
content: String,
image: String,
latitude: Number,
longitude: Number,
author: {
type: mongoose.Schema.ObjectId,
ref: "User"
},
comments: [
{
text: String,
createdAt: {
type: Date,
default: Date.now,
author: {
type: mongoose.Schema.ObjectId,
ref: "User"
}
}
}
]
}, { timestamps: true });
module.exports = mongoose.model("Pin", PinSchema);
and
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: String,
email: String,
picture: String
});
module.exports = mongoose.model("User", UserSchema);
As you can see author field in Pin is the same as the _id in User schema.
I then try to populate the comments author field in the Pin schema like this:
const pinUpdated = await Pin.findOneAndUpdate(
{ _id: pinId },
{ $push: { comments: "some comment" } },
{ new: true }
).populate("author")
.populate("comments.author");
however the result object has author field set to null so population doesn't work.
I'm not against doing this with native mongo syntax using $lookup but in my case it's not just looking up an array it's looking up a field of an objects array:
db.pins.aggregate([
{
$lookup:
{
from: "users",
localField: "comments._id", // this won't work
foreignField: "_id",
as: "authorOutput"
}
}
])
what am I missing in populate()?
It looks like your author field in the comments array is nested inside the createdAt object, which is likely unintentional. Changing PinSchema to the following (closing curly brace before author) should fix it:
const PinSchema = new mongoose.Schema({
...
comments: [
{
text: String,
createdAt: {
type: Date,
default: Date.now,
},
author: {
type: mongoose.Schema.ObjectId,
ref: "User"
}
}
]
}, { timestamps: true });
I am trying to display users that are in a friend group. I am using mongoose and express. This is the friend group schema:
var mongoose = require("mongoose");
var FriendGroupSchema = new mongoose.Schema({
name: String,
public_or_private: { type: String, enum: ["public", "private",] },
friends: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "friend"
},
admin: Boolean
}
]
});
module.exports = mongoose.model("friend_group", FriendGroupSchema);
and this is the user schema:
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = new mongoose.Schema({
username: String,
password: String,
profile_picture: String,
about: String,
friend_groups: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "friend_group"
}
],
friends: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "friend"
}
],
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
I will need to display the users inside the friend group in a show page of a user. I will need to .populate into the user, then populate into the friend group, and then into the friends array and finally the user and whether he/she is an admin(I will need to display both).
this is what I have so far:
.populate({path:"friend_groups", populate: {path: 'friends', populate: {path: 'user', model: 'User'}}})
but this doesn't seem to work. what am I doing wrong?