2

I would like to attach some extra properties to mongoose schema fields and access them later.

Suppose I have a schema which looks like that:

var dauStatsSchema = {
    dtKey:{type:Number},
    date:{type:Date},
    appId: {type: String},
    users: {type: Number}
};

Now I'd like to add some metadata to each field, something like this:

var dauStatsSchema = {
    dtKey:{type:Number, selector: {$month: '$date'} },
    date:{type:Date, selector: {$week: '$date'} },
    appId: {type: String, selector: {$dayOfYear: '$date'}},
    users: {type: Number}
};

You will notice the selector properties I added and would like to access those from the model, by doing something like this:

mongoose.model('dauStats').dtKey.selector, mongoose.model('dauStats').date.selector, mongoose.model('dauStats').appId.selector etc.

Any ideas?

Thanks!

Leo
  • 900
  • 1
  • 11
  • 26

2 Answers2

2

Although I ended up taking another direction, I found a way to access the data in question by just doing:

myModel.prototype.schema.tree.dtKey.selector

If there is a "clearer" way of achieving the same - I'd love to hear.

Thanks!

Leo
  • 900
  • 1
  • 11
  • 26
0

Late answer (2023.), but maybe usefull for anyone who tries to find a solution:

Keep in mind that there is always a chance that a future update in mongoose will break your model, if you use custom properties. For example if the mongoose will want to use the "selector" property for something, and you have an invalid value stored in your existing schemas for this, you have to rewrite your models because your application will completlely crashed.

Alternative 1:

An alternative for Leo's answer: myModel.schema.paths.myFieldName.options.myUniqueProperty

The risks can significantly lowered if you use totally unique property names like "myUniqueSelectorToAvoidMongooseFutureCrashes" instead of "selector".

Alternative 2:

Or, you can get rid of the risks completely, if you use separate metadata objects, somewhere in your controller's code:

const mySelectors = {
    dtKey: {$month: '$date'},
    date: {$week: '$date'},
    appId: {$dayOfYear: '$date'}
}

Then you can find the selector later with:

    const selector = mySelectors.dtKey

Alternative 3:

If you want to remain inside your myModel.js file, you can add any kind of statics variables to the Schema also:
Another StackOverflow post about 'static' vs. 'instance' methods

Keep in mind that with this solution, the risk also lower, but not 0.
Later in your myModel.js, but before the module export:

mySchema.statics.myFieldMetadatas = {
    dtKey: {selector: {$month: '$date'}, anyOtherDataYouWant: 5},
    date: {selector: {$week: '$date'}, anyOtherDataYouWant: 4 },
    appId: {selector : {$dayOfYear: '$date'}, anyOtherDataYouWant: 3 }
}

You can access it later in your server with:

const selector = myModel.myFieldMetadatas.dtKey.selector
  • For your first alternative, there's also [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) to generate a unique value that you can use as a key. Define it in a file: `const metadata = Symbol('custom metadata');`, `require/import` it where needed and use like this: `myFieldName: {type: ..., [metadata]: {selector: ...}}`& `myModel.schema.paths.myFieldName.options[metadata].selector`. Even if the authors of Sequelize add their own symbol with 'custom metadata' as a description, all symbols are unique so there will never be a conflict. – RickN Jul 05 '23 at 12:45
  • You are right, I didn't think about the symbols, which is actually the solution for exactly this kind of problem. Thank you :) – László Bende Jul 06 '23 at 13:46