diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..4b66f7f --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,33 @@ +module.exports = { + root: true, + env: { + node: true, + es2022: true + }, + plugins: ["prettier"], + extends: ["plugin:prettier/recommended"], + rules: { + "no-console": 0, + "max-len": [ + "error", + { + code: 120, + ignoreComments: true + } + ], + "prettier/prettier": [ + "warn", + { + printWidth: 120, + tabWidth: 2, + bracketSpacing: false, + trailingComma: "none", + arrowParens: "avoid" + } + ] + }, + parserOptions: { + ecmaVersion: 2022, + sourceType: "module" + } +}; diff --git a/.gitignore b/.gitignore index 38a8f8e..47e8be4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ $RECYCLE.BIN/ node_modules/ .tmp npm-debug.log +Session.vim diff --git a/index.js b/index.js index e6d39f1..0076a2e 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ const get = require("lodash.get"); // Function typecheck helper const isFunc = val => typeof val === "function"; -const deepPath = function(schema, pathName) { +const deepPath = function (schema, pathName) { let path; const paths = pathName.split("."); @@ -26,7 +26,7 @@ const deepPath = function(schema, pathName) { }; // Export the mongoose plugin -module.exports = function(schema, options) { +module.exports = function (schema, options) { options = options || {}; const type = options.type || "unique"; const message = options.message || "Error, expected `{PATH}` to be unique. Value: `{VALUE}`"; @@ -51,77 +51,74 @@ module.exports = function(schema, options) { if (path) { // Add an async validator path.validate( - function() { - return new Promise(resolve => { - const isSubdocument = isFunc(this.ownerDocument) && this.ownerDocument() !== this; - const isQuery = this.constructor.name === "Query"; - const parentDoc = isSubdocument ? this.ownerDocument() : this; - const isNew = typeof parentDoc.isNew === "boolean" ? parentDoc.isNew : !isQuery; - - let conditions = {}; - each(paths, name => { - let pathValue; - - // If the doc is a query, this is a findAndUpdate - if (isQuery) { - pathValue = get(this, "_update." + name) || get(this, "_update.$set." + name); - } else { - pathValue = get(this, isSubdocument ? name.split(".").pop() : name); - } - - // Wrap with case-insensitivity - if (get(path, "options.uniqueCaseInsensitive") || indexOptions.uniqueCaseInsensitive) { - //no escapar si es un arreglo - if(!Array.isArray(pathValue)){ - // Escape RegExp chars - pathValue = pathValue.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - pathValue = new RegExp("^" + pathValue + "$", "i"); - } - } + async function () { + const isSubdocument = isFunc(this.ownerDocument) && this.ownerDocument() !== this; + const isQuery = this.constructor.name === "Query"; + const parentDoc = isSubdocument ? this.ownerDocument() : this; + const isNew = typeof parentDoc.isNew === "boolean" ? parentDoc.isNew : !isQuery; - conditions[name] = pathValue; - }); - - if (!isNew) { - // Use conditions the user has with find*AndUpdate - if (isQuery) { - each(this._conditions, (value, key) => { - conditions[key] = {$ne: value}; - }); - } else if (pathName !== "_id") { - // if it's not new then it always has _id - conditions._id = {$ne: this._id}; - } else { - // if is not new and is not query and the pathName is _id then is the same document no need to check anything - return resolve(true); - } - } + const conditions = {}; + each(paths, name => { + let pathValue; - // Obtain the model depending on context - // https://github.com/Automattic/mongoose/issues/3430 - // https://github.com/Automattic/mongoose/issues/3589 - let model; + // If the doc is a query, this is a findAndUpdate if (isQuery) { - model = this.model; - } else if (isSubdocument) { - model = this.ownerDocument().model(this.ownerDocument().constructor.modelName); - } else if (this.constructor.modelName) { - // if the constructor has modelName then the constructor is the model - model = this.constructor; + pathValue = get(this, "_update." + name) || get(this, "_update.$set." + name); + } else { + pathValue = get(this, isSubdocument ? name.split(".").pop() : name); } - // Is this model a discriminator and the unique index is on the whole collection, - // not just the instances of the discriminator? If so, use the base model to query. - // https://github.com/Automattic/mongoose/issues/4965 - if (model.baseModelName && indexOptions.partialFilterExpression === null) { - model = model.db.model(model.baseModelName); + // Wrap with case-insensitivity + if (get(path, "options.uniqueCaseInsensitive") || indexOptions.uniqueCaseInsensitive) { + //no escapar si es un arreglo + if (!Array.isArray(pathValue)) { + // Escape RegExp chars + pathValue = pathValue.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + pathValue = new RegExp("^" + pathValue + "$", "i"); + } } - conditions = {$and: [conditions, indexOptions.partialFilterExpression || {}]}; - model.find(conditions).countDocuments((err, count) => { - resolve(count === 0); - }); + conditions[name] = pathValue; }); + + if (!isNew) { + // Use conditions the user has with find*AndUpdate + if (isQuery) { + each(this._conditions, (value, key) => { + conditions[key] = {$ne: value}; + }); + } else if (pathName !== "_id") { + // if it's not new then it always has _id + conditions._id = {$ne: this._id}; + } else { + // if is not new and is not query and the pathName is _id then is the same document no need to check anything + return resolve(true); + } + } + + // Obtain the model depending on context + // https://github.com/Automattic/mongoose/issues/3430 + // https://github.com/Automattic/mongoose/issues/3589 + let model; + if (isQuery) { + model = this.model; + } else if (isSubdocument) { + model = this.ownerDocument().model(this.ownerDocument().constructor.modelName); + } else if (this.constructor.modelName) { + // if the constructor has modelName then the constructor is the model + model = this.constructor; + } + + // Is this model a discriminator and the unique index is on the whole collection, + // not just the instances of the discriminator? If so, use the base model to query. + // https://github.com/Automattic/mongoose/issues/4965 + if (model.baseModelName && indexOptions.partialFilterExpression === null) { + model = model.db.model(model.baseModelName); + } + conditions = {$and: [conditions, indexOptions.partialFilterExpression || {}]}; + + const count = await model.find(conditions).countDocuments(); + return count === 0; }, pathMessage, type diff --git a/package.json b/package.json index a5f58eb..972fb45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mongoose-unique-validator", - "version": "2.1.3", + "version": "2.1.4", "description": "mongoose-unique-validator is a plugin which adds pre-save validation for unique fields within a Mongoose schema.", "main": "index.js", "scripts": {