Select: false doesn't work on timestamp fields #4868

Open
jensengar opened this Issue Jan 4, 2017 · 7 comments

Projects

None yet

4 participants

@jensengar
jensengar commented Jan 4, 2017 edited

Bug unless there's another way to do this. Long story short, I want mongoose to record timestamps for me but I don't want them selected unless specified.

I have verified that adding a select: false to the schema elsewhere works, but not on the timestamp fields. For example if I have something like

var UserSchema = new Schema({
	email: {type: String, index: {unique: true, trim: true, index: true, sparse: true}},
	firstName: {type: String, required: true},
	lastName: {type: String, required: true},
	createdAt: {type: Date, select: false},
	updatedAt: {type: Date, select: false}
}, {timestamps: true});

my queries are still returning the createdAt and updatedAt fields. Is there a way only fetch these fields if requested?

node 6.9.1
mongoose 4.7.5
mongo 3.2.8

@varunjayaraman
Collaborator
varunjayaraman commented Jan 5, 2017 edited

That won't work because passing in { timestamps: true } as an option causes the schema to rewrite the createdAt and updatedAt fields as Date fields with no other parameters.

This is the relevant code from Schema.prototype.setupTimestamps:

    var createdAt = timestamps.createdAt || 'createdAt';
    var updatedAt = timestamps.updatedAt || 'updatedAt';
    var schemaAdditions = {};

    schemaAdditions[updatedAt] = Date;

    if (!this.paths[createdAt]) {
      schemaAdditions[createdAt] = Date;
    }

    this.add(schemaAdditions);

Allowing you to pass in an option of select: false would be a backwards breaking change because the timestamps object only takes a nested object to rewrite the field name:

{ timestamps: { createdAt: 'myNewFieldName' } }

My guess is that this change would require the option to instead be something like this, which would be backwards-breaking

{ timestamps: { createdAt: { fieldName: 'myNewFieldName', select: false } } }

The other option is to allow a check for if the createdAt or updatedAt object within the timestamps options object is a string or object, and use it accordingly, which would not be backwards breaking.

Looping in @vkarpov15 here, since this seems like a feature request more than a bug.

@jensengar

Awesome. Happy to help with anything.

@TrejGun
Contributor
TrejGun commented Jan 5, 2017

meanwhile you can use global plugin

import {omit} from "lodash";

mongoose.plugin(schema => {
	schema.set("timestamps", true); // for all models

	const transform = (schema.get("toJSON") || {transform: (doc, ret) => ret}).transform;
	schema.set("toJSON", {
		virtuals: true,
		transform(doc, ret) {
			return omit(transform(doc, ret), ["__v", "createdAt", "updatedAt", "id"]); // remove whatever you don't like on global level
		}
	});
})

mongoose.model("SomeName", new Schema({
   // some fields
}, {
	toJSON: {
		transform(doc, ret) {
			ret.created = ret.createdAt; // if you still need this info
			delete ret.someOtherField; // remove whatever you don't like on model level
			return ret;
		}
	}
}))
@jensengar

I'll do that. thanks.

@jensengar

So I see this as a partial work around. Using the plugin method works, but not when the query is done with the lean option which unfortunately is the majority of my queries probably. Is there anything else you can recommend?

@vkarpov15
Collaborator

You can just use a pre find hook to deselect those fields unless they're explicitly selected. Or you can use a post find hook to remove them after the query. Is there some reason why you can't do this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment