These are chat archives for Automattic/mongoose

29th
Jun 2018
Srinivasan
@kksrini89
Jun 29 2018 06:54
hey guys, can you help me this?
new mongoose.schema({
  margin:{
   type: Number,
   default:0,
   validate : [validators.isInt,'Value should be either int or decimal']
 }
})
I want to validate margin field either integer or decimal type
how to have either wise validator condition here?
Srinivasan
@kksrini89
Jun 29 2018 07:45
Thanks anyways, I handled it myself
Ian Paschal
@ianpaschal
Jun 29 2018 14:23

Hello everyone, I have a question. I was hoping to use Mongoose on a project which involves getting documents with varying structures. This is sort of the main reason I opted for MongoDB instead of a relational database with tables. I figured out how to put documents with many different schemas into one collection.

But now I cannot figure out how to access them as it semes queries must be performed as a method of a given model. Instead, I want to get all the documents within a given collection regardless of which model they were created with.

Is this possible, and if not? Whyyyy? If you have to query up front a specific model with spcific properties you might as well be querying a table...

Kev
@lineus
Jun 29 2018 14:26
@ianpaschal are you using discriminators?
Ian Paschal
@ianpaschal
Jun 29 2018 14:29
I don’t think so? Not sure what those are
Kev
@lineus
Jun 29 2018 14:31
it's a way of storing documents in a collection that inherit properties from a common schema. Are you just using a Mixed schemaType then?
Ian Paschal
@ianpaschal
Jun 29 2018 14:31
For example, I made some schemas, one for “nut” and one for “bolt”, which of course have different attributes to them.
And for each one: Mongoose.model( “Nut", nutSchema, "parts" );
now in my UI i want to query the database for all documents in parts, be they nut, screw, bolt, washer, etc.
but indeed i would be open to using one general model as all have certain similar properties such as “material” or “weight”. To do this I was mixing in a “metadata” schema with fields like “brand” and “product number” and a “physical” schema with fields like “material” and “weight”, and then an object called “technical” which would have the technical properties of that particular type of part
Kev
@lineus
Jun 29 2018 14:35
check out the discriminator docs here, it might be relevant to your situation.
Ian Paschal
@ianpaschal
Jun 29 2018 14:35
Will do. Thanks for the pointer.
am i understanding right that basically a sub class of whatever model you created?
Ian Paschal
@ianpaschal
Jun 29 2018 14:42
const Part = mongoose.model( "Part", partSchema, "parts" );
const Nut = Part.discriminator( "Nut", new mongoose.Schema({
    // … props of nuts
},  { discriminatorKey: "partType" }));
something like that?
Kev
@lineus
Jun 29 2018 14:44
make sure and declare the discriminatorKey on the base Model Schema as well, but yeah that's it.
Ben Chiciudean
@benydc
Jun 29 2018 15:47
I have an issue with a document property not showing up in the modifiedPaths when pre-saving
        user.password = password;
        user.resetPasswordToken = null;
        user.resetPasswordExpires = null;
        await user.save();
I change user password and save and in pre-save:
    console.log(user.modifiedPaths());

    if (!user.isModified('password')) return next();

    bcrypt.genSalt(SALT_FACTOR, (err, salt) => {
        if (err) return next(err);
        bcrypt.hash(user.password, salt, (err, hash) => {
            if (err) return next(err);
            user.password = hash;
            next();
        });
    });
only resetPasswordToken and resetPasswordExpires show up in modifiedPaths
Kev
@lineus
Jun 29 2018 15:59
@benydc can you share the whole pre save hook?
Ben Chiciudean
@benydc
Jun 29 2018 16:00

userSchema.pre<IUser>("save", async function (next) {
    if (!this.createdAt) {
        this.createdAt = new Date();
    }
    const user = this,
        SALT_FACTOR = 5;

    if (!user.password) {
        const buffer = await crypto.randomBytes(24);
        user.password = await crypto.createHmac('sha256', buffer.toString('hex')).digest('hex');
    }

    if (user.resetPasswordExpires && user.isModified('resetPasswordExpires')) {
        const buffer = await crypto.randomBytes(20);
        user.resetPasswordToken = await buffer.toString('hex');
    }

    if (user.isModified('email')) {
        if (user.active)
            user.active = false;

        let activateHash = new ActivateHash();
        activateHash.userId = user._id;
        await activateHash.save();

        emailService.sendVerifyEmail(user, activateHash.hash);
    }

    console.log(user.modifiedPaths());

    if (!user.isModified('password')) return next();

    bcrypt.genSalt(SALT_FACTOR, (err, salt) => {
        if (err) return next(err);
        bcrypt.hash(user.password, salt, (err, hash) => {
            if (err) return next(err);
            user.password = hash;
            next();
        });
    });
});
Kev
@lineus
Jun 29 2018 16:07
@benydc I'm assuming that you verified that the condition if (!user.password) { is met and that the response from crypto.createHmac fits into the shape of the schema path of user.password?
Ben Chiciudean
@benydc
Jun 29 2018 16:09
the condition if (!user.password) is for checking if there is no password set and it will set a random one with crypto
I don’t think that’s an issue
Kev
@lineus
Jun 29 2018 16:13
oh, I see, you're manually changing your password outside. sorry.
@benydc there's nothing wrong with your hook, can you share the part of your code where you query for a doc, update the property and save it? example
Ben Chiciudean
@benydc
Jun 29 2018 16:23
export const resetPassword = async (ctx: Context) => {
    const { userEmail, password, token } = ctx.request.body;

    try {
        let user = await User.findOne({ email: userEmail });
        if (user.resetPasswordExpires < Date.now())
            return ctx.body = new PasswordResetError();

        if (user.resetPasswordToken !== token)
            return ctx.body = new PasswordResetError();

        user.password = password;
        user.resetPasswordToken = null;
        user.resetPasswordExpires = null;
        await user.save();

        return ctx.body = { code: 200, message: "Password reset success." };
    } catch {
        return ctx.body = new MongoExceptionError();
    }
}
Kev
@lineus
Jun 29 2018 16:25
@benydc what version of mongoose are you using?
Ben Chiciudean
@benydc
Jun 29 2018 16:25
5.1.7
Kev
@lineus
Jun 29 2018 16:28
do you have any validation on the password path?
@benydc ^
Ben Chiciudean
@benydc
Jun 29 2018 16:28
nope, just String type
Kev
@lineus
Jun 29 2018 16:31
the only way I can think of to duplicate this is to set user.password to the exact same value it was before like this. which in your scenario is extremely unlikely.
Ben Chiciudean
@benydc
Jun 29 2018 16:34
nah, I tried different values
Kev
@lineus
Jun 29 2018 16:36
@benydc it shouldn't matter, but have you tried user.set('password', password) instead of user.password = password?
Ben Chiciudean
@benydc
Jun 29 2018 16:49
yes, still same
Kev
@lineus
Jun 29 2018 17:05
@benydc I tried to replicate it more thoroughly here minus the activateHash/email bits, but it works for me.
Ben Chiciudean
@benydc
Jun 29 2018 17:09
I look into it more thoroughly
don’t worry
Kev
@lineus
Jun 29 2018 17:12
@benydc let me know what you find out, I'm curious not worried :smile:
Ben Chiciudean
@benydc
Jun 29 2018 17:26
it’s working
I did a restart of my laptop
Kev
@lineus
Jun 29 2018 19:28
that's weird :)
Ben Chiciudean
@benydc
Jun 29 2018 19:31
yeah
Lamp
@ledlamp
Jun 29 2018 21:04
hey how can i add like a hook to a schema so i can run a function whenever a model is saved?
oh middleware
ok thanks
Kev
@lineus
Jun 29 2018 21:06
you're welcome
:smile: