Is possible to have multiple different types of refs for a single attribute in mongoose (mongodb) - mongodb

I am using mongodb with mongoose. And i am wondering if it is possible to have multiple references for an object id attribute in a schema.
I have tried the code underneath and it did not work.
const Schema = new Schema({
refrens: {
type: ObjectId,
// this did not work
ref: [
"Post",
"Account"
],
required: true
}
});
I know it is possible to remove the ref attribute (field, key) and then all object ids are valid but i want certain object ids to be valid, the object ids of the Post and Account model.
const Schema = new Schema({
refrens: {
// This will allow all different types of object ids to be the value
type: ObjectId,
required: true
}
});

Look like refPath is what you need. You can do something like this:
const Schema = new Schema({
refrens: {
type: ObjectId,
refPath: 'onModel',
required: true
},
onModel: {
type: String,
required: true,
enum: ['Post', 'Account']
}
});

Related

Mongoose set default on field update if field is not present or null

I have a mongoose schema like this suppose:-
var mSchema = new Schema({
name: { type: String, required: true}
});
and have been using this schema for a year and now i want to add gender to it like this :-
var mSchema = new Schema({
name: { type: String, required: true},
gender: { type: String, default: 'Male' }
});
whenever there will be an update request i want this gender to automatically set Male as default but i found that default don't set on update request.
(Note: It's just an example not a real life scenario. i just want mongoose default work if field is not present or null)
Is there any way in which i can set default on the updation of document ?
If you are using a function like update(), then this is not directly possible as stated by this answer. Still, you can simply switch to a function like findOne() and use save(), which should do the same.
When upserting documents, you can also check out the setDefaultsOnInsert option: https://mongoosejs.com/docs/defaults.html#the-setdefaultsoninsert-option
const options = {
// Create a document if one isn't found. Required
// for `setDefaultsOnInsert`
upsert: true,
setDefaultsOnInsert: true
};
await XY.findOneAndUpdate(query, update, options);

Mongoose Schema planning: using ObjectID reference or using array of type: [otherSchema]?

I'm currently planning out the database structure for an app I'm building, and something in this linked answer raised some questions for me.
In the structure that Shivam proposed, he sometimes references another collection directly, in other words within one Schema he defines the type of a field to be an array of another schema type. Example:
import { Schema } from "mongoose";
import { QuestionSchema } from "./question-schema";
const mongoose = require('mongoose');
export const QuestionSetSchema: Schema = new Schema({
questionSet: {
type: [QuestionSchema],
validate: {
validator: function(value: any) {
return value.length === 12;
},
message: 'Question set must be 12.'
}
},
}, {
timestamps: true
});
In other cases, he only uses an ObjectID reference to another schema/collection:
export const CandidateSchema: Schema = new Schema({
name: String,
email: String, // you can store other candidate related information here.
totalAttempt: {
type: Number,
default: 0,
validate: {
validator: function(value: number) {
return value === 3;
},
message: 'You have already done three attempts.'
}
},
candidateQuestionAnswers: {
type: [Schema.Types.ObjectId],
ref: 'CandidateQuesAnswer'
}
}, {
timestamps: true
});
What are the use cases for each of the above? Does the "type:[otherSchema]" method actually embed instances of that collection or does it only provide their properties to the Schema they are called from?

Why can't mongo push my ObjectId in a predefined array?

I have made my model, it has this Schema:
const CompanySchema = new Schema({
companyName: {
type: String,
required: [true,'the name of the companies working on the game are missing.']
},
companyAge: {
type: Number,
required: [true,'the age of the company is missing.']
},
companyDeveloper:[{
type: Schema.Types.ObjectId,
ref: "developer"
}]
});
I am trying to push a element into the companyDeveloper array like this:
addDev(req,res,next){
const companyId = req.params.id;
const companyDeveloper = ObjectId.fromString(req.body.companyDeveloper);
Company.findById({_id: companyId})
.then((company) => company.companyDeveloper.push({companyDeveloper}))
.then(company => res.send(company))
.catch(next);
}
but I keep getting this error:
"error": "ObjectId is not defined".
Before I tried casting it, I got this error
Cast to ObjectId failed for value
How can I get this function to work?
printscreen
postman call error
The ObjectId class from mongoose is defined on Mongoose.Schema.Types.ObjectId
You can require it in the file where you define addDev
const ObjectId = require('mongoose').Schema.Types.ObjectId
or load mongoose into a global where you initialize your node code so you can access it in any file:
global.Mongoose = require('mongoose')
And then use it in your method:
const companyDeveloper = Mongoose.Schema.Types.ObjectId.fromString(req.body.companyDeveloper);

mongoose schema multi ref for one property

How to write multi ref for one property of one mongoose schema, like this(but wrong):
var Schema = mongoose.Schema;
var PeopleSchema = new Schema({
peopleType:{
type: Schema.Types.ObjectId,
ref: ['A', 'B'] /*or 'A, B'*/
}
})
You should add string field to your model and store external model name in it, and refPath property - Mongoose Dynamic References
var Schema = mongoose.Schema;
var PeopleSchema = new Schema({
externalModelType:{
type: String
},
peopleType:{
type: Schema.Types.ObjectId,
refPath: 'externalModelType'
}
})
Now Mongoose will populate peopleType with object from corresponding model.
In the current version of Mongoose i still don't see that multi ref possible with syntax like you want. But you can use part of method "Populating across Databases" described here. We just need to move population logic to explicitly variant of population method:
var PeopleSchema = new Schema({
peopleType:{
//Just ObjectId here, without ref
type: mongoose.Schema.Types.ObjectId, required: true,
},
modelNameOfThePeopleType:{
type: mongoose.Schema.Types.String, required: true
}
})
//And after that
var People = mongoose.model('People', PeopleSchema);
People.findById(_id)
.then(function(person) {
return person.populate({ path: 'peopleType',
model: person.modelNameOfThePeopleType });
})
.then(populatedPerson) {
//Here peopleType populated
}
...

mongoose dynamically attaching schema to validate against on the fly

I'd like to dynamically attach a schema to a particular field, based on some business logic in schema.pre('save',function(..){...}). How to do this, if at all possible?
Some (simplified) schemas and background:
var fact= new Schema({
name: { type: String, required: true, index: { unique: false }}
,value: {type: {}, required: true}
,moreinfodesc: { type: String, required: false}
,createddate : { type: Date, required: true, default: Date.now, select: false } }
}, { collection: 'Fact' } );
var factSchema= new Schema({
name: { type: String, required: true, index: { unique: true }}
, valueType: { type: {}, required: true}
,isMulti: {type: Boolean, required: true }
//ACL-stuff
,directChangeRoles: {type: [String]} //i.e: [super, admin,owner]
,suggestChangeRoles: {type: [String]} //ie: [editor]
,enum: {type: [{}]}
,mixins: {type: [String]}
}, { collection: 'FactSchema' });
This is a simplified structure to allow "facts' of a particular 'entity' to be edited.
e.g: entityA.facts=[fact]
As can be seen from the schema's fact.value can have any type as far as mongoose is concerned. I however, want to constrain it at runtime to the schema as defined in
FactSchema.valueType (a String containing "Boolean", "String" or something more complex as "[Tag]") . This might all seem cumbersome, but this is the way i'd like to go for several reasons.
So let's say that for a particular fact with fact.name=tags I want to assign fact.value the type [Tag] at runtime. For this I would have setup a Tag-schema with validation like usual and have fact.value validate against it.
I was thinking of somehow "attaching" the [Tag]-schema to fact.value in fact.pre('save',function(..){.. //validation here }) and hope validation would magically happen as if fact.valuewas assigned the type [Tag] at design-time instead of run-time.
Finally the question: I've got no idea if it's possible to do that 'attach' and if so, how?
Thanks.
"attaching" at run time isn't possible but you can add a custom validator to your path and base its logic on current document state:
https://gist.github.com/2789681
Try using mongoose discriminators
And if needed, you can alter the validation at runtime with:
YourModelName.schema.path('your_field_name').required(false);