Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(schema): add alias() method that makes it easier to define multiple aliases for a given path #12491

Merged
merged 8 commits into from
Oct 5, 2022
92 changes: 83 additions & 9 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,50 @@ function Schema(obj, options) {
* @api private
*/
function aliasFields(schema, paths) {
paths = paths || Object.keys(schema.paths);
for (const path of paths) {
const options = get(schema.paths[path], 'options');
if (options == null) {
for (const path of Object.keys(paths)) {
let alias = null;
if (typeof paths[path] === 'string') {
alias = paths[path];
} else {
const options = get(schema.paths[path], 'options');
if (options == null) {
continue;
}

alias = options.alias;
}

if (!alias) {
continue;
}

const prop = schema.paths[path].path;
const alias = options.alias;

if (!alias) {
if (Array.isArray(alias)) {
for (const a of alias) {
if (typeof a !== 'string') {
throw new Error('Invalid value for alias option on ' + prop + ', got ' + a);
}

schema.aliases[a] = prop;

schema.
virtual(a).
get((function(p) {
return function() {
if (typeof this.get === 'function') {
return this.get(p);
}
return this[p];
};
})(prop)).
set((function(p) {
return function(v) {
return this.$set(p, v);
};
})(prop));
}

continue;
}

Expand Down Expand Up @@ -659,9 +692,50 @@ Schema.prototype.add = function add(obj, prefix) {
}
}

const addedKeys = Object.keys(obj).
map(key => prefix ? prefix + key : key);
aliasFields(this, addedKeys);
const aliasObj = Object.fromEntries(
Object.entries(obj).map(([key]) => ([prefix + key, null]))
);
aliasFields(this, aliasObj);
return this;
};

/**
* Add an alias for `path`. This means getting or setting the `alias`
* is equivalent to getting or setting the `path`.
*
* #### Example:
*
* const toySchema = new Schema({ n: String });
*
* // Make 'name' an alias for 'n'
* toySchema.alias('n', 'name');
*
* const Toy = mongoose.model('Toy', toySchema);
* const turboMan = new Toy({ n: 'Turbo Man' });
*
* turboMan.name; // 'Turbo Man'
* turboMan.n; // 'Turbo Man'
*
* turboMan.name = 'Turbo Man Action Figure';
* turboMan.n; // 'Turbo Man Action Figure'
*
* await turboMan.save(); // Saves { _id: ..., n: 'Turbo Man Action Figure' }
*
*
* @param {String} path real path to alias
* @param {String|String[]} alias the path(s) to use as an alias for `path`
* @return {Schema} the Schema instance
* @api public
*/

Schema.prototype.alias = function alias(path, alias) {
if (Array.isArray(alias)) {
for (const a of alias) {
this.alias(path, a);
}
hasezoey marked this conversation as resolved.
Show resolved Hide resolved
return this;
}
aliasFields(this, { [path]: alias });
return this;
};

Expand Down
30 changes: 30 additions & 0 deletions test/schema.alias.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,34 @@ describe('schema alias option', function() {
done();
// acquit:ignore:end
});

it('array of aliases (gh-12368)', function() {
const productSchema = new Schema({
n: {
type: String,
alias: ['name', 'product_name']
}
});

const Product = db.model('Test', productSchema);
const doc = new Product({});

doc['product_name'] = 'Turbo Man';
assert.equal(doc.n, 'Turbo Man');
assert.equal(doc.name, 'Turbo Man');
});

it('alias() method (gh-12368)', function() {
const schema = new Schema({ name: String });

schema.alias('name', 'otherName');
assert.equal(schema.aliases['otherName'], 'name');
assert.ok(schema.virtuals['otherName']);

schema.alias('name', ['name1', 'name2']);
assert.equal(schema.aliases['name1'], 'name');
assert.equal(schema.aliases['name2'], 'name');
assert.ok(schema.virtuals['name1']);
assert.ok(schema.virtuals['name2']);
});
});
6 changes: 6 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ declare module 'mongoose' {
/** Adds key path / schema type pairs to this schema. */
add(obj: SchemaDefinition<SchemaDefinitionType<EnforcedDocType>> | Schema, prefix?: string): this;

/**
* Add an alias for `path`. This means getting or setting the `alias`
* is equivalent to getting or setting the `path`.
*/
alias(path: string, alias: string | string[]): this;

/**
* Array of child schemas (from document arrays and single nested subdocs)
* and their corresponding compiled models. Each element of the array is
Expand Down
2 changes: 1 addition & 1 deletion types/schematypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ declare module 'mongoose' {
T | typeof SchemaType | Schema<any, any, any> | SchemaDefinition<T> | Function | AnyArray<Function>;

/** Defines a virtual with the given name that gets/sets this path. */
alias?: string;
alias?: string | string[];

/** Function or object describing how to validate this schematype. See [validation docs](https://mongoosejs.com/docs/validation.html). */
validate?: SchemaValidator<T> | AnyArray<SchemaValidator<T>>;
Expand Down