Skip to content

Commit

Permalink
feat(schema): add Schema#pick() function to create a new schema wit…
Browse files Browse the repository at this point in the history
…h a picked subset of the original schema's paths

Fix #8207
  • Loading branch information
vkarpov15 committed Nov 20, 2019
1 parent 23fad8f commit ec7d7d1
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 2 deletions.
4 changes: 2 additions & 2 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1341,9 +1341,9 @@ Model.syncIndexes = function syncIndexes(options, callback) {
/**
* Deletes all indexes that aren't defined in this model's schema. Used by
* `syncIndexes()`.
*
*
* The returned promise resolves to a list of the dropped indexes' names as an array
*
*
* @param {Function} [callback] optional callback
* @return {Promise|undefined} Returns `undefined` if callback is specified, returns a promise if no callback.
* @api public
Expand Down
44 changes: 44 additions & 0 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

const EventEmitter = require('events').EventEmitter;
const Kareem = require('kareem');
const MongooseError = require('./error/mongooseError');
const SchemaType = require('./schematype');
const SchemaTypeOptions = require('./options/SchemaTypeOptions');
const VirtualType = require('./virtualtype');
Expand Down Expand Up @@ -332,6 +333,49 @@ Schema.prototype.clone = function() {
return s;
};

/**
* Returns a new schema that has the picked `paths` from this schema.
*
* This method is analagous to [Lodash's `pick()` function](https://lodash.com/docs/4.17.15#pick) for Mongoose schemas.
*
* ####Example:
*
* const schema = Schema({ name: String, age: Number });
* // Creates a new schema with the same `name` path as `schema`,
* // but no `age` path.
* const newSchema = schema.pick(['name']);
*
* newSchema.path('name'); // SchemaString { ... }
* newSchema.path('age'); // undefined
*
* @param {Array} paths list of paths to pick
* @param {Object} [options] options to pass to the schema constructor. Defaults to `this.options` if not set.
* @return {Schema}
* @api public
*/

Schema.prototype.pick = function(paths, options) {
const newSchema = new Schema({}, options || this.options);
if (!Array.isArray(paths)) {
throw new MongooseError('Schema#pick() only accepts an array argument, ' +
'got "' + typeof paths + '"');
}

for (const path of paths) {
if (this.nested[path]) {
newSchema.add({ [path]: get(this.tree, path) });
} else {
const schematype = this.path(path);
if (schematype == null) {
throw new MongooseError('Path `' + path + '` is not in the schema');
}
newSchema.add({ [path]: schematype });
}
}

return newSchema;
};

/**
* Returns default options for this schema, merged with `options`.
*
Expand Down
77 changes: 77 additions & 0 deletions test/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1704,8 +1704,10 @@ describe('schema', function() {
});

it('removes a single path', function(done) {
assert.ok(this.schema.paths.a);
this.schema.remove('a');
assert.strictEqual(this.schema.path('a'), undefined);
assert.strictEqual(this.schema.paths.a, void 0);
done();
});

Expand Down Expand Up @@ -2168,4 +2170,79 @@ describe('schema', function() {

assert.equal(newSchema.path('title').options.type, String);
});

describe('pick() (gh-8207)', function() {
it('works with nested paths', function() {
const schema = Schema({
name: {
first: {
type: String,
required: true
},
last: {
type: String,
required: true
}
},
age: {
type: Number,
index: true
}
});
assert.ok(schema.path('name.first'));
assert.ok(schema.path('name.last'));

let newSchema = schema.pick(['age']);
assert.ok(!newSchema.path('name.first'));
assert.ok(newSchema.path('age'));
assert.ok(newSchema.path('age').index);

newSchema = schema.pick(['name']);
assert.ok(newSchema.path('name.first'));
assert.ok(newSchema.path('name.first').required);
assert.ok(newSchema.path('name.last'));
assert.ok(newSchema.path('name.last').required);
assert.ok(!newSchema.path('age'));

newSchema = schema.pick(['name.first']);
assert.ok(newSchema.path('name.first'));
assert.ok(newSchema.path('name.first').required);
assert.ok(!newSchema.path('name.last'));
assert.ok(!newSchema.path('age'));
});

it('with single nested paths', function() {
const schema = Schema({
name: Schema({
first: {
type: String,
required: true
},
last: {
type: String,
required: true
}
}),
age: {
type: Number,
index: true
}
});
assert.ok(schema.path('name.first'));
assert.ok(schema.path('name.last'));

let newSchema = schema.pick(['name']);
assert.ok(newSchema.path('name.first'));
assert.ok(newSchema.path('name.first').required);
assert.ok(newSchema.path('name.last'));
assert.ok(newSchema.path('name.last').required);
assert.ok(!newSchema.path('age'));

newSchema = schema.pick(['name.first']);
assert.ok(newSchema.path('name.first'));
assert.ok(newSchema.path('name.first').required);
assert.ok(!newSchema.path('name.last'));
assert.ok(!newSchema.path('age'));
});
});
});

0 comments on commit ec7d7d1

Please sign in to comment.