Mongoose $inc with enum validation failed - mongodb

I'm working with featherJs and mongoose.
I have a schema with an enum which I want to $inc.
new Schema({
status: { type: Number, enum: [0, 1, 2, 3], default: 0 },
});
But when I $inc the 'status' field, it doesn't care about the enum, I can $inc as many times I want and get a status to 100000.
I don't know if it's because of featherjs or something else
Thanks

This is "working as intended", as specified in the docs:
enum: Array, creates a validator that checks if the value is strictly equal to one of the values in the given array.
So enum just creates a mongoose validator, that is:
Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default.
In theory you should be using their update validators for this, however $inc is not supported by them, and in addition their behavior is not quite clear at times.
I personally would recommend not to use mongoose at all, it's a black box that only adds bugs and confusion. specifically when it comes to their "validators" which are not "real" validators.
So what can you do to fix this?
The easiest solution is to just do it in code, first find the object and if it fits the criteria only then do the $inc, obviously this does not give actual validation and is only supported where you'd implement it, if you have many places in the code where such update can occur this is also not optimal.
use mongodb validation, this is "real" validation that actually validates at the db level. For example you could create your collection:
db.createCollection('collection_name', {
validator: {
$jsonSchema: {
bsonType: 'object',
properties: {
status: {
bsonType: 'int',
enum: [0, 1, 2, 3],
description: 'must be a valid status integer',
},
},
},
},
validationAction: 'error',
});
Now any update with a none valid value will fail.

Related

Mongoose {$exists: false} not working, why?

I have the following query:
const messageRules = await MessageRule.findOne({
reservationLength: {$exists: false}
});
on the following schema:
const MessageRule = new Schema(
{
...,
reservationLength: {type: Number, default: 1},
...
}
);
And the query returns a document with:
{
...,
reservationLength: 1,
...
}
I'm going crazy here. Does it have something to do with the default setting in my schema? Any other ideas?
Its a bug i've encountered with mongoose several times already and i did not find too much information about it (granted i decided not to waste time exploring it).
It occurs with all Default value'd fields, mongoose just automatically sets these values to their defaulted value on the return call (if you check the actual document in the database it will not have this field set).
One easy fix to ease the nerve is to add lean() to the call:
const messageRules = await MessageRule.findOne({
reservationLength: {$exists: false}
}).lean();
For some reason this ends up fixing the bug (debatably feature ???)

Mongoose prevent assignment of default value to enum

I have the following property in object defined in Mongoose schema, is there a way to prevent Moongose from assigning default enum value on App.create() or during upserting
band_collection: {
type: String,
enum: COLLECTIONS.concat('Custom')
}
// collection
COLLECTIONS = ['red', 'white', 'blue']
Hmm nope that shouldn't be happening, that code looks correct as far as I can tell. Can you provide a more complete code sample?

What exactly is "data" that is passed to responses?

