alchemymvc 1.3.22 → 1.4.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/lib/app/behaviour/publishable_behaviour.js +5 -5
  4. package/lib/app/behaviour/revision_behaviour.js +96 -59
  5. package/lib/app/behaviour/sluggable_behaviour.js +14 -14
  6. package/lib/app/conduit/electron_conduit.js +9 -9
  7. package/lib/app/conduit/http_conduit.js +13 -13
  8. package/lib/app/conduit/loopback_conduit.js +15 -15
  9. package/lib/app/conduit/socket_conduit.js +43 -43
  10. package/lib/app/config/routes.js +26 -0
  11. package/lib/app/controller/00-default_app_controller.js +21 -0
  12. package/lib/app/controller/alchemy_info_controller.js +12 -12
  13. package/lib/app/datasource/mongo_datasource.js +27 -18
  14. package/lib/app/element/00-default_app_element.js +19 -0
  15. package/lib/app/element/time_ago.js +5 -5
  16. package/lib/app/helper/00-default_app_helper.js +11 -0
  17. package/lib/app/helper/alchemy_helper.js +22 -22
  18. package/lib/app/helper/backed_map.js +1 -1
  19. package/lib/app/helper/breadcrumb.js +10 -10
  20. package/lib/app/helper/client_collection.js +3 -3
  21. package/lib/app/helper/cron.js +29 -29
  22. package/lib/app/helper/enum_values.js +6 -6
  23. package/lib/app/helper/pagination_helper.js +36 -36
  24. package/lib/app/helper/router_helper.js +35 -35
  25. package/lib/app/helper/socket_helper.js +57 -57
  26. package/lib/app/helper/syncable.js +62 -62
  27. package/lib/app/helper_component/paginate_component.js +9 -9
  28. package/lib/app/helper_controller/component.js +1 -1
  29. package/lib/app/helper_controller/conduit.js +31 -31
  30. package/lib/app/helper_controller/controller.js +54 -39
  31. package/lib/app/helper_datasource/00-nosql_datasource.js +624 -70
  32. package/lib/app/helper_datasource/05-fallback_datasource.js +10 -10
  33. package/lib/app/helper_datasource/idb_datasource.js +6 -6
  34. package/lib/app/helper_datasource/indexed_db.js +22 -22
  35. package/lib/app/helper_datasource/remote_datasource.js +5 -5
  36. package/lib/app/helper_error/http_error.js +4 -4
  37. package/lib/app/helper_error/model_error.js +2 -2
  38. package/lib/app/helper_error/validation_error.js +12 -12
  39. package/lib/app/helper_field/00-objectid_field.js +7 -7
  40. package/lib/app/helper_field/05-string_field.js +16 -12
  41. package/lib/app/helper_field/06-text_field.js +2 -4
  42. package/lib/app/helper_field/10-number_field.js +9 -12
  43. package/lib/app/helper_field/11-date_field.js +15 -15
  44. package/lib/app/helper_field/15-local_temporal_field.js +10 -10
  45. package/lib/app/helper_field/20-decimal_field.js +8 -9
  46. package/lib/app/helper_field/belongsto_field.js +1 -1
  47. package/lib/app/helper_field/big_int_field.js +8 -8
  48. package/lib/app/helper_field/boolean_field.js +9 -11
  49. package/lib/app/helper_field/datetime_field.js +3 -3
  50. package/lib/app/helper_field/enum_field.js +13 -8
  51. package/lib/app/helper_field/fixed_decimal_field.js +6 -7
  52. package/lib/app/helper_field/geopoint_field.js +9 -10
  53. package/lib/app/helper_field/habtm_field.js +3 -3
  54. package/lib/app/helper_field/hasoneparent_field.js +1 -1
  55. package/lib/app/helper_field/html_field.js +2 -4
  56. package/lib/app/helper_field/integer_field.js +8 -11
  57. package/lib/app/helper_field/local_date_field.js +5 -5
  58. package/lib/app/helper_field/local_date_time_field.js +5 -5
  59. package/lib/app/helper_field/local_time_field.js +5 -5
  60. package/lib/app/helper_field/mixed_field.js +5 -5
  61. package/lib/app/helper_field/object_field.js +6 -6
  62. package/lib/app/helper_field/password_field.js +3 -3
  63. package/lib/app/helper_field/regexp_field.js +7 -9
  64. package/lib/app/helper_field/schema_field.js +91 -88
  65. package/lib/app/helper_field/settings_field.js +92 -0
  66. package/lib/app/helper_field/time_field.js +6 -6
  67. package/lib/app/helper_field/url_field.js +2 -4
  68. package/lib/app/helper_model/00-base_criteria.js +662 -0
  69. package/lib/app/helper_model/05-criteria_expressions.js +605 -0
  70. package/lib/app/helper_model/10-model_criteria.js +1182 -0
  71. package/lib/app/helper_model/data_provider.js +2 -2
  72. package/lib/app/helper_model/document.js +103 -92
  73. package/lib/app/helper_model/document_list.js +14 -14
  74. package/lib/app/helper_model/field_config.js +11 -11
  75. package/lib/app/helper_model/field_set.js +17 -17
  76. package/lib/app/helper_model/model.js +201 -123
  77. package/lib/app/helper_model/remote_data_provider.js +2 -2
  78. package/lib/app/helper_validator/00_validator.js +16 -16
  79. package/lib/app/helper_validator/not_empty_validator.js +9 -9
  80. package/lib/app/model/00-default_app_model.js +18 -0
  81. package/lib/app/model/05-system_model.js +27 -0
  82. package/lib/app/model/{alchemy_migration_model.js → system_migration_model.js} +4 -4
  83. package/lib/app/model/system_setting_model.js +154 -0
  84. package/lib/app/model/{alchemy_task_history_model.js → system_task_history_model.js} +7 -7
  85. package/lib/app/model/{alchemy_task_model.js → system_task_model.js} +11 -11
  86. package/lib/bootstrap.js +22 -312
  87. package/lib/class/accumulator.js +5 -5
  88. package/lib/class/behaviour.js +5 -5
  89. package/lib/class/component.js +3 -3
  90. package/lib/class/conduit.js +152 -150
  91. package/lib/class/controller.js +42 -42
  92. package/lib/class/datasource.js +74 -79
  93. package/lib/class/document.js +74 -95
  94. package/lib/class/document_list.js +5 -5
  95. package/lib/class/element.js +17 -17
  96. package/lib/class/error.js +3 -3
  97. package/lib/class/field.js +169 -91
  98. package/lib/class/field_value.js +6 -6
  99. package/lib/class/helper.js +3 -3
  100. package/lib/class/inode.js +17 -17
  101. package/lib/class/inode_dir.js +12 -12
  102. package/lib/class/inode_file.js +26 -26
  103. package/lib/class/inode_list.js +4 -4
  104. package/lib/class/migration.js +4 -4
  105. package/lib/class/model.js +146 -165
  106. package/lib/class/path_definition.js +22 -22
  107. package/lib/class/path_evaluator.js +5 -5
  108. package/lib/class/path_param_definition.js +7 -7
  109. package/lib/class/plugin.js +312 -0
  110. package/lib/class/postponement.js +29 -29
  111. package/lib/class/reciprocal.js +8 -8
  112. package/lib/class/route.js +33 -33
  113. package/lib/class/router.js +73 -73
  114. package/lib/class/schema.js +21 -21
  115. package/lib/class/schema_client.js +105 -82
  116. package/lib/class/session.js +63 -29
  117. package/lib/class/session_scene.js +4 -4
  118. package/lib/class/sitemap.js +16 -16
  119. package/lib/class/task.js +39 -39
  120. package/lib/class/task_service.js +43 -47
  121. package/lib/{init → core}/alchemy.js +411 -373
  122. package/lib/{init/functions.js → core/alchemy_functions.js} +113 -91
  123. package/lib/core/alchemy_load_functions.js +715 -0
  124. package/lib/core/base.js +50 -62
  125. package/lib/core/client_alchemy.js +144 -152
  126. package/lib/core/client_base.js +27 -50
  127. package/lib/core/discovery.js +16 -18
  128. package/lib/core/middleware.js +54 -43
  129. package/lib/core/{routing.js → prefix.js} +14 -16
  130. package/lib/core/setting.js +1684 -0
  131. package/lib/core/stage.js +758 -0
  132. package/lib/scripts/create_constants.js +119 -0
  133. package/lib/{init/languages.js → scripts/create_languages.js} +5 -5
  134. package/lib/scripts/create_settings.js +449 -0
  135. package/lib/scripts/create_shared_constants.js +95 -0
  136. package/lib/scripts/create_stages.js +55 -0
  137. package/lib/scripts/init_alchemy.js +51 -0
  138. package/lib/{init/requirements.js → scripts/preload_modules.js} +15 -2
  139. package/lib/scripts/setup_devwatch.js +238 -0
  140. package/lib/stages/00-load_core.js +342 -0
  141. package/lib/stages/05-load_app.js +57 -0
  142. package/lib/stages/10-datasource.js +61 -0
  143. package/lib/stages/15-tasks.js +27 -0
  144. package/lib/stages/20-settings.js +68 -0
  145. package/lib/stages/50-routes.js +218 -0
  146. package/lib/stages/90-server.js +347 -0
  147. package/package.json +5 -7
  148. package/lib/app/helper_model/criteria.js +0 -2294
  149. package/lib/app/helper_model/db_query.js +0 -1488
  150. package/lib/app/routes.js +0 -11
  151. package/lib/core/socket.js +0 -171
  152. package/lib/init/constants.js +0 -158
  153. package/lib/init/devwatch.js +0 -238
  154. package/lib/init/load_functions.js +0 -979
  155. package/lib/stages.js +0 -515
