Consider there is documents like this in DB:
{
name: 'my list',
items: [
{ type: 'book', id: 5364 },
{ type: 'car', id: 354 },
{ type: 'laptop', id: 228 }
]
}
I need to grab data of each item from its own collection, based on type value.
I searched about it but couldn't figure out the correct approach.
Expected output:
{
name: 'my list',
items: [
{ type: 'book', id: 5364, data: [{...}] },
{ type: 'car', id: 354, data: [{...}] },
{ type: 'laptop', id: 228, data: [{...}] }
]
}
Mongoose schema of first collection (above):
{
name: String,
items: {
type: Array,
default: []
}
}
And other collections that must be looked up has corresponding _id field.
There are a few different ways to do this. To be most similar to what you have currently, you just need to make one small change. type is a keyword in Mongoose, so if you want to have a field in your schema which is actually called "type", you need to use the word twice. Once for the Mongoose keyword and again to define your schema.
{
name: String,
items: [{
type: { type: String},
id: Number,
data: []
}]
}
If the data field is coming from another collection, you could use a reference and then call populate() on your find method.
// ItemSchema
{
name: String,
items: [{
type: { type: String },
id: Number,
data: [{
type: Schema.Types.ObjectId,
ref: 'OtherSchemaName'
}]
}]
}
// Find Method with populate
const myList = await ItemSchema.find({type: "book"}).populate('data')
// Result
{
name: 'my list',
items: [
{ type: 'book', id: 5364, data: [{...}] },
]
}
Related
I'm having an issue running a query on my mongo DB. I want to return the objects that match the ids in the array of Ids. Not sure what I'm doing wrong everywhere online suggests doing it this exact way.
Mongoose Query
MovieSchema.statics.findKnownForMovies = function(ids) { return this.find({ id: { $in: [ids]}});
}
Mongoose Schema
const MovieSchema = new Schema({
adult: { type: Boolean },
category: {type: String},
id: { type: Number, required: true, unique: true },
poster_path: { type: String },
overview: { type: String },
release_date: { type: String },
original_title: { type: String },
genre_ids: [{ type: Number }],
original_language: { type: String },
title: { type: String },
backdrop_path: { type: String },
popularity: { type: Number },
vote_count: { type: Number },
video: { type: Boolean },
vote_average: { type: Number })
Array of ids
[ '577922', '487558', '429203' ]
I get the following error Cannot create field 'likes' in element whenever I am trying to push into my likeList array nested inside my comments.
When executing the following:
Feed.findOneAndUpdate(
{
owner: req.body.authorId,
"posts.comments.commentList._id": req.body.commentId
},
{
$push: {
"posts.$.comments.commentList.likes.likeList": {
user: req.user._id,
avatar: req.user.profile.profile_picture.url,
name: req.user.name
}
)
And my schema is as follows:
Feed Schema
owner: {
type: Schema.Types.ObjectId,
ref: "userType"
},
posts: [
{
author: {
userType: {
type: String,
enum: ["IndustryPartner", "User", "School"]
},
user: {
type: Schema.Types.ObjectId,
ref: "posts.author.userType", //<- This may cause an issue, if there are any issues with retrieving user fields, CHECK THIS
required: true
},
name: { type: String, required: true },
avatar: { type: String, required: true }
},
comments: {
totalComments: { type: Number, default: 0 },
commentList: [
{
likes: {
totalLikes: { type: Number, default: 0 },
likeList: [ <---//Trying to push here
{
user: { type: Schema.Types.ObjectId, ref: "User" },
avatar: { type: String },
name: { type: String },
date: {
type: Date,
default: Date.now
}
}
]
...
I am not sure if it's an issue with the query I am using in the first parameter to filter.
Update entire error message
It is odd because it appears that it is actually finding the correct commentList to go to, but is unable to access the likes field within the array itself. Am I wrong assuming that this should be able to step through it? posts.$.comments.commentList.likes.likeList
{ MongoError: Cannot create field 'likes' in element {commentList: [ { likes: { totalLikes: 0, likeList: [] },
_id: ObjectId('5cf6b3293b61fe06f48794e3'), user: ObjectId('5c9bf6eb1da18b038ca660b8'), avatar: "https://sli.blob.core.windows.net/stuli/
profile-picture-e1367a7a-41c2-4ab4-9cb5-621d2008260f.jpg", name: "Luke Skywalker", text: "Test comment from Luke", repliesToComment: [], date: new Date(1559671593009) } ]}
After further research, it appears the positional operator is no longer useful after stepping through 2 levels of arrays. So, the solution would be to use JS to change push the values into the array and then save them.
Schema:
let projectSchema = new Schema({
filters: [
{
name: { type: String, required: true},
items: {
q: { type: Number, required: true}
}
}
],
});
Update function:
const project = await mongoose.model('project').findById(id).exec();
console.log(filter); // { name: 'abc', items: [ { q: 3}]
project.filters.push(filter);
console.log(project.filters); // { _id: "123", name: 'abc' } // items array is missing
await project.save();
When I fetch a document via mongoose, then add an item to an array of that doc, only the first property is included.
Why is that?
I prefer not to use $push since the benefits of mongoose (validation etc) is not respected when $push is used.
The items field is an object instead of an array. Change your schema:
let projectSchema = new Schema({
filters: [
{
name: { type: String, required: true},
items: [ // square brackets here
q: { type: Number, required: true}
]
}
],
})
Suppose I have the following schemas:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
});
var QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
question: { type: String, required: true },
type: { type: String, enum: ['multiple choice', 'multiple select', 'short answer'] },
choices: [String],
answers: [String]
});
var QuestionGroupSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
prompt: { type: String },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
I am trying to design a way that will allow me to order questions and question groups together.
I was thinking maybe of adding a new field order
var QuizSchema = new mongoose.Schema({
// ...
order: [
{
type: { type: String, enum: ['Question', 'QuestionGroup'] },
id: mongoose.Schema.Types.ObjectId // reference
}
]
});
such that in the database, the field would contain something such as
[
{ type: 'Question', id: ObjectId('57867a34567g67790') },
{ type: 'Question', id: ObjectId('57867a34567g67765') },
{ type: 'QuestionGroup', id: ObjectId('69864b64765y45645') },
{ type: 'Question', id: ObjectId('57867a34567g67770') },
{ type: 'QuestionGroup', id: ObjectId('69864b64767y45647') }
]
This may mean that I would need to "populate" the ordered list of questions and question groups as
quiz.populate('questions questionGroups').exec(function (err, quiz) {
// sort questions and groups by the order
quiz.order = quiz.order.map(function (o) {
if (o.type === 'QuestionGroup') {
return quiz.questionGroups.id(o.id);
}
return quiz.questions.id(o.id);
});
});
So my question: is there a better way to design this?
Virtuals can come in handy here; without persisting order field in db and doing calculations on client each time:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
}
);
QuizSchema
.virtual('order')
.get(function() {
return this.questions.concat(this.questionGroups); //questions followed by questionGroups
});
Sort on createdAt is of course optional, but for that you need to have this field in Question and QuestionGroup:
Quiz.find({}, function (err, quiz) {
//...
})
.populate({path : 'questions', options: {sort: { 'createdAt': 1 }}})
.populate({path : 'questionGroups', options: {sort: { 'createdAt': 1 }}});
I'm getting null array value in main_categories. My schema is for brand collection:
Schema Definition
Schema.main_category = new SimpleSchema({
name: {type: String},
icon_image: {type: String},
description: {type: String}
});
Main_Category.attachSchema(Schema.main_category);
Schema.brand = new SimpleSchema({
name: {
type: String,
},
admin_number: {
type: String,
},
company_name: {
type: String,
},
owner_name: {
type: String,
},
owner_number: {
type: String,
},
admin_comment: {
type: String,
},
address: {
type: Schema.address,
},
logo_image: {
type: String
},
staffs: {
type: Array
},
"staffs.$": {
type: Object
},
"staffs.$.type": {
type: String,
allowedValues: ['admin']
},
"staffs.$.user_id": {
type: String
},
main_categories: {
type: [Schema.main_category]
},
sub_categories: {
type: [Schema.sub_category]
},
showcase: {
type: Boolean
}
});
Brand.attachSchema(Schema.brand);
Implementation
"addBrandMethod": function(jsonData) {
var json = {
name: jsonData.brandName,
admin_number: jsonData.adminNumber,
company_name: jsonData.companyName,
address: jsonData.companyAddress,
owner_name: jsonData.ownerName,
owner_number: jsonData.ownerNumber,
admin_comment: "jsonData.adminComment",
logo_image: "fasdfa",
staffs: [{
type: "admin",
user_id: "jaskjjkj"
}],
main_categories: [{
"_id": "uBibwEqaoDkZtXhsR",
"name": "Hair",
"icon_image": "nbdenck",
"description": "Hair Cut with Massage"
}
],
sub_categories: Sub_Category.find().fetch(),
showcase: true
};
Brand.insert(json);
return "Success";
}
Try removing the _id key from the main_categories array.
You didn't specify the _id key in the schema and simple-schema will only add the key when it's a schema that's attached to a collection.
I was getting main_categories object null because main_categories file alphabetically down from brand schema file.. and in brand schema file i was getting object of main_categories schema undefined. when i paste file up to brand schema file then problem solve..