I'm writing a custom response that takes data as an input, and I am finding strange properties being added, namely:
add: [Function: add],
remove: [Function: remove]
When I log out some example data, I get:
[ { books:
[ { id: 1,
title: 'A Game of Thrones',
createdAt: '2015-08-04T04:53:38.043Z',
updatedAt: '2015-08-04T04:53:38.080Z',
author: 1 } ],
id: 1,
name: 'George R. R. Martin',
createdAt: '2015-08-04T04:53:38.040Z',
updatedAt: '2015-08-04T04:53:38.073Z' },
{ books:
[ { id: 2,
title: 'Ender\'s Game',
createdAt: '2015-08-04T04:53:38.043Z',
updatedAt: '2015-08-04T04:53:38.080Z',
author: 2 },
{ id: 3,
title: 'Speaker for the Dead',
createdAt: '2015-08-04T04:53:38.043Z',
updatedAt: '2015-08-04T04:53:38.081Z',
author: 2 } ],
id: 2,
name: 'Orson Scott Card',
createdAt: '2015-08-04T04:53:38.042Z',
updatedAt: '2015-08-04T04:53:38.074Z' } ]
Which looks innocent enough, but results in the strange add and remove functions when I use a custom serializer on it. If I take this data and hard-code it straight into the serializer, those are not present. Apparently something is lurking inside of data that's not being printed to the console.
So, what is data?
Edit: So, I'm still not quite sure what other magical properties live in here, but:
Object.keys(data[0].books))
reveals
[ '0', 'add', 'remove' ]
Which is where those are coming from. Why is this included in the data passed to custom responses? And what else might be hiding in there...
More importantly, how do I strip this gunk out and make data a normal object?
JSON.parse(JSON.stringify(data));
That cleans it up nicely, though it feels like a hack. (Actually, it's definitely a hack.)
I assume your data attribute is returned by a database query. e.g.:
Model.find(...).exec(function (err, data) { ... });
But what are these .add() and .remove() methods?
Here is what you can find in the docs:
For the most part, records are just plain old JavaScript objects (aka POJOs). However they do have a few protected (non-enumerable) methods for formatting their wrapped data, as well as a special method (.save()) for persisting programmatic changes to the database.
We can go deeper:
"collection" associations, on the other hand, do have a couple of special (non-enumerable) methods for associating and disassociating linked records. However, .save() must still be called on the original record in order for changes to be persisted to the database.
orders[1].buyers.add({ name: 'Jon Snow' });
orders[1].save(function (err) { ... });
So these methods (.add(), .remove(), .save()) are useful if you play with "collection" associations.
How to remove them?
You'll need to use .toObject() which returns a cloned model instance stripped of all instance methods.
You might want to use .toJSON() that also returns a cloned model instance. This one however includes all instance methods.

Mongoose OR operator for schema definitions

Does Mongoose support, or is there an available package that supports multiple "options" for the embedded schemas in an array?
For example, the things property can contain only one of two schemas:
new Schema({
things: [{
requiredProp: String,
otherProp: Number
}, {
otherOption: Number
}]
});
In other words, I do not want to just allow anything (AKA Schema.Types.Mixed) to be stored in this property, but only these two possible definitions.
Or, do schema design recommendations exist to avoid this problem?
You should only define one dict in the array type of the schema, and then set if they are required or not with the mongoose schema types logic. Use pre save if you want to do more logic to assure that either one of the fields have been set, like this:
var MySchema = new Schema({
things: [{
requiredProp: {type: String, required: true},
otherProp: Number,
otherOption: Number,
}]
});
MySchema.pre('save', function(next) {
if (!this.otherProp && !this.otherOption) {
next(new Error('Both otherProp and otherOption can\'t be null'))
} else {
next()
}
})
Opon saving the object it will return an error if neither otherProp nor otherOption has been set.

Posting limited fields with ExtJS 4.2.x REST on store update

We are trying to use ExtJS grid/forms and bind them with Store to perform REST operations.
Now as I was playing with extjs examples for restful api, I came across http://docs.sencha.com/extjs/4.2.1/#!/example/restful/restful.html and tried editing a model to add a new field 'phone' in the list like below:
Ext.define('Person', {
extend: 'Ext.data.Model',
fields: [{
name: 'id',
type: 'int',
useNull: true
}, 'email', 'first', 'last','phone'],
validations: [{
type: 'length',
field: 'email',
min: 1
}, {
type: 'length',
field: 'first',
min: 1
}, {
type: 'length',
field: 'last',
min: 1
}]
});
As you can see "phone" is a new field added in fields list of a model and after adding that field while I was trying to perform any of the rest operation (PUT/POST) it was posting that field along with rest of the visible fields in grid. This is something I have not expected.
Is there anyway by which I can just post dirty fields (which are modified) and not all those exist in model with default store/rest manipulation way that ExtJS has provided?
In the proxy writer definition you would want to use the writeAllFields param which will work for updates. New model instances will send all fields.
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.writer.Writer-cfg-writeAllFields