@@ -1,1488 +0,0 @@
1
- /**
2
- * The DbQuery class
3
- *
4
- * @author Jelle De Loecker <jelle@develry.be>
5
- * @since 0.0.1
6
- * @version 1.0.7
7
- *
8
- * @param {Model} model The originating model
9
- * @param {Object} options The find options
10
- * @param {Number} level The current level (associated model)
11
- */
12
- var DbQuery = Function.inherits('Alchemy.Base', function DbQuery(model, options, level) {
13
-
14
- // The originating model
15
- this.model = model;
16
-
17
- // The query options
18
- this.options = options;
19
-
20
- // The fields to return
21
- this.fields = false;
22
-
23
- // The fields we need to query
24
- this.queryFields = false;
25
-
26
- // Sort options
27
- this.sort = null;
28
-
29
- // Possible errors
30
- this.error = false;
31
-
32
- // Store the current recursive level
33
- this.level = level || 0;
34
-
35
- // Should we debug?
36
- this.debug = !!options.debug;
37
-
38
- // Store how deep we can go
39
- this.recursive = options.recursive;
40
-
41
- // Are there translation specific conditions?
42
- this.translation_conditions = [];
43
-
44
- if (options.page && options.limit) {
45
- options.offset = (options.page - 1) * options.limit;
46
- }
47
-
48
- this.processFields();
49
- this.processSort();
50
- this.processContain();
51
- });
52
-
53
- /**
54
- * All comparison functions
55
- *
56
- * @author Jelle De Loecker <jelle@develry.be>
57
- * @since 1.0.0
58
- * @version 1.0.0
59
- */
60
- DbQuery.setStatic('comparisons', {});
61
-
62
- /**
63
- * All logical operator functions
64
- *
65
- * @author Jelle De Loecker <jelle@develry.be>
66
- * @since 1.0.0
67
- * @version 1.0.0
68
- */
69
- DbQuery.setStatic('logical_operators', {});
70
-
71
- /**
72
- * Add comparison function
73
- *
74
- * @author Jelle De Loecker <jelle@develry.be>
75
- * @since 1.0.0
76
- * @version 1.0.0
77
- */
78
- DbQuery.setStatic(function addComparison(fnc) {
79
- this.comparisons[fnc.name] = fnc;
80
- });
81
-
82
- /**
83
- * Add logical operator function
84
- *
85
- * @author Jelle De Loecker <jelle@develry.be>
86
- * @since 1.0.0
87
- * @version 1.0.0
88
- */
89
- DbQuery.setStatic(function addLogicalOperator(fnc) {
90
- this.logical_operators[fnc.name] = fnc;
91
- });
92
-
93
- /**
94
- * Lower than comparison
95
- *
96
- * @author Jelle De Loecker <jelle@develry.be>
97
- * @since 1.0.0
98
- * @version 1.0.0
99
- */
100
- DbQuery.addComparison(function $lt(a, b) {
101
- return DbQuery.areComparable(a, b) && a < b;
102
- });
103
-
104
- /**
105
- * Lower than or equal comparison
106
- *
107
- * @author Jelle De Loecker <jelle@develry.be>
108
- * @since 1.0.0
109
- * @version 1.0.0
110
- */
111
- DbQuery.addComparison(function $lte(a, b) {
112
- return DbQuery.areComparable(a, b) && a <= b;
113
- });
114
-
115
- /**
116
- * Greater than comparison
117
- *
118
- * @author Jelle De Loecker <jelle@develry.be>
119
- * @since 1.0.0
120
- * @version 1.0.0
121
- */
122
- DbQuery.addComparison(function $gt(a, b) {
123
- return DbQuery.areComparable(a, b) && a > b;
124
- });
125
-
126
- /**
127
- * Greater than or equal comparison
128
- *
129
- * @author Jelle De Loecker <jelle@develry.be>
130
- * @since 1.0.0
131
- * @version 1.0.0
132
- */
133
- DbQuery.addComparison(function $gte(a, b) {
134
- return DbQuery.areComparable(a, b) && a >= b;
135
- });
136
-
137
- /**
138
- * Not equal comparison
139
- *
140
- * @author Jelle De Loecker <jelle@develry.be>
141
- * @since 1.0.0
142
- * @version 1.0.0
143
- */
144
- DbQuery.addComparison(function $ne(a, b) {
145
-
146
- if (a === undefined) {
147
- return true;
148
- }
149
-
150
- return !Object.alike(a, b);
151
- });
152
-
153
- /**
154
- * In comparison
155
- *
156
- * @author Jelle De Loecker <jelle@develry.be>
157
- * @since 1.0.0
158
- * @version 1.0.0
159
- */
160
- DbQuery.addComparison(function $in(a, b) {
161
-
162
- var i;
163
-
164
- if (!Array.isArray(b)) {
165
- throw new Error("$in operator called with a non-array");
166
- }
167
-
168
- for (i = 0; i < b.length; i++) {
169
- if (Object.alike(a, b[i])) {
170
- return true;
171
- }
172
- }
173
-
174
- return false;
175
- });
176
-
177
- /**
178
- * Not in comparison
179
- *
180
- * @author Jelle De Loecker <jelle@develry.be>
181
- * @since 1.0.0
182
- * @version 1.0.0
183
- */
184
- DbQuery.addComparison(function $nin(a, b) {
185
- return !this.$in(a, b);
186
- });
187
-
188
- /**
189
- * Regex comparison
190
- *
191
- * @author Jelle De Loecker <jelle@develry.be>
192
- * @since 1.0.0
193
- * @version 1.0.0
194
- */
195
- DbQuery.addComparison(function $regex(a, b) {
196
-
197
- if (!RegExp.isRegExp(b)) {
198
- throw new Error('$regex operator called with non regular expression');
199
- }
200
-
201
- if (typeof a != 'string') {
202
- return false;
203
- }
204
-
205
- return b.test(a);
206
- });
207
-
208
- /**
209
- * Exists comparison
210
- *
211
- * @author Jelle De Loecker <jelle@develry.be>
212
- * @since 1.0.0
213
- * @version 1.0.0
214
- */
215
- DbQuery.addComparison(function $exists(value, exists) {
216
-
217
- // The value needs to be truthy or an empty string
218
- // (This is how mongo does it, so 0 or false returns falsy)
219
- exists = !!(exists || exists === '');
220
-
221
- if (value === undefined) {
222
- return !exists;
223
- } else {
224
- return exists;
225
- }
226
- });
227
-
228
- /**
229
- * Array size comparison
230
- *
231
- * @author Jelle De Loecker <jelle@develry.be>
232
- * @since 1.0.0
233
- * @version 1.0.0
234
- */
235
- DbQuery.addComparison(function $size(obj, value) {
236
-
237
- if (!Array.isArray(obj)) {
238
- return false;
239
- }
240
-
241
- if (value % 1 !== 0) {
242
- throw new Error('$size operator called without an integer');
243
- }
244
-
245
- return obj.length == value;
246
- });
247
-
248
- /**
249
- * Array elemMatch comparison
250
- *
251
- * @author Jelle De Loecker <jelle@develry.be>
252
- * @since 1.0.0
253
- * @version 1.0.0
254
- */
255
- DbQuery.addComparison(function $elemMatch(obj, value) {
256
-
257
- var i;
258
-
259
- if (!Array.isArray(obj)) {
260
- return result;
261
- }
262
-
263
- while (i--) {
264
- if (DbQuery.match(obj[i], value)) {
265
- return true;
266
- }
267
- }
268
-
269
- return false;
270
- });
271
-
272
- /**
273
- * Match any of the subqueries
274
- *
275
- * @author Jelle De Loecker <jelle@develry.be>
276
- * @since 1.0.0
277
- * @version 1.0.0
278
- */
279
- DbQuery.addLogicalOperator(function $or(obj, query) {
280
-
281
- var i;
282
-
283
- if (!Array.isArray(query)) {
284
- throw new Error('$or operator used without an array');
285
- }
286
-
287
- for (i = 0; i < query.length; i++) {
288
- if (DbQuery.match(obj, query[i])) {
289
- return true;
290
- }
291
- }
292
-
293
- return false;
294
- });
295
-
296
- /**
297
- * Match all of the subqueries
298
- *
299
- * @author Jelle De Loecker <jelle@develry.be>
300
- * @since 1.0.0
301
- * @version 1.0.0
302
- */
303
- DbQuery.addLogicalOperator(function $and(obj, query) {
304
-
305
- var i;
306
-
307
- if (!Array.isArray(query)) {
308
- throw new Error('$and operator used without an array');
309
- }
310
-
311
- for (i = 0; i < query.length; i++) {
312
- if (!DbQuery.match(obj, query[i])) {
313
- return false;
314
- }
315
- }
316
-
317
- return true;
318
- });
319
-
320
- /**
321
- * Inverted match of the query
322
- *
323
- * @author Jelle De Loecker <jelle@develry.be>
324
- * @since 1.0.0
325
- * @version 1.0.0
326
- */
327
- DbQuery.addLogicalOperator(function $not(obj, query) {
328
- return !DbQuery.match(obj, query);
329
- });
330
-
331
- /**
332
- * Use a function to match
333
- *
334
- * @author Jelle De Loecker <jelle@develry.be>
335
- * @since 1.0.0
336
- * @version 1.0.0
337
- */
338
- DbQuery.addLogicalOperator(function $where(obj, fnc) {
339
-
340
- var result;
341
-
342
- if (typeof fnc != 'function') {
343
- throw new Error('$where operator used without a function');
344
- }
345
-
346
- result = fnc.call(obj);
347
-
348
- if (typeof result != 'boolean') {
349
- throw new Error('$where function must return boolean');
350
- }
351
-
352
- return result;
353
- });
354
-
355
- /**
356
- * Match an object against a specific {key: value} part of a query.
357
- * If the treatObjAsValue flag is set,
358
- * don't try to match every part separately, but the array as a whole.
359
- *
360
- * @author Jelle De Loecker <jelle@develry.be>
361
- * @since 1.0.0
362
- * @version 1.0.6
363
- *
364
- * @param {Object} obj
365
- * @param {Object} query
366
- *
367
- * @return {Boolean}
368
- */
369
- DbQuery.setStatic(function matchQueryPart(obj, query_key, query_value, treat_obj_as_value) {
370
-
371
- var dollar_first_chars,
372
- first_chars,
373
- obj_value,
374
- keys,
375
- i;
376
-
377
- obj_value = DbQuery.getDotValue(obj, query_key);
378
-
379
- // @TODO: On the client side, in indexdb, dates are dried.
380
- // It's stupid to undry the entire object just to check a date,
381
- // so just undry a piece of it
382
- if (Blast.isBrowser && obj_value && typeof obj_value == 'object' && obj_value.dry) {
383
- obj_value = Blast.Bound.JSON.undry(obj_value);
384
- }
385
-
386
- // Check if the value is an array if we don't force a treatment as value
387
- if (Array.isArray(obj_value) && !treat_obj_as_value) {
388
- // If the queryValue is an array, try to perform an exact match
389
- if (Array.isArray(query_value)) {
390
- return matchQueryPart(obj, query_key, query_value, true);
391
- }
392
-
393
- // Check if we are using an array-specific comparison function
394
- if (query_value !== null && typeof query_value === 'object' && !RegExp.isRegExp(query_value)) {
395
- keys = Object.keys(query_value);
396
-
397
- for (i = 0; i < keys.length; i++) {
398
- if (keys[i] == '$size' || keys[i] == '$elemMatch') {
399
- return matchQueryPart(obj, query_key, query_value, true);
400
- }
401
- }
402
- }
403
-
404
- // If not, treat it as an array of { obj, query } where there needs to be at least one match
405
- for (i = 0; i < obj_value.length; i++) {
406
- if (matchQueryPart({k: obj_value[i]}, 'k', query_value)) {
407
- return true;
408
- }
409
- }
410
-
411
- return false;
412
- }
413
-
414
- // query_value is an actual object. Determine whether it
415
- // contains comparison operators or only normal fields.
416
- // Mixed objects are not allowed
417
- if (query_value !== null && typeof query_value === 'object' && !RegExp.isRegExp(query_value) && !Array.isArray(query_value)) {
418
- keys = Object.keys(query_value);
419
-
420
- first_chars = keys.map(function eachItem(item) {
421
- return item[0];
422
- });
423
-
424
- dollar_first_chars = first_chars.filter(function eachChar(c) {
425
- return c === '$';
426
- });
427
-
428
- if (dollar_first_chars.length !== 0 && dollar_first_chars.length !== first_chars.length) {
429
- throw new Error('You cannot mix operators and normal fields');
430
- }
431
-
432
- // query_value is an object of this form:
433
- // {$comparison_operator_1: value_1, ...}
434
- if (dollar_first_chars.length > 0) {
435
- for (i = 0; i < keys.length; i++) {
436
- if (!DbQuery.comparisons[keys[i]]) {
437
- throw new Error('Unknown comparison function ' + keys[i]);
438
- }
439
-
440
- if (!DbQuery.comparisons[keys[i]](obj_value, query_value[keys[i]])) {
441
- return false;
442
- }
443
- }
444
-
445
- return true;
446
- }
447
- }
448
-
449
- // Using regular expressions with basic querying
450
- if (RegExp.isRegExp(query_value)) {
451
- return DbQuery.comparisons.$regex(obj_value, query_value);
452
- }
453
-
454
- // query_value is either anative value or a normal object
455
- // Basic matching is possible
456
- if (!Object.alike(obj_value, query_value)) {
457
- return false;
458
- }
459
-
460
- return true;
461
- });
462
-
463
- /**
464
- * Tell if a given document matches a query
465
- *
466
- * @author Jelle De Loecker <jelle@develry.be>
467
- * @since 1.0.0
468
- * @version 1.0.0
469
- *
470
- * @param {Object} obj
471
- * @param {Object} query
472
- *
473
- * @return {Boolean}
474
- */
475
- DbQuery.setStatic(function match(obj, query) {
476
-
477
- var query_value,
478
- query_keys,
479
- query_key,
480
- i;
481
-
482
- // Primitive queyr against a primitive type
483
- if (Object.isPrimitive(obj) || Object.isPrimitive(query)) {
484
- return this.matchQueryPart({need_a_key: obj}, 'need_a_key', query);
485
- }
486
-
487
- // Normal query
488
- query_keys = Object.keys(query);
489
-
490
- for (i = 0; i < query_keys.length; i++) {
491
- query_key = query_keys[i];
492
- query_value = query[query_key];
493
-
494
- if (query_key[0] == '$') {
495
- if (!this.logical_operators[query_key]) {
496
- throw new Error('Unknown logical operator ' + query_key);
497
- }
498
-
499
- if (!this.logical_operators[query_key](obj, query_value)) {
500
- return false;
501
- }
502
- } else {
503
- if (!this.matchQueryPart(obj, query_key, query_value)) {
504
- return false;
505
- }
506
- }
507
- }
508
-
509
- return true;
510
- });
511
-
512
- /**
513
- * Parse a record path
514
- *
515
- * @author Jelle De Loecker <jelle@develry.be>
516
- * @since 1.0.0
517
- * @version 1.0.0
518
- *
519
- * @param {String} path
520
- *
521
- * @return {Object}
522
- */
523
- DbQuery.setStatic(function parseRecordPath(path) {
524
-
525
- var result = {},
526
- first;
527
-
528
- // Cast to an array of path pieces
529
- if (!Array.isArray(path)) {
530
- path = path.split('.');
531
- }
532
-
533
- // Get the first character of the (possible) alias
534
- first = path[0][0];
535
-
536
- // Check if it's a valid uppercase character
537
- if (first === first.toUpperCase() && first !== first.toLowerCase()) {
538
- result.alias = path.shift();
539
- }
540
-
541
- result.path = path;
542
- result.field = path[0];
543
-
544
- return result;
545
- });
546
-
547
- /**
548
- * Get a value from object with dot notation
549
- *
550
- * @author Jelle De Loecker <jelle@develry.be>
551
- * @since 1.0.0
552
- * @version 1.0.0
553
- *
554
- * @return {Object}
555
- */
556
- DbQuery.setStatic(function getDotValue(obj, path) {
557
-
558
- var pieces,
559
- first,
560
- objs,
561
- i;
562
-
563
- if (Array.isArray(path)) {
564
- pieces = path;
565
- } else {
566
- pieces = path.split('.');
567
- }
568
-
569
- if (!obj) {
570
- return undefined;
571
- }
572
-
573
- if (pieces.length == 0) {
574
- return obj;
575
- }
576
-
577
- first = pieces[0];
578
-
579
- if (pieces.length == 1) {
580
- return obj[first];
581
- }
582
-
583
- if (Array.isArray(obj[first])) {
584
- i = parseInt(pieces[1], 10);
585
-
586
- if (typeof i === 'number' && !isNaN(i)) {
587
- return getDotValue(obj[first][i], pieces.slice(2));
588
- }
589
-
590
- // Return the array of values
591
- objs = [];
592
-
593
- for (i = 0; i < obj[first].length; i++) {
594
- objs.push(getDotValue(obj[first][i], pieces.slice(1)));
595
- }
596
-
597
- return objs;
598
- }
599
-
600
- return getDotValue(obj[first], pieces.slice(1));
601
- });
602
-
603
- /**
604
- * Are 2 values comparable
605
- *
606
- * @author Jelle De Loecker <jelle@develry.be>
607
- * @since 1.0.0
608
- * @version 1.0.0
609
- *
610
- * @return {Boolean}
611
- */
612
- DbQuery.setStatic(function areComparable(a, b) {
613
-
614
- var at = typeof a,
615
- bt = typeof b;
616
-
617
- if (at != bt) {
618
- return false;
619
- }
620
-
621
- if (at != 'string' && at != 'number' && !Date.isDate(a) &&
622
- bt != 'string' && bt != 'number' && !Date.isDate(b)) {
623
- return false;
624
- }
625
-
626
- return true;
627
- });
628
-
629
- /**
630
- * Get a list of prefixes
631
- *
632
- * @author Jelle De Loecker <jelle@develry.be>
633
- * @since 1.0.0
634
- * @version 1.0.0
635
- */
636
- DbQuery.setMethod(function getPrefixes() {
637
-
638
- var result;
639
-
640
- if (this.model && this.model.render) {
641
- result = [this.model.render.prefix].concat(this.model.render.fallback);
642
- } else {
643
- if (typeof Prefix != 'undefined') {
644
- result = Prefix.getPrefixList();
645
- }
646
- }
647
-
648
- return result;
649
- });
650
-
651
- /**
652
- * Get a checksum of this object
653
- *
654
- * @author Jelle De Loecker <jelle@develry.be>
655
- * @since 0.1.0
656
- * @version 1.1.5
657
- */
658
- DbQuery.setMethod(function toChecksum() {
659
- return alchemy.checksum([
660
- this.model.name,
661
- this.options.conditions,
662
- this.options.contain,
663
- this.options.limit,
664
- this.options.offset,
665
- this.options.available,
666
- this.fields,
667
- this.sort,
668
- this.recursive
669
- ]);
670
- });
671
-
672
- /**
673
- * Process containable instructions
674
- *
675
- * @author Jelle De Loecker <jelle@develry.be>
676
- * @since 0.1.0
677
- * @version 1.0.0
678
- */
679
- DbQuery.setMethod(function processContain() {
680
-
681
- // Don't process anything if no contain options are given
682
- if (Object.isEmpty(this.options.contain)) {
683
- this.contain = false;
684
- return;
685
- }
686
-
687
- this.contain = Object.objectify(this.options.contain, true, true);
688
- });
689
-
690
- /**
691
- * Normalize the conditions by fetching associated data
692
- *
693
- * @author Jelle De Loecker <jelle@develry.be>
694
- * @since 0.1.0
695
- * @version 0.3.3
696
- */
697
- DbQuery.setMethod(function normalize(callback) {
698
-
699
- var that = this,
700
- tasks = [],
701
- normalized = {},
702
- divided;
703
-
704
- if (this.debug) {
705
- console.log('Normalizing conditions', this.options.conditions);
706
- }
707
-
708
- // Turn the root $and object into an array
709
- divided = Object.divide(this.options.conditions);
710
-
711
- this.normalizeGroup('$and', normalized, divided, function doneRootGroup(err) {
712
-
713
- if (err) {
714
- return callback(err);
715
- }
716
-
717
- if (that.debug) {
718
- console.log('Normalized conditions are:', normalized);
719
- }
720
-
721
- callback(null, normalized);
722
- });
723
- });
724
-
725
- /**
726
- * Normalize the given group
727
- *
728
- * @author Jelle De Loecker <jelle@develry.be>
729
- * @since 0.1.0
730
- * @version 1.0.6
731
- *
732
- * @param {String} type The type of group ($and, $or, $not)
733
- * @param {Object} target An object where the compiled conditions go
734
- * @param {Object} conditions The conditions to compile
735
- * @param {Function} callback
736
- */
737
- DbQuery.setMethod(function normalizeGroup(type, target, conditions, callback) {
738
-
739
- var that = this,
740
- tasks = [],
741
- collections = {},
742
- subgroups = {};
743
-
744
- function doCallback(err) {
745
- if (err) {
746
- Blast.nextTick(function callbackWithError() {
747
- callback(err);
748
- });
749
- } else {
750
- Blast.nextTick(callback);
751
- }
752
- }
753
-
754
- // Handle array of conditions
755
- if (Array.isArray(conditions)) {
756
-
757
- conditions.forEach(function eachCondition(obj, index) {
758
- tasks[tasks.length] = function normalizeTask(next) {
759
-
760
- var temp = {};
761
-
762
- that.normalizeGroup('$and', temp, obj, function normalized$AndGroup() {
763
- next(null, temp);
764
- });
765
- };
766
- });
767
-
768
- Function.parallel(false, tasks, function normalizedConditions(err, arr) {
769
-
770
- if (err) {
771
- return doCallback(err);
772
- }
773
-
774
- if (!target[type]) {
775
- target[type] = [];
776
- }
777
-
778
- target[type] = target[type].concat(arr);
779
-
780
- // Make sure every $or and $and group is a non-empty array
781
- if (Array.isArray(target.$or) && target.$or.length == 0) {
782
- delete target.$or;
783
- }
784
-
785
- if (Array.isArray(target.$and) && target.$and.length == 0) {
786
- delete target.$and;
787
- }
788
-
789
- doCallback();
790
- });
791
-
792
- return;
793
- }
794
-
795
- // Group by collections first
796
- Object.each(conditions, function eachCondition(value, fieldPath) {
797
-
798
- var fieldConfig,
799
- fieldPaths,
800
- prefixes,
801
- temp,
802
- obj,
803
- i;
804
-
805
- // See if it's a subgroup
806
- if (fieldPath[0] === '$') {
807
-
808
- if (fieldPath == '$text') {
809
- if (!target['$and']) {
810
- target['$and'] = [];
811
- }
812
-
813
- // $text values need to be objects, for mongo at least
814
- if (typeof value == 'string') {
815
- value = {$search: value};
816
- }
817
-
818
- target['$and'].push({$text: value});
819
- return;
820
- }
821
-
822
- subgroups[fieldPath] = value;
823
- return;
824
- }
825
-
826
- // Get field config information
827
- fieldConfig = that.getFieldInfo(fieldPath, true);
828
-
829
- if (that.debug) {
830
- console.log(fieldPath, fieldConfig, fieldConfig.alias, that.model.name);
831
- }
832
-
833
- // If it's a translatable field we'll need to sort by a dot-notation path
834
- if (fieldConfig.config.isTranslatable) {
835
- temp = {};
836
- temp[fieldPath] = value;
837
-
838
- // Remember translatable conditions
839
- that.translation_conditions.push({
840
- query : temp,
841
- value : value,
842
- path : fieldPath
843
- });
844
-
845
- prefixes = that.getPrefixes();
846
-
847
- fieldPaths = [fieldConfig.path];
848
-
849
- for (i = 0; i < prefixes.length; i++) {
850
- fieldPaths.push(fieldConfig.field + '.' + prefixes[i]);
851
- }
852
- } else {
853
- fieldPaths = [fieldConfig.path];
854
- }
855
-
856
- // If a "fallback" field has been given, also look there
857
- if (fieldConfig.config.fallback) {
858
- fieldPaths.push(fieldConfig.config.fallback);
859
- }
860
-
861
-
862
- // Cast the value to the correct type
863
- if (fieldConfig.config.castForQuery) {
864
- value = fieldConfig.config.castForQuery(value, fieldPaths);
865
- } else if (Blast.isNode && typeof value == 'string' && that.model) {
866
- if (that.model.datasource && that.model.datasource.supports('objectid') && fieldPath.endsWith('_id') && value.isObjectId()) {
867
- value = alchemy.castObjectId(value);
868
- }
869
- }
870
-
871
- // If it's a condition for the main model, just add it to the target
872
- if (fieldConfig.alias == that.model.name) {
873
-
874
- if (fieldPaths.length == 1) {
875
- target[fieldPaths.first()] = value;
876
- } else {
877
-
878
- if (!target['$or']) {
879
- target['$or'] = [];
880
- }
881
-
882
- for (i = 0; i < fieldPaths.length; i++) {
883
- obj = {};
884
- obj[fieldPaths[i]] = value;
885
- target['$or'].push(obj);
886
- }
887
- }
888
-
889
- return;
890
- }
891
-
892
- if (!collections[fieldConfig.alias]) {
893
- collections[fieldConfig.alias] = {
894
- fieldConfig : fieldConfig,
895
- table : fieldConfig.table,
896
- model : fieldConfig.model,
897
- conditions : {}
898
- };
899
- }
900
-
901
- // Add it to the collections conditions
902
- if (fieldPaths.length == 1) {
903
- collections[fieldConfig.alias].conditions[fieldPaths.first()] = value;
904
- } else {
905
- if (!collections[fieldConfig.alias].conditions['$or']) {
906
- collections[fieldConfig.alias].conditions['$or'] = [];
907
- }
908
-
909
- for (i = 0; i < fieldPaths.length; i++) {
910
- obj = {};
911
- obj[fieldPaths[i]] = value;
912
- collections[fieldConfig.alias].conditions['$or'].push(obj);
913
- }
914
- }
915
- });
916
-
917
- // Now we'll handle the external collections
918
- Object.each(collections, function eachCollection(collection, alias) {
919
-
920
- tasks[tasks.length] = function getExternalCollection(next) {
921
-
922
- var config = that.model.associations[alias],
923
- fields = {},
924
- conds = {},
925
- assocKey,
926
- localKey,
927
- needLength;
928
-
929
- localKey = config.options.localKey;
930
- assocKey = config.options.foreignKey;
931
-
932
- // Indicate we only want the associate field value
933
- fields[assocKey] = 1;
934
-
935
- if (type == '$or') {
936
- conds['$or'] = Object.divide(collection.conditions);
937
- } else {
938
- // $not and $ands are both looked for using $and,
939
- // $not will be inverted later on
940
- conds['$and'] = Object.divide(collection.conditions);
941
- }
942
-
943
- needLength = true;
944
-
945
- // Hack to get {$exists: false} working
946
- Object.walk(conds, function eachCond(value, key, parent) {
947
- if (key == '$exists' && value == false) {
948
- needLength = false;
949
- }
950
- });
951
-
952
- collection.model.readDatasource(conds, {fields: fields}, function doneRead(err, items) {
953
-
954
- var ids = [],
955
- temp = {},
956
- i;
957
-
958
- if (that.debug) {
959
- console.log('Querying assoc', collection.model.name, 'conds:', conds, 'result:', err, items, 'field config:', config);
960
- }
961
-
962
- if (err) {
963
- return next(err);
964
- }
965
-
966
- // Put these ids in an $and array
967
- if (!target.$and) {
968
- target.$and = [];
969
- }
970
-
971
- if (!items.length) {
972
- if (needLength) {
973
- ids.push('no_assoc_data_found');
974
- }
975
- } else {
976
- for (i = 0; i < items.length; i++) {
977
- ids.push(items[i][assocKey]);
978
- }
979
- }
980
-
981
- // If no length is needed, and no ids have been found then
982
- // don't add the condition
983
- if (!needLength && !ids.length) {
984
- return next();
985
- }
986
-
987
- // Add the normalized condition
988
- temp[localKey] = {$in: ids};
989
-
990
- // If it was in a $not group, wrap it in a $not object
991
- if (type == '$not') {
992
- temp[localKey] = {$not: temp[localKey]};
993
- }
994
-
995
- target.$and.push(temp);
996
-
997
- next();
998
- });
999
- };
1000
- });
1001
-
1002
- if (this.error) {
1003
- return doCallback(this.error);
1004
- }
1005
-
1006
- if (this.debug) {
1007
- console.log('Normalizing query subgroups:', subgroups);
1008
- }
1009
-
1010
- // Now we'll handle subgroups
1011
- Object.each(subgroups, function eachSubgroup(subgroup, subtype) {
1012
-
1013
- tasks[tasks.length] = function subGroupTask(next) {
1014
-
1015
- var newTarget = {};
1016
-
1017
- if (that.debug) {
1018
- console.log('Normalizing subgroup type "' + subtype + '"', subgroup);
1019
- }
1020
-
1021
- that.normalizeGroup(subtype, newTarget, subgroup, function normalizedSubGroup() {
1022
-
1023
- var key,
1024
- obj,
1025
- notobj;
1026
-
1027
- if (!target[subtype]) {
1028
- target[subtype] = [];
1029
- }
1030
-
1031
- if (subtype === '$not') {
1032
-
1033
- // $not groups don't actually exist in mongodb
1034
- delete target['$not'];
1035
-
1036
- // @todo: this just adds it to other $and groups,
1037
- // which could be not what we want in complicated queries
1038
- subtype = '$and';
1039
- }
1040
-
1041
- for (key in newTarget) {
1042
-
1043
- obj = {};
1044
- obj[key] = newTarget[key];
1045
-
1046
- // Make sure the subtype exists ($and / $or)
1047
- // It could be this doesn't exist yet when only searching
1048
- // with a $not condition
1049
- if (!target[subtype]) {
1050
- target[subtype] = [];
1051
- }
1052
-
1053
- target[subtype].push(obj);
1054
- }
1055
-
1056
- if (that.debug) {
1057
- console.log('Final conds', target);
1058
- }
1059
-
1060
- next();
1061
- });
1062
- };
1063
- });
1064
-
1065
- // Execute all the finds
1066
- Function.parallel(false, tasks, function executedAllFinds() {
1067
- doCallback();
1068
- });
1069
- });
1070
-
1071
- /**
1072
- * Process the sort options
1073
- *
1074
- * @author Jelle De Loecker <jelle@develry.be>
1075
- * @since 0.0.1
1076
- * @version 1.0.0
1077
- */
1078
- DbQuery.setMethod(function processSort() {
1079
-
1080
- var sort = this.options.sort,
1081
- aliasSort,
1082
- fieldInfo,
1083
- prefixes,
1084
- name,
1085
- temp,
1086
- key,
1087
- val,
1088
- i;
1089
-
1090
- if (sort == null) {
1091
- if (Array.isArray(this.model.sort)) {
1092
- sort = this.model.sort.slice(0);
1093
- } else if (this.model.sort) {
1094
- sort = Object.assign({}, this.model.sort);
1095
- }
1096
- }
1097
-
1098
- // If sort is explicitly false, don't sort anything!
1099
- if (sort === false) {
1100
- return;
1101
- }
1102
-
1103
- aliasSort = {};
1104
-
1105
- // @todo: Keep using arrays, do not use objects.
1106
- // arrays are actually the correct way to provide sorting data,
1107
- // since only arrays maintain their order. Objects do not.
1108
- if (Array.isArray(sort)) {
1109
- temp = sort;
1110
- sort = {};
1111
-
1112
- for (i = 0; i < temp.length; i++) {
1113
- sort[temp[i]] = 1;
1114
- }
1115
- }
1116
-
1117
- // Cast to an object
1118
- if (typeof sort !== 'object') {
1119
- key = sort;
1120
- sort = {};
1121
- sort[key] = 1;
1122
- }
1123
-
1124
- // Normalize the fields
1125
- for (key in sort) {
1126
-
1127
- // Get the aliasmodel & field name of the given key
1128
- fieldInfo = this.getFieldInfo(key, true);
1129
-
1130
- // If it's a translatable field we'll need to sort by a dot-notation path
1131
- if (fieldInfo.config && fieldInfo.config.translatable && this.model.render) {
1132
-
1133
- prefixes = this.getPrefixes();
1134
-
1135
- name = [];
1136
-
1137
- for (i = 0; i < prefixes.length; i++) {
1138
- name.push(fieldInfo.field + '.' + prefixes[i]);
1139
- }
1140
- } else {
1141
- name = [fieldInfo.path];
1142
- }
1143
-
1144
- // Create an entry for this alias
1145
- if (!aliasSort[fieldInfo.alias]) {
1146
- aliasSort[fieldInfo.alias] = {};
1147
- }
1148
-
1149
- // Get the current value of the sort direction
1150
- val = String(sort[key]).toLowerCase();
1151
-
1152
- switch (val) {
1153
- case '1':
1154
- case 'asc':
1155
- val = 1;
1156
- break;
1157
-
1158
- default:
1159
- val = -1;
1160
- }
1161
-
1162
- // Add this field to the queryFields
1163
- this.addQueryField(fieldInfo);
1164
-
1165
- for (i = 0; i < name.length; i++) {
1166
- aliasSort[fieldInfo.alias][name[i]] = val;
1167
- }
1168
- }
1169
-
1170
- // If there is nothing to sort, set the property to false
1171
- if (Object.isEmpty(aliasSort)) {
1172
- sort = false;
1173
- } else {
1174
- sort = aliasSort;
1175
- }
1176
-
1177
- this.sort = sort;
1178
- });
1179
-
1180
- /**
1181
- * Execute the query
1182
- *
1183
- * @author Jelle De Loecker <jelle@develry.be>
1184
- * @since 0.1.0
1185
- * @version 1.0.6
1186
- *
1187
- * @param {Function} callback
1188
- */
1189
- DbQuery.setMethod(function execute(callback) {
1190
-
1191
- var that = this,
1192
- tasks,
1193
- schema;
1194
-
1195
- if (this.error) {
1196
- Blast.setImmediate(function immediateError() {
1197
- callback(that.error);
1198
- });
1199
- return;
1200
- }
1201
-
1202
- tasks = {};
1203
- schema = this.model.schema;
1204
-
1205
- // Normalize the conditions by getting associated data
1206
- this.normalize(function gotNormalizedConditions(err, conditions) {
1207
-
1208
- var options = {},
1209
- associations = {},
1210
- alias;
1211
-
1212
- if (that.error) {
1213
- return callback(that.error)
1214
- }
1215
-
1216
- // Limit the amount of fields returned if the fields object has been set
1217
- if (that.fields && that.fields[that.model.name]) {
1218
- options.fields = that.fields[that.model.name];
1219
-
1220
- for (alias in that.fields) {
1221
- if (schema.associations[alias]) {
1222
- associations[alias] = schema.associations[alias];
1223
- }
1224
- }
1225
- } else {
1226
-
1227
- if (that.contain) {
1228
- associations = {};
1229
-
1230
- for (alias in that.contain) {
1231
- if (schema.associations[alias]) {
1232
- associations[alias] = schema.associations[alias];
1233
- }
1234
- }
1235
- } else {
1236
- associations = schema.associations;
1237
- }
1238
- }
1239
-
1240
- // Sort the results
1241
- if (that.sort && that.sort[that.model.name]) {
1242
- options.sort = that.sort[that.model.name];
1243
- }
1244
-
1245
- if (that.options.limit > 0) {
1246
- options.limit = that.options.limit;
1247
- }
1248
-
1249
- if (that.options.offset > 0) {
1250
- options.skip = that.options.offset;
1251
- }
1252
-
1253
- options.recursive = that.options.recursive;
1254
- options.available = that.options.available;
1255
- options.extraneous = that.options.extraneous;
1256
-
1257
- // Store the associations we need to look for later
1258
- that.associations = associations;
1259
-
1260
- that.model.emit('reading_datasource', that);
1261
-
1262
- // Query the main model
1263
- that.model.readDatasource(conditions, options, function gotItems(err, items, available) {
1264
-
1265
- var item_prefixes = [],
1266
- translations,
1267
- found_prefix,
1268
- condition,
1269
- results,
1270
- record,
1271
- prefix,
1272
- temp,
1273
- i,
1274
- j;
1275
-
1276
- that.model.emit('read_datasource', that);
1277
-
1278
- if (err) {
1279
- return callback(err);
1280
- }
1281
-
1282
- if (items == null) {
1283
- return callback(new Error('Query read returned null results'));
1284
- }
1285
-
1286
- results = [];
1287
-
1288
- for (i = 0; i < items.length; i++) {
1289
- record = {};
1290
- record[that.model.name] = items[i];
1291
- found_prefix = null;
1292
-
1293
- if (that.options.locale && that.options.locale !== true) {
1294
- found_prefix = that.options.locale;
1295
- } else {
1296
- FindPrefix:
1297
- for (j = 0; j < that.translation_conditions.length; j++) {
1298
- condition = that.translation_conditions[j];
1299
-
1300
- // Get the translation object
1301
- translations = Object.path(items[i], condition.path);
1302
-
1303
- // Go over every found prefix
1304
- for (prefix in translations) {
1305
- temp = Object.setPath({}, condition.path, translations[prefix]);
1306
-
1307
- // If this translatable condition matches what was found,
1308
- // then this is the found prefix
1309
- // @todo: currently only 1 prefix will be set,
1310
- // even if there are multiple conditions with
1311
- // multiple matches in different translations
1312
- if (DbQuery.match(temp, condition.query)) {
1313
- found_prefix = prefix;
1314
- break FindPrefix;
1315
- }
1316
- }
1317
- }
1318
- }
1319
-
1320
- item_prefixes.push(found_prefix);
1321
- results.push(record);
1322
- }
1323
-
1324
- if (that.debug) {
1325
- console.log('Query results for', that.model.name, conditions, options, JSON.parse(JSON.stringify(results)));
1326
- }
1327
-
1328
- results.available = available;
1329
- results.item_prefixes = item_prefixes;
1330
-
1331
- return callback(null, results);
1332
- });
1333
- });
1334
- });
1335
-
1336
- /**
1337
- * If the field option has been provided,
1338
- * limit the fields returned
1339
- *
1340
- * @author Jelle De Loecker <jelle@develry.be>
1341
- * @since 0.1.0
1342
- * @version 0.1.0
1343
- */
1344
- DbQuery.setMethod(function processFields() {
1345
-
1346
- var fieldConfig,
1347
- fieldPath,
1348
- fields,
1349
- i;
1350
-
1351
- fields = this.options.fields;
1352
-
1353
- if (!fields || typeof fields !== 'object') {
1354
- return;
1355
- }
1356
-
1357
- if (!Array.isArray(fields)) {
1358
- fields = Object.keys(fields);
1359
- }
1360
-
1361
- // Go over every given field in the array
1362
- for (i = 0; i < fields.length; i++) {
1363
-
1364
- fieldPath = fields[i];
1365
-
1366
- if (!this.fields) {
1367
- this.fields = {};
1368
- this.queryFields = {};
1369
- }
1370
-
1371
- this.addField(fieldPath);
1372
- this.addField(fieldPath, this.queryFields);
1373
- }
1374
- });
1375
-
1376
- /**
1377
- * Add the field in the given object under its alias
1378
- *
1379
- * @author Jelle De Loecker <jelle@develry.be>
1380
- * @since 0.1.0
1381
- * @version 0.3.0
1382
- */
1383
- DbQuery.setMethod(function addField(fieldPath, obj) {
1384
-
1385
- var associations = this.model.associations,
1386
- fieldConfig,
1387
- assoc;
1388
-
1389
- // If the fields object is false, all fields should be returned
1390
- if (!this.fields) {
1391
- return;
1392
- }
1393
-
1394
- if (typeof fieldPath == 'string') {
1395
- // Get the field config info
1396
- fieldConfig = this.getFieldInfo(fieldPath, true);
1397
- } else {
1398
- fieldConfig = fieldPath;
1399
- }
1400
-
1401
- // If the field required is from an associated model,
1402
- // make sure the local key is also queried
1403
- if (assoc = associations[fieldConfig.alias]) {
1404
-
1405
- if (assoc.alias == this.model.name) {
1406
- log.warn('Trying to add alias with the same name as the current model: "' + assoc.alias + '"');
1407
- return;
1408
- }
1409
-
1410
- if (!this.fields[this.model.name] || !this.fields[this.model.name][assoc.options.localKey]) {
1411
- this.addField(this.model.name + '.' + assoc.options.localKey);
1412
- }
1413
- }
1414
-
1415
- if (typeof obj === 'undefined') {
1416
- obj = this.fields;
1417
-
1418
- // Also add it to the query field
1419
- this.addQueryField(fieldConfig);
1420
- }
1421
-
1422
- // Create an entry for this alias
1423
- if (!obj[fieldConfig.alias]) {
1424
- obj[fieldConfig.alias] = {};
1425
- }
1426
-
1427
- obj[fieldConfig.alias][fieldConfig.field] = 1;
1428
- });
1429
-
1430
-
1431
- /**
1432
- * Make sure the given field is queried (returned by mongo)
1433
- *
1434
- * @author Jelle De Loecker <jelle@develry.be>
1435
- * @since 0.0.1
1436
- * @version 0.0.1
1437
- *
1438
- * @param {String} fieldPath
1439
- */
1440
- DbQuery.setMethod(function addQueryField(fieldPath) {
1441
- this.addField(fieldPath, this.queryFields);
1442
- });
1443
-
1444
- /**
1445
- * Get field info from a field path string,
1446
- * which can contain a model name and a field chain,
1447
- * all joined by dots
1448
- *
1449
- * @author Jelle De Loecker <jelle@develry.be>
1450
- * @since 0.0.1
1451
- * @version 1.0.0
1452
- *
1453
- * @param {String} path eg: AclRule.settings.halt
1454
- *
1455
- * @return {Object}
1456
- */
1457
- DbQuery.setMethod(function getFieldInfo(path) {
1458
-
1459
- var model = this.model,
1460
- alias_model,
1461
- config,
1462
- alias;
1463
-
1464
- // Parse the path
1465
- path = DbQuery.parseRecordPath(path);
1466
-
1467
- // Get the alias
1468
- alias = path.alias || model.name;
1469
-
1470
- // Get the alias model
1471
- alias_model = model.getAliasModel(alias);
1472
-
1473
- config = alias_model.getField(path.field);
1474
-
1475
- return {
1476
- alias : alias,
1477
- model : alias_model,
1478
- table : alias_model.table,
1479
- field : path.field,
1480
- path : path.path.join('.'),
1481
- depth : path.path.length - 1, // How deep we look in a field
1482
- config : config || {}
1483
- };
1484
- });
1485
-
1486
- if (Blast.isNode) {
1487
- global.DbQuery = DbQuery;
1488
- }