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.
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/lib/app/behaviour/publishable_behaviour.js +5 -5
- package/lib/app/behaviour/revision_behaviour.js +96 -59
- package/lib/app/behaviour/sluggable_behaviour.js +14 -14
- package/lib/app/conduit/electron_conduit.js +9 -9
- package/lib/app/conduit/http_conduit.js +13 -13
- package/lib/app/conduit/loopback_conduit.js +15 -15
- package/lib/app/conduit/socket_conduit.js +43 -43
- package/lib/app/config/routes.js +26 -0
- package/lib/app/controller/00-default_app_controller.js +21 -0
- package/lib/app/controller/alchemy_info_controller.js +12 -12
- package/lib/app/datasource/mongo_datasource.js +27 -18
- package/lib/app/element/00-default_app_element.js +19 -0
- package/lib/app/element/time_ago.js +5 -5
- package/lib/app/helper/00-default_app_helper.js +11 -0
- package/lib/app/helper/alchemy_helper.js +22 -22
- package/lib/app/helper/backed_map.js +1 -1
- package/lib/app/helper/breadcrumb.js +10 -10
- package/lib/app/helper/client_collection.js +3 -3
- package/lib/app/helper/cron.js +29 -29
- package/lib/app/helper/enum_values.js +6 -6
- package/lib/app/helper/pagination_helper.js +36 -36
- package/lib/app/helper/router_helper.js +35 -35
- package/lib/app/helper/socket_helper.js +57 -57
- package/lib/app/helper/syncable.js +62 -62
- package/lib/app/helper_component/paginate_component.js +9 -9
- package/lib/app/helper_controller/component.js +1 -1
- package/lib/app/helper_controller/conduit.js +31 -31
- package/lib/app/helper_controller/controller.js +54 -39
- package/lib/app/helper_datasource/00-nosql_datasource.js +624 -70
- package/lib/app/helper_datasource/05-fallback_datasource.js +10 -10
- package/lib/app/helper_datasource/idb_datasource.js +6 -6
- package/lib/app/helper_datasource/indexed_db.js +22 -22
- package/lib/app/helper_datasource/remote_datasource.js +5 -5
- package/lib/app/helper_error/http_error.js +4 -4
- package/lib/app/helper_error/model_error.js +2 -2
- package/lib/app/helper_error/validation_error.js +12 -12
- package/lib/app/helper_field/00-objectid_field.js +7 -7
- package/lib/app/helper_field/05-string_field.js +16 -12
- package/lib/app/helper_field/06-text_field.js +2 -4
- package/lib/app/helper_field/10-number_field.js +9 -12
- package/lib/app/helper_field/11-date_field.js +15 -15
- package/lib/app/helper_field/15-local_temporal_field.js +10 -10
- package/lib/app/helper_field/20-decimal_field.js +8 -9
- package/lib/app/helper_field/belongsto_field.js +1 -1
- package/lib/app/helper_field/big_int_field.js +8 -8
- package/lib/app/helper_field/boolean_field.js +9 -11
- package/lib/app/helper_field/datetime_field.js +3 -3
- package/lib/app/helper_field/enum_field.js +13 -8
- package/lib/app/helper_field/fixed_decimal_field.js +6 -7
- package/lib/app/helper_field/geopoint_field.js +9 -10
- package/lib/app/helper_field/habtm_field.js +3 -3
- package/lib/app/helper_field/hasoneparent_field.js +1 -1
- package/lib/app/helper_field/html_field.js +2 -4
- package/lib/app/helper_field/integer_field.js +8 -11
- package/lib/app/helper_field/local_date_field.js +5 -5
- package/lib/app/helper_field/local_date_time_field.js +5 -5
- package/lib/app/helper_field/local_time_field.js +5 -5
- package/lib/app/helper_field/mixed_field.js +5 -5
- package/lib/app/helper_field/object_field.js +6 -6
- package/lib/app/helper_field/password_field.js +3 -3
- package/lib/app/helper_field/regexp_field.js +7 -9
- package/lib/app/helper_field/schema_field.js +91 -88
- package/lib/app/helper_field/settings_field.js +92 -0
- package/lib/app/helper_field/time_field.js +6 -6
- package/lib/app/helper_field/url_field.js +2 -4
- package/lib/app/helper_model/00-base_criteria.js +662 -0
- package/lib/app/helper_model/05-criteria_expressions.js +605 -0
- package/lib/app/helper_model/10-model_criteria.js +1182 -0
- package/lib/app/helper_model/data_provider.js +2 -2
- package/lib/app/helper_model/document.js +103 -92
- package/lib/app/helper_model/document_list.js +14 -14
- package/lib/app/helper_model/field_config.js +11 -11
- package/lib/app/helper_model/field_set.js +17 -17
- package/lib/app/helper_model/model.js +201 -123
- package/lib/app/helper_model/remote_data_provider.js +2 -2
- package/lib/app/helper_validator/00_validator.js +16 -16
- package/lib/app/helper_validator/not_empty_validator.js +9 -9
- package/lib/app/model/00-default_app_model.js +18 -0
- package/lib/app/model/05-system_model.js +27 -0
- package/lib/app/model/{alchemy_migration_model.js → system_migration_model.js} +4 -4
- package/lib/app/model/system_setting_model.js +154 -0
- package/lib/app/model/{alchemy_task_history_model.js → system_task_history_model.js} +7 -7
- package/lib/app/model/{alchemy_task_model.js → system_task_model.js} +11 -11
- package/lib/bootstrap.js +22 -312
- package/lib/class/accumulator.js +5 -5
- package/lib/class/behaviour.js +5 -5
- package/lib/class/component.js +3 -3
- package/lib/class/conduit.js +152 -150
- package/lib/class/controller.js +42 -42
- package/lib/class/datasource.js +74 -79
- package/lib/class/document.js +74 -95
- package/lib/class/document_list.js +5 -5
- package/lib/class/element.js +17 -17
- package/lib/class/error.js +3 -3
- package/lib/class/field.js +169 -91
- package/lib/class/field_value.js +6 -6
- package/lib/class/helper.js +3 -3
- package/lib/class/inode.js +17 -17
- package/lib/class/inode_dir.js +12 -12
- package/lib/class/inode_file.js +26 -26
- package/lib/class/inode_list.js +4 -4
- package/lib/class/migration.js +4 -4
- package/lib/class/model.js +146 -165
- package/lib/class/path_definition.js +22 -22
- package/lib/class/path_evaluator.js +5 -5
- package/lib/class/path_param_definition.js +7 -7
- package/lib/class/plugin.js +312 -0
- package/lib/class/postponement.js +29 -29
- package/lib/class/reciprocal.js +8 -8
- package/lib/class/route.js +33 -33
- package/lib/class/router.js +73 -73
- package/lib/class/schema.js +21 -21
- package/lib/class/schema_client.js +105 -82
- package/lib/class/session.js +63 -29
- package/lib/class/session_scene.js +4 -4
- package/lib/class/sitemap.js +16 -16
- package/lib/class/task.js +39 -39
- package/lib/class/task_service.js +43 -47
- package/lib/{init → core}/alchemy.js +411 -373
- package/lib/{init/functions.js → core/alchemy_functions.js} +113 -91
- package/lib/core/alchemy_load_functions.js +715 -0
- package/lib/core/base.js +50 -62
- package/lib/core/client_alchemy.js +144 -152
- package/lib/core/client_base.js +27 -50
- package/lib/core/discovery.js +16 -18
- package/lib/core/middleware.js +54 -43
- package/lib/core/{routing.js → prefix.js} +14 -16
- package/lib/core/setting.js +1684 -0
- package/lib/core/stage.js +758 -0
- package/lib/scripts/create_constants.js +119 -0
- package/lib/{init/languages.js → scripts/create_languages.js} +5 -5
- package/lib/scripts/create_settings.js +449 -0
- package/lib/scripts/create_shared_constants.js +95 -0
- package/lib/scripts/create_stages.js +55 -0
- package/lib/scripts/init_alchemy.js +51 -0
- package/lib/{init/requirements.js → scripts/preload_modules.js} +15 -2
- package/lib/scripts/setup_devwatch.js +238 -0
- package/lib/stages/00-load_core.js +342 -0
- package/lib/stages/05-load_app.js +57 -0
- package/lib/stages/10-datasource.js +61 -0
- package/lib/stages/15-tasks.js +27 -0
- package/lib/stages/20-settings.js +68 -0
- package/lib/stages/50-routes.js +218 -0
- package/lib/stages/90-server.js +347 -0
- package/package.json +5 -7
- package/lib/app/helper_model/criteria.js +0 -2294
- package/lib/app/helper_model/db_query.js +0 -1488
- package/lib/app/routes.js +0 -11
- package/lib/core/socket.js +0 -171
- package/lib/init/constants.js +0 -158
- package/lib/init/devwatch.js +0 -238
- package/lib/init/load_functions.js +0 -979
- 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
|
-
}
|