Gathering detailed insights and metrics for mongoose-association
Gathering detailed insights and metrics for mongoose-association
Gathering detailed insights and metrics for mongoose-association
Gathering detailed insights and metrics for mongoose-association
express-association
A controller plugin designed to utilize mongoose with the mongoose-associations plugin
mongoose-polymer
Polymorphic association for mongoose
mongoose-associations
Relations / Associations plugin for Mongoose
mongoose-polymorphic
create polymorphic associations in mongoose
A cleaner and faster way to setup mongoose populate with virtual field
npm install mongoose-association
Typescript
Module System
Node Version
NPM Version
JavaScript (56.07%)
TypeScript (43.93%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
10 Stars
69 Commits
2 Forks
3 Watchers
4 Branches
1 Contributors
Updated on Jul 12, 2023
Latest Version
0.0.44
Package Id
mongoose-association@0.0.44
Unpacked Size
283.60 kB
Size
45.87 kB
File Count
92
NPM Version
6.9.0
Node Version
8.11.3
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
3
1
A cleaner and faster way to setup mongoose populate with virtual field using normalized associations. This library is built for async await node environment minimum 7.6 This library is built for mongo 3.6, and mongoose version >=4.11.0
npm install mongoose-association
1 const mongoose = require('mongoose') 2 const { mongooseAssociation } = require('mongoose-association') 3 mongooseAssociation(mongoose)
mongoose-association
has 4 types of association to relation mongoose schemas.
belongsTo
o ->polymorphic
o =>hasOne
through
-> ohasMany
through
->> obelongsTo
this relation specify the reference lies directly on the schema and related to a single model using a foreignField
1belongsTo(foreignModelName, { as, localField, foreignField } = {}, schemaOptions = {}) { 2 3}
polymorphic
this relation specify the reference lies directly on the schema and related to multiple models
1polymorphic([foreignModelName, ...], { as, localField, foreignField, typeField } = {}, schemaOptions = {}) { 2 3}
hasOne
this relation specify the reference lies on another schema and it is unique
1hasOne(foreignModelName, { as, with, localField, foreignField, through, throughAs, throughWith } = {}) { 2 3}
hasMany
this relation specify the reference lies on another schema and it is non-unique
1hasMany(foreignModelName, { as, with, localField, foreignField, through, throughAs, throughWith } = {}) { 2 3}
As mongoose model classes are typically singular thus mongoose-association
stick to naming models using singular words.
As typically javascript utilize camel casing thus all auto generated naming is done using camel case
foreignModelName
the modelName property for a mongoose model holding the related schema.
as
the property on mongoose model instance that holds the related object
with
for has associations, specify which as property is referenced.
foreignField
the schema property for the relational reference typically an mongoose ObjectID
localField
the model instance property that holds relational record
through
through is used to associate via another document type other than the two document types forming the relation
throughAs
specifies the reference between the through model and associated model.
throughWith
specifies the reference between the through model and association origin model.
Once we have apply the plugin to mongoose, we can start defining some schemas
1const mongoose = require('mongoose') 2const { Schema } = mongoose 3 4const riderSchema = new Schema() 5riderSchema.belongsTo('Bike').index(1, { sparse: true }) 6riderSchema.belongsTo('Helmet')
Right here we have defined a schema for Rider. Using the belongsTo
method with the model Bike can automatically have the localField
defined as bike, this results in a standard mongoose virtual property bike on each instance. all mongoose-association
defined virtuals returns a promise and is designed to function with await
. A localField
was also automatically defined as bikeId. This will be the auto generated property storing the reference on the databased mongoose document. Here we have also applied an index on the localField
generated by belongsTo
1const bikeSchema = new Schema() 2bikeSchema.hasOne('Rider') 3bikeSchema.hasOne('Helmet', { 4 through: 'Rider' 5})
In this weird world each bike only have zero or one Rider and Helmet. we can now define a reverse relationship using hasOne
method. Because we know how Rider behaves with both Bike and Helmet. We can infer that a bike can only have zero or one Helmet. This inference can only be made through
the Rider
1const helmetSchema = new Schema() 2helmetSchema.hasOne('Rider') 3helmetSchema.hasOne('Bike', { 4 through: 'Rider' 5})
mongo's limitation on $lookup
restrict the through relationship to span only across three document types.
1const registrationSchema = new Schema() 2registrationSchema.belongsTo('Car') 3registrationSchema.belongsTo('Alien', { 4 as: 'owner' 5})
Lets visit another scenario that is a bit foreign, where we run an Alien Car Registration. There are two different ways an Alien can interact with a Registration. One is by assigning the as
to be owner, which renders the localField
to be ownerId. this allows the Alien to be the "owner" of the Car.
1registrationSchema.belongsTo('Alien', { 2 as: 'approver', 3 localField: 'approver_id' 4})
Similarly an Alien can also be an approver of the registration, and the localField
can be modified to use snake case approver_id or any format preferred.
1const alienSchema = new Schema() 2alienSchema.hasMany('Registration', { 3 as: 'ownedRegistration', 4 with: 'owner' 5})
With this robust system we can declare that each Alien may have many Registration. by default the as
would be the downcase pluralize version of the modelName
, in this case as registrations. We also had to apply owner for with
, otherwise we are unable to distinguish which interaction Alien has with
the Registration
1alienSchema.hasMany('Registration', { 2 as: 'approvedRegistrations', 3 with: 'approver' 4})
Alien can also be the approver of Registration. This is where we can define that each Alien to hasMany
approvedRegistrations using as
. The localField
is fetch via the reverse association as approver_id.
1alienSchema.hasMany('Car', { 2 through: 'Registration', 3 with: 'owner' 4})
hasMany
through
is normally used for many to many relationships. with
function in reverse of as
in term of its ability to defined the localField
and foreignField
used in the mongo $lookup
. In this case the owner will be used to reference ownerId. This relationship will result in the as
of cars, and using the localField
of carId
1alienSchema.hasMany('Car', { 2 through: 'Registration', 3 with: 'approver', 4 as: 'approvedCars' 5})
Similar to above case of the cars, this association stores approvedCars through
Registration using the approver for as
.
1const carSchema = new Schema() 2carSchema.hasOne('Registration') 3carSchema.hasOne('Alien', { 4 through: 'Registration', 5 throughAs: 'owner' 6})
This is a reverse hasMany
relationship coming from Car. The relationship to Alien is through
Registration thoughAs
owner.
1const assemblySchema = new Schema() 2const vehicleAssociation = assemblySchema.polymorphic(['Bike', 'Car'], { 3 as: 'vehicle' 4}) 5const partAssociation = assemblySchema.belongsTo('Part') 6assemblySchema.indexAssociations([vehicleAssociation, 1], [partAssociation, 1], { unique: true })
polymorphic
is last type of relation, and it is most similar of to belongsTo
, with the exception that more than one model can be associated. A as
is required for polymorphic association because mongoose-association
doesn't pick favors in auto generation. Though the localField
is inferred from the as
as vehicleId. polymorphic also generates an additional schema property storing the document type modelName
this property is auto generated using the foreignField
as vehicleIdType. Using the index Association
method we were able to create a compound index on the assembly schema using both the localField
and typeField
of the polymorphic
association along with the localField
of the belongsTo
with Part
1const partSchema = new Schema() 2partSchema.hasMany('Assembly') 3partSchema.hasMany('Bike', { 4 through: 'Assembly', 5 throughAs: 'vehicle' 6}) 7partSchema.hasMany('Car', { 8 through: 'Assembly', 9 throughAs: 'vehicle' 10})
A model can also define hasMany
through
a polymorphic
relationship. with
perform identical functionality in this scheme.
1carSchema.hasMany('Assembly', { 2 with: 'vehicle' 3}) 4carSchema.hasMany('Part', { 5 through: 'Assembly', 6 with: 'vehicle' 7}) 8 9bikeSchema.hasMany('Assembly', { 10 with: 'vehicle' 11}) 12bikeSchema.hasMany('Part', { 13 through: 'Assembly', 14 with: 'vehicle', 15 as: 'components' 16})
The reverse hasMany
relationships that is mostly auto generated
1carSchema.hasOne('Rating', { 2 with: 'vehicle' 3}) 4bikeSchema.hasOne('Rating', { 5 with: 'vehicle' 6}) 7 8const ratingSchema = new Schema() 9ratingSchema.polymorphic(['Bike', 'Car'], { 10 as: 'vehicle' 11}) 12ratingSchema.belongsTo('Alien') 13ratingSchema.hasOne('Rider', { 14 through: 'Bike' 15}) 16riderSchema.hasOne('Rating', { 17 through: 'Bike' 18}) 19 20alienSchema.hasOne('Rating') 21alienSchema.hasOne('Car', { 22 through: 'Rating', 23 throughAs: 'vehicle', 24 as: 'ratedCar' 25})
Make sure the model is defined after all the schema fields. Otherwise the getters and setters on the model instance will miss behave
1const Rider = mongoose.model('Rider', riderSchema) 2const Bike = mongoose.model('Bike', bikeSchema) 3const Helmet = mongoose.model('Helmet', helmetSchema) 4const registration = mongoose.model('Registration', registrationSchema) 5const Alien = mongoose.model('Alien', alienSchema) 6const Car = mongoose.model('Car', carSchema) 7const Assembly = mongoose.model('Assembly', assemblySchema) 8const Part = mongoose.model('Part', partSchema) 9const Rating = mongoose.model('Rating', ratingSchema) 10const Settings = mongoose.model('Settings', settingsSchema)
creating records with relationship
1const bike = await new Bike().save() 2const helmet = await new Helment().save() 3const rider = await new Rider({ 4 bike, 5 helmet 6}).save() 7const bikeId = rider.bikeId // returns ObjectId
updating relationship on record
1const anotherBike = await new Bike().save() 2rider.bike = anotherBike 3await rider.save()
working with polymorphic relationship
1const bike = await new Bike().save() 2const rating = await new Rating({ 3 vehicle: bike 4}).save() 5const car = await new Car().save() 6rating.vehicle = car 7rating.save()
from the object itself
1const rider = await Rider.findOne() // request count 1 2rider.populateAssociation('bike.rating', 'helmet') // request count 4 3const bike = await rider.bike // request count 4 4const helmet = await rider.helmet // request count 4 5const rating = await bike.rating // request count 4
from the model
1const rider = await Rider.findOne() // request count 1 2Rider.populateAssociation(rider, 'bike.rating', 'helmet') // request count 4 3const bike = await rider.bike // request count 4 4const helmet = await rider.helmet // request count 4 5const rating = await bike.rating // request count 4
from the query is slightly more efficient allows for further optimization
1const rider = await Rider.findOne().populateAssociation('bike.rating', 'helmet') // request count 2 2const bike = await rider.bike // request count 2 3const helmet = await rider.helmet // request count 2 4const rating = await bike.rating // request count 2
1const rider = await Rider.findOne().populateAssociation('bike.rating', 'helmet') // request count 2 2const bike = await rider.fetchBike() // request count 3 3const sameBike = await rider.fetchBike().populateAssociation('rating') // request count 4 4const rating = await sameBike.rating // request count 4
unsetting association cache
1const rider = await Rider.findOne().populateAssociation('bike.rating', 'helmet') // request count 2 2const bike = await rider.unsetBike().bike // request count 3
helper methods to do more meta programming
1const rider = await Rider.findOne().populateAssociation('bike.rating', 'helmet') // request count 2 2const bike = await rider.fetch('bike') // request count 3 3const sameBike = await rider.unset('bike').bike // request count 4 4const anotherSameBike = await rider.unset().bike // request count 5
All mongoose Queries and Aggregates now have the explain method which returns the population plan it will use to optimize the fetching of associations.
1Rider.findOne({ _id: riders[0]._id }).populateAssociation('helmet', 'bike.assemblies.part')._explain() 2[ 3 [ 4 'aggregate', 'Rider', [{ 5 $match: { 6 _id: 5 b47845d8222031fd88ef049 7 } 8 }, { 9 $limit: 1 10 }, { 11 $lookup: { 12 from: 'helmets', 13 'let': { 14 localField: '$helmetId' 15 }, 16 pipeline: [{ 17 $match: { 18 $expr: { 19 $eq: ['$$localField', '$_id'] 20 } 21 } 22 }], 23 as: 'helmet' 24 } 25 }, 26 { 27 $unwind: '$helmet' 28 }, { 29 $lookup: { 30 from: 'bikes', 31 let: { 32 localField: '$bikeId' 33 }, 34 pipeline: [{ 35 $match: { 36 $expr: { 37 $eq: ['$$localField', '$_id'] 38 } 39 } 40 }], 41 as: 'bike' 42 } 43 }, 44 { 45 $unwind: '$bike' 46 } 47 ], [ 48 'aggregate', 'Assembly', [{ 49 $match: { 50 vehicleId: { 51 $in: [ 'Bike._id' ] 52 }, 53 vehicleIdType: 'Bike' 54 } 55 }, { 56 $lookup: { 57 from: 'parts', 58 let: { localField: '$partId' }, 59 pipeline: [{ 60 $match: { 61 $expr: { 62 $eq: [ '$$localField', '$_id' ] 63 } 64 } 65 }], 66 as: 'part' 67 } 68 }, { 69 $unwind: '$part' 70 } 71 ] 72]
built @
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
license file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
31 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More