1 | 'use strict';
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | const get = require('./helpers/get');
|
8 | const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
|
9 | const utils = require('./utils');
|
10 |
|
11 | /*!
|
12 | * Prepare a set of path options for query population.
|
13 | *
|
14 | * @param {Query} query
|
15 | * @param {Object} options
|
16 | * @return {Array}
|
17 | */
|
18 |
|
19 | exports.preparePopulationOptions = function preparePopulationOptions(query, options) {
|
20 | const pop = utils.object.vals(query.options.populate);
|
21 |
|
22 |
|
23 | if (options.lean != null) {
|
24 | pop.
|
25 | filter(p => get(p, 'options.lean') == null).
|
26 | forEach(makeLean(options.lean));
|
27 | }
|
28 |
|
29 | return pop;
|
30 | };
|
31 |
|
32 | /*!
|
33 | * Prepare a set of path options for query population. This is the MongooseQuery
|
34 | * version
|
35 | *
|
36 | * @param {Query} query
|
37 | * @param {Object} options
|
38 | * @return {Array}
|
39 | */
|
40 |
|
41 | exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, options) {
|
42 | const pop = utils.object.vals(query._mongooseOptions.populate);
|
43 |
|
44 |
|
45 | if (options.lean != null) {
|
46 | pop.
|
47 | filter(p => get(p, 'options.lean') == null).
|
48 | forEach(makeLean(options.lean));
|
49 | }
|
50 |
|
51 | const session = get(query, 'options.session', null);
|
52 | if (session != null) {
|
53 | pop.forEach(path => {
|
54 | if (path.options == null) {
|
55 | path.options = { session: session };
|
56 | return;
|
57 | }
|
58 | if (!('session' in path.options)) {
|
59 | path.options.session = session;
|
60 | }
|
61 | });
|
62 | }
|
63 |
|
64 | const projection = query._fieldsForExec();
|
65 | pop.forEach(p => {
|
66 | p._queryProjection = projection;
|
67 | });
|
68 |
|
69 | return pop;
|
70 | };
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | function getDiscriminatorByValue(model, value) {
|
80 | let discriminator = null;
|
81 | if (!model.discriminators) {
|
82 | return discriminator;
|
83 | }
|
84 | for (const name in model.discriminators) {
|
85 | const it = model.discriminators[name];
|
86 | if (
|
87 | it.schema &&
|
88 | it.schema.discriminatorMapping &&
|
89 | it.schema.discriminatorMapping.value == value
|
90 | ) {
|
91 | discriminator = it;
|
92 | break;
|
93 | }
|
94 | }
|
95 | return discriminator;
|
96 | }
|
97 |
|
98 | exports.getDiscriminatorByValue = getDiscriminatorByValue;
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | exports.createModel = function createModel(model, doc, fields, userProvidedFields) {
|
111 | model.hooks.execPreSync('createModel', doc);
|
112 | const discriminatorMapping = model.schema ?
|
113 | model.schema.discriminatorMapping :
|
114 | null;
|
115 |
|
116 | const key = discriminatorMapping && discriminatorMapping.isRoot ?
|
117 | discriminatorMapping.key :
|
118 | null;
|
119 |
|
120 | const value = doc[key];
|
121 | if (key && value && model.discriminators) {
|
122 | const discriminator = model.discriminators[value] || getDiscriminatorByValue(model, value);
|
123 | if (discriminator) {
|
124 | const _fields = utils.clone(userProvidedFields);
|
125 | exports.applyPaths(_fields, discriminator.schema);
|
126 | return new discriminator(undefined, _fields, true);
|
127 | }
|
128 | }
|
129 |
|
130 | return new model(undefined, fields, {
|
131 | skipId: true,
|
132 | isNew: false,
|
133 | willInit: true
|
134 | });
|
135 | };
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | exports.applyPaths = function applyPaths(fields, schema) {
|
142 |
|
143 | let exclude;
|
144 | let keys;
|
145 | let ki;
|
146 | let field;
|
147 |
|
148 | if (fields) {
|
149 | keys = Object.keys(fields);
|
150 | ki = keys.length;
|
151 |
|
152 | while (ki--) {
|
153 | if (keys[ki][0] === '+') {
|
154 | continue;
|
155 | }
|
156 | field = fields[keys[ki]];
|
157 |
|
158 | if (!isDefiningProjection(field)) {
|
159 | continue;
|
160 | }
|
161 | exclude = field === 0;
|
162 | break;
|
163 | }
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 |
|
169 | const selected = [];
|
170 | const excluded = [];
|
171 | const stack = [];
|
172 |
|
173 | const analyzePath = function(path, type) {
|
174 | const plusPath = '+' + path;
|
175 | const hasPlusPath = fields && plusPath in fields;
|
176 | if (hasPlusPath) {
|
177 |
|
178 | delete fields[plusPath];
|
179 | }
|
180 |
|
181 | if (typeof type.selected !== 'boolean') return;
|
182 |
|
183 | if (hasPlusPath) {
|
184 |
|
185 | delete fields[plusPath];
|
186 |
|
187 |
|
188 |
|
189 | if (exclude === false && keys.length > 1 && !~keys.indexOf(path)) {
|
190 | fields[path] = 1;
|
191 | }
|
192 |
|
193 | return;
|
194 | }
|
195 |
|
196 |
|
197 | const pieces = path.split('.');
|
198 | const root = pieces[0];
|
199 | if (~excluded.indexOf(root)) {
|
200 | return;
|
201 | }
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | if (!exclude && get(type, 'options.$skipDiscriminatorCheck', false)) {
|
207 | let cur = '';
|
208 | for (let i = 0; i < pieces.length; ++i) {
|
209 | cur += (cur.length === 0 ? '' : '.') + pieces[i];
|
210 | const projection = get(fields, cur, false);
|
211 | if (projection && typeof projection !== 'object') {
|
212 | return;
|
213 | }
|
214 | }
|
215 | }
|
216 |
|
217 | (type.selected ? selected : excluded).push(path);
|
218 | };
|
219 |
|
220 | analyzeSchema(schema);
|
221 |
|
222 | switch (exclude) {
|
223 | case true:
|
224 | for (let i = 0; i < excluded.length; ++i) {
|
225 | fields[excluded[i]] = 0;
|
226 | }
|
227 | break;
|
228 | case false:
|
229 | if (schema &&
|
230 | schema.paths['_id'] &&
|
231 | schema.paths['_id'].options &&
|
232 | schema.paths['_id'].options.select === false) {
|
233 | fields._id = 0;
|
234 | }
|
235 | for (let i = 0; i < selected.length; ++i) {
|
236 | fields[selected[i]] = 1;
|
237 | }
|
238 | break;
|
239 | case undefined:
|
240 | if (fields == null) {
|
241 | break;
|
242 | }
|
243 |
|
244 | for (const key of Object.keys(fields || {})) {
|
245 | if (key.charAt(0) === '+') {
|
246 | delete fields[key];
|
247 | }
|
248 | }
|
249 |
|
250 |
|
251 |
|
252 | for (let i = 0; i < excluded.length; ++i) {
|
253 | fields[excluded[i]] = 0;
|
254 | }
|
255 | break;
|
256 | }
|
257 |
|
258 | function analyzeSchema(schema, prefix) {
|
259 | prefix || (prefix = '');
|
260 |
|
261 |
|
262 | if (stack.indexOf(schema) !== -1) {
|
263 | return;
|
264 | }
|
265 | stack.push(schema);
|
266 |
|
267 | schema.eachPath(function(path, type) {
|
268 | if (prefix) path = prefix + '.' + path;
|
269 |
|
270 | analyzePath(path, type);
|
271 |
|
272 |
|
273 | if (type.schema) {
|
274 | analyzeSchema(type.schema, path);
|
275 | }
|
276 | });
|
277 |
|
278 | stack.pop();
|
279 | }
|
280 | };
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 | function makeLean(val) {
|
289 | return function(option) {
|
290 | option.options || (option.options = {});
|
291 | option.options.lean = val;
|
292 | };
|
293 | }
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 | exports.handleDeleteWriteOpResult = function handleDeleteWriteOpResult(callback) {
|
300 | return function _handleDeleteWriteOpResult(error, res) {
|
301 | if (error) {
|
302 | return callback(error);
|
303 | }
|
304 | return callback(null,
|
305 | Object.assign({}, res.result, {deletedCount: res.deletedCount }));
|
306 | };
|
307 | };
|