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(mongoose): export omitUndefined() helper #14582

Merged
merged 1 commit into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions lib/helpers/omitUndefined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

module.exports = function omitUndefined(val) {
if (val == null || typeof val !== 'object') {
return val;
}
if (Array.isArray(val)) {
for (let i = val.length - 1; i >= 0; --i) {
if (val[i] === undefined) {
val.splice(i, 1);
}
}
}
for (const key of Object.keys(val)) {
if (val[key] === void 0) {
delete val[key];
}
}
return val;
};
10 changes: 2 additions & 8 deletions lib/helpers/query/cast$expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const CastError = require('../../error/cast');
const StrictModeError = require('../../error/strict');
const castNumber = require('../../cast/number');
const omitUndefined = require('../omitUndefined');

const booleanComparison = new Set(['$and', '$or']);
const comparisonOperator = new Set(['$cmp', '$eq', '$lt', '$lte', '$gt', '$gte']);
Expand Down Expand Up @@ -125,18 +126,11 @@ function _castExpression(val, schema, strictQuery) {
val.$round = $round.map(v => castNumberOperator(v, schema, strictQuery));
}

_omitUndefined(val);
omitUndefined(val);

return val;
}

function _omitUndefined(val) {
const keys = Object.keys(val);
for (let i = 0, len = keys.length; i < len; ++i) {
(val[keys[i]] === void 0) && delete val[keys[i]];
}
}

// { $op: <number> }
function castNumberOperator(val) {
if (!isLiteral(val)) {
Expand Down
22 changes: 22 additions & 0 deletions lib/mongoose.js
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,28 @@ Mongoose.prototype.skipMiddlewareFunction = Kareem.skipWrappedFunction;

Mongoose.prototype.overwriteMiddlewareResult = Kareem.overwriteResult;

/**
* Takes in an object and deletes any keys from the object whose values
* are strictly equal to `undefined`.
* This function is useful for query filters because Mongoose treats
* `TestModel.find({ name: undefined })` as `TestModel.find({ name: null })`.
*
* #### Example:
*
* const filter = { name: 'John', age: undefined, status: 'active' };
* mongoose.omitUndefined(filter); // { name: 'John', status: 'active' }
* filter; // { name: 'John', status: 'active' }
*
* await UserModel.findOne(mongoose.omitUndefined(filter));
*
* @method omitUndefined
* @param {Object} [val] the object to remove undefined keys from
* @returns {Object} the object passed in
* @api public
*/

Mongoose.prototype.omitUndefined = require('./helpers/omitUndefined');

/**
* The exports object is an instance of Mongoose.
*
Expand Down
2 changes: 2 additions & 0 deletions test/types/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ function setAsObject() {

expectError(mongoose.set({ invalid: true }));
}

const x: { name: string } = mongoose.omitUndefined({ name: 'foo' });
vkarpov15 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ declare module 'mongoose' {
/** Gets mongoose options */
export function get<K extends keyof MongooseOptions>(key: K): MongooseOptions[K];

export function omitUndefined<T extends Record<string, any>>(val: T): T;

/* ! ignore */
export type CompileModelOptions = {
overwriteModels?: boolean,
Expand Down