MongooseError: Cannot read property 'options' of undefined when setting reference - mongodb

Trying to set a simple reference field in Mongoose is giving me huge problems.
I get the following error. As far as I can tell there are no actual validation errors.
'contents.0.modules.0.matches.0.':
{ MongooseError: Cannot read property 'options' of undefined
at ValidatorError (C:\Users\Simon\Documents\Projects\eventvods\node_modules\mongoose\lib\error\validator.js:24:11)
at _init (C:\Users\Simon\Documents\Projects\eventvods\node_modules\mongoose\lib\document.js:372:37)
...
at init (C:\Users\Simon\Documents\Projects\eventvods\node_modules\mongoose\lib\document.js:348:7)
at model.Document.init (C:\Users\Simon\Documents\Projects\eventvods\node_modules\mongoose\lib\document.js:313:3)
message: 'Cannot read property \'options\' of undefined',
name: 'ValidatorError',
properties: [Object],
kind: 'cast',
path: undefined,
value: undefined } } }
Mongoose schema like so
var matchSchema = new Schema({
team1: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Teams'
},
team2: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Teams'
},
team1_2: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Teams'
},
team2_2: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Teams'
},
...
});
var moduleSchema = new Schema({
matches: [matchSchema],
...
});
var sectionSchema = new Schema({
modules: [moduleSchema],
...
});
A sample object that fails to save:
{
team1: 5835a5f653d4ce23bb33ab19,
team2: 5835a70353d4ce23bb33ab21
}

So this was a weird one, but I was able to bypass it with a little awkward manipulation.
Creating a new schema field of the same type, and setting that to
the value.
Going through all my documents, and setting the
original field to that field's value.

this is your team1 definition:
team1: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Teams'
}
and this is your data in mongo:
team1: 5835a5f653d4ce23bb33ab19
As you can see, team1 object type is not ObjectId! it's just a normal String!
Mongo stores a reference like this:
team1: {
"$ref" : "Teams",
"$id" : ObjectId("5835a5f653d4ce23bb33ab19")
}
So either fix your data in mongo or fix your scheme!

You have not defined your schema correctly. It should be more like so:
var matchSchema = new Schema({
team1: {
type: mongoose.Schema.Types.ObjectId,
ref: String
},
team2: {
type: mongoose.Schema.Types.ObjectId,
ref: String
},
team1_2: {
type: mongoose.Schema.Types.ObjectId,
ref: String
},
team2_2: {
type: mongoose.Schema.Types.ObjectId,
ref: String
},
...
});

Related

Create ref to sub document's array for each property of subdocument

Model A looks like
{ a : {
step1: [{
type: { type: String },
category: { type: String },
}],
step2: [{
type: { type: String },
category: { type: String },
}]
} }
Model B which I wanted to create should contain a prop which will ref to Model A.step1 or A.step2 , trying to acheive this by following
{ progress : [
{
step: {
type: Schema.Types.ObjectId,
ref: "A.a.{What should be here}",
required: true,
},
details: { type: Schema.Types.Mixed },
}
]
}
I think you need to create Schemas for all of them :)
I would just separate the three completely - not sure if this is viable to you as the whole idea behind this is a bit mysterious, but would something like this work for you?
const stepSchema = new Schema({
type: String,
category: String,
});
const Step = mongoose.model("steps", stepSchema);
const aSchema = new Schema({
step1: [stepSchema],
step2: [stepSchema],
});
const A = mongoose.model("as", aSchema);
const progressSchema = new Schema({
a: { type: aSchema, required: true, ref: "as"},
progress: [{ type: stepSchema, required: true, ref: "steps" }],
details: Schema.Types.Mixed,
});
const Progress = mongoose.model("progresses", aSchema);

Collection referencing multiple collections

I want to have a collection with multiple fields referencing multiple collections, something like it:
var comboSchema = new Schema({
oneId: { type: Schema.Types.ObjectId, ref: "One" },
twoId: { type: Schema.Types.ObjectId, ref: "Two" },
threeId: { type: Schema.Types.ObjectId, ref: "Three" },
components: {
id: {type: Schema.Types.ObjectId, ref: "Component"},
amount: {type: Number}
}
}
I know I can use $lookup and aggregate to get data, but it looks like it works only on a single field in a collection?
Any help? Thank you! :-)
This is a model sample using the ref, the ref key in the object will take the name of the model in which you are referencing
const mongoose = require('mongoose');
const postSchema = mongoose.Schema({
text: {
type: String,
required: 1
},
mediatype: {
type: String,
required: 1
},
media: {
type: String,
required: true
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
likes: {
type: [{
userid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}
}]
},
comments: {
type: [{
userid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
comment: String
}]
},
}, {
timestamps: true
})
const Post = mongoose.model('post', postSchema)
module.exports = Post
so you can then populate it like Post.find().populate('user')

Ordering two reference arrays together

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 }}});

Mongoose Sailsjs Schema hasn't been registered for model when populating

I'm trying to get mongoose working with sailsjs using this hook
https://github.com/mikermcneil/sails-hook-orm-mongoose
which work fine until I try to populate, when I get the error
MissingSchemaError: Schema hasn't been registered for model
Here's and example of my schema setup.
var Schema = sails.mongoose.Schema;
module.exports = {
schema: {
product: {
type: Schema.Types.ObjectId,
ref: 'Product'
},
order: {
type: Schema.Types.ObjectId,
ref: 'Order'
},
qty: {
type: Number,
default: 1
},
image: {
type: Schema.Types.ObjectId,
ref: 'Image'
},
total: Number,
custom_items: [{
type: Schema.Types.ObjectId,
ref: 'CustomLineItem'
}],
createdBy: {
type: Schema.Types.ObjectId,
ref: 'User',
index: true
},
owner: {
type: Schema.Types.ObjectId,
ref: 'User',
index: true
}
},
/**
* constructSchema()
*
* Note that this function must be synchronous!
*
* #param {Dictionary} schemaDefinedAbove [the raw schema defined above, or `{}` if no schema was provided]
* #param {SailsApp} sails [just in case you have globals disabled, this way you always have access to `sails`]
* #return {MongooseSchema}
*/
constructSchema: function(schemaDefinedAbove, sails) {
// e.g. we might want to pass in a second argument to the schema constructor
var lineItemSchema = new sails.mongoose.Schema(schemaDefinedAbove, {
autoIndex: false,
collection: 'lineitem'
});
return lineItemSchema;
}
};
Found it was because my ref needed to be lowercase

How to save populated Document?

Is it possible to save a populated document?
I am trying to do:
var Group = new Db['Group']();
for (var i=0; i<50; i++)
Db.Members.push({ User: { _id: "521014731e27b1b008000002"}, pseudo: 'John' });
Group.save();
Schemas
var GroupSchemaModel = {
Members: [{
User: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
updated_at: { type: Date, required: true, default: Date.now }
}]
};
I get the error
{ message: 'Cast to ObjectId failed for value "[object Object]" at path "User"',
name: 'CastError',
type: 'ObjectId',
value: { _id: '521014731e27b1b008000002' },
path: 'User' }
This:
User: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
Tells mongoose that User field will be a collection of references of type ObjectId pointing to another collection.
You, on the other hand, are trying to insert an object there:
Db.Members.push({ User: { _id: "521014731e27b1b008000002"}, pseudo: 'John' });
Mongoose tries to cast it to ObjectId and fails. That's apart from the fact that pseudo field isn't in the group schema.
Try this instead:
Db.Members.push({User: mongoose.Types.ObjectId("521014731e27b1b008000002"), updated_at: whatever});