-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
131 lines (110 loc) · 4.77 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"use strict";
const each = require("lodash.foreach");
const get = require("lodash.get");
// Function typecheck helper
const isFunc = val => typeof val === "function";
const deepPath = function (schema, pathName) {
let path;
const paths = pathName.split(".");
if (paths.length > 1) {
pathName = paths.shift();
}
if (isFunc(schema.path)) {
path = schema.path(pathName);
}
if (path && path.schema) {
path = deepPath(path.schema, paths.join("."));
}
return path;
};
// Export the mongoose plugin
module.exports = function (schema, options) {
options = options || {};
const type = options.type || "unique";
const message = options.message || "Error, expected `{PATH}` to be unique. Value: `{VALUE}`";
// Mongoose Schema objects don't describe default _id indexes
// https://github.com/Automattic/mongoose/issues/5998
const indexes = [[{_id: 1}, {unique: true}]].concat(schema.indexes());
// Dynamically iterate all indexes
each(indexes, index => {
const indexOptions = index[1];
if (indexOptions.unique) {
const paths = Object.keys(index[0]);
//FIX cambiar este each por un forEach
each(paths, pathName => {
// Choose error message
const pathMessage = typeof indexOptions.uniqueMessage === "string" ? indexOptions.uniqueMessage : message;
// Obtain the correct path object
const path = deepPath(schema, pathName) || schema.path(pathName);
if (path) {
// Add an async validator
path.validate(
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;
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");
}
}
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 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
);
}
});
}
});
};