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
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
const CriteriaNS = Function.getNamespace('Alchemy.Criteria'),
|
|
2
|
+
Expressions = Function.getNamespace('Alchemy.Criteria.Expression');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Criteria class
|
|
6
|
+
*
|
|
7
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
8
|
+
* @since 1.1.0
|
|
9
|
+
* @version 1.4.0
|
|
10
|
+
*
|
|
11
|
+
* @param {Model} model
|
|
12
|
+
*/
|
|
13
|
+
const Criteria = Function.inherits('Alchemy.Criteria.Criteria', function Model(model) {
|
|
14
|
+
|
|
15
|
+
// The model
|
|
16
|
+
this.model = model;
|
|
17
|
+
|
|
18
|
+
Model.super.call(this, {
|
|
19
|
+
select : new Select(this),
|
|
20
|
+
document : true,
|
|
21
|
+
document_list : true,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Make sure to get a criteria
|
|
27
|
+
*
|
|
28
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
29
|
+
* @since 1.2.5
|
|
30
|
+
* @version 1.4.0
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} conditions The thing that should be a criteria
|
|
33
|
+
* @param {Object} options The options to apply
|
|
34
|
+
* @param {Model} model The model that it probably belongs to
|
|
35
|
+
*
|
|
36
|
+
* @return {Criteria}
|
|
37
|
+
*/
|
|
38
|
+
Criteria.setStatic(function cast(conditions, options, model) {
|
|
39
|
+
|
|
40
|
+
if (Criteria.isCriteria(conditions)) {
|
|
41
|
+
return conditions;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (arguments.length == 2) {
|
|
45
|
+
model = options;
|
|
46
|
+
options = conditions;
|
|
47
|
+
conditions = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let instance = new Criteria(model);
|
|
51
|
+
|
|
52
|
+
if (options) {
|
|
53
|
+
instance.applyOldOptions(options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (conditions) {
|
|
57
|
+
instance.applyConditions(conditions);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return instance;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Undry the given object
|
|
65
|
+
*
|
|
66
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
67
|
+
* @since 1.1.0
|
|
68
|
+
* @version 1.2.3
|
|
69
|
+
*
|
|
70
|
+
* @param {Object} data
|
|
71
|
+
*
|
|
72
|
+
* @return {Criteria}
|
|
73
|
+
*/
|
|
74
|
+
Criteria.setStatic(function unDry(data) {
|
|
75
|
+
|
|
76
|
+
var criteria = new Criteria();
|
|
77
|
+
|
|
78
|
+
if (data.model) {
|
|
79
|
+
try {
|
|
80
|
+
criteria.model = alchemy.getModel(data.model);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
// Ignore
|
|
83
|
+
console.warn('Failed to find "' + data.model + '" model');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Revive the group instance
|
|
88
|
+
criteria.group = Expressions.Group.revive(data.group, criteria);
|
|
89
|
+
|
|
90
|
+
if (!data.options) {
|
|
91
|
+
data.options = {};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Revive the select
|
|
95
|
+
data.options.select = Select.revive(data.options.select, criteria);
|
|
96
|
+
|
|
97
|
+
criteria.options = data.options || {};
|
|
98
|
+
|
|
99
|
+
return criteria;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse a path to an object
|
|
104
|
+
*
|
|
105
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
106
|
+
* @since 1.1.0
|
|
107
|
+
* @version 1.1.0
|
|
108
|
+
*
|
|
109
|
+
* @param {string} path
|
|
110
|
+
* @param {Criteria} criteria
|
|
111
|
+
*
|
|
112
|
+
* @return {Object}
|
|
113
|
+
*/
|
|
114
|
+
Criteria.setStatic(function parsePath(path, criteria) {
|
|
115
|
+
|
|
116
|
+
var target_path,
|
|
117
|
+
result = {},
|
|
118
|
+
pieces,
|
|
119
|
+
piece,
|
|
120
|
+
alias,
|
|
121
|
+
i;
|
|
122
|
+
|
|
123
|
+
if (path.indexOf('.') > -1) {
|
|
124
|
+
pieces = path.split('.');
|
|
125
|
+
} else {
|
|
126
|
+
pieces = [path];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (i = 0; i < pieces.length; i++) {
|
|
130
|
+
piece = pieces[i];
|
|
131
|
+
alias = null;
|
|
132
|
+
|
|
133
|
+
if (criteria && criteria.model) {
|
|
134
|
+
if (criteria.model.associations[piece]) {
|
|
135
|
+
alias = piece;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!alias && piece[0].isUpperCase()) {
|
|
140
|
+
alias = piece;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (alias) {
|
|
144
|
+
if (!result.association) {
|
|
145
|
+
result.association = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
result.association.push(alias);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
target_path = pieces.slice(i).join('.');
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (target_path) {
|
|
157
|
+
result.target_path = target_path;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a reference to the datasource
|
|
165
|
+
*
|
|
166
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
167
|
+
* @since 1.1.0
|
|
168
|
+
* @version 1.1.0
|
|
169
|
+
*
|
|
170
|
+
* @type {Datasource}
|
|
171
|
+
*/
|
|
172
|
+
Criteria.setProperty(function datasource() {
|
|
173
|
+
|
|
174
|
+
if (this._datasource) {
|
|
175
|
+
return this._datasource;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (this.model) {
|
|
179
|
+
return this.model.datasource;
|
|
180
|
+
}
|
|
181
|
+
}, function setDatasource(ds) {
|
|
182
|
+
this._datasource = ds;
|
|
183
|
+
return this._datasource;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The recursiveness of this criteria
|
|
188
|
+
*
|
|
189
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
190
|
+
* @since 1.1.0
|
|
191
|
+
* @version 1.1.0
|
|
192
|
+
*
|
|
193
|
+
* @type {number}
|
|
194
|
+
*/
|
|
195
|
+
Criteria.setProperty(function recursive_level() {
|
|
196
|
+
|
|
197
|
+
if (this.options.recursive) {
|
|
198
|
+
return this.options.recursive;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return 0;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Allow the criteria to be used in a for wait loop
|
|
206
|
+
*
|
|
207
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
208
|
+
* @since 1.1.0
|
|
209
|
+
* @version 1.1.0
|
|
210
|
+
*/
|
|
211
|
+
Criteria.setMethod(Symbol.asyncIterator, function asyncIterator() {
|
|
212
|
+
|
|
213
|
+
const that = this,
|
|
214
|
+
model = this.model;
|
|
215
|
+
|
|
216
|
+
if (!model) {
|
|
217
|
+
throw new Error('Unable to iterate over a criteria without a model');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Clone it
|
|
221
|
+
let criteria = this.clone();
|
|
222
|
+
|
|
223
|
+
// Set the limit to 1
|
|
224
|
+
criteria.limit(1);
|
|
225
|
+
|
|
226
|
+
// Create the iterator context
|
|
227
|
+
let context = {
|
|
228
|
+
index : 0,
|
|
229
|
+
next : async function next() {
|
|
230
|
+
|
|
231
|
+
criteria.skip(this.index++);
|
|
232
|
+
|
|
233
|
+
let record = await model.find('first', criteria);
|
|
234
|
+
|
|
235
|
+
if (!record) {
|
|
236
|
+
return {done: true};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {value: record, done: false};
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return context;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Return object for jsonifying
|
|
248
|
+
*
|
|
249
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
250
|
+
* @since 1.1.0
|
|
251
|
+
* @version 1.2.3
|
|
252
|
+
*
|
|
253
|
+
* @return {Object}
|
|
254
|
+
*/
|
|
255
|
+
Criteria.setMethod(function toJSON() {
|
|
256
|
+
|
|
257
|
+
let result = {},
|
|
258
|
+
options;
|
|
259
|
+
|
|
260
|
+
if (this.model && this.model.name) {
|
|
261
|
+
result.model = this.model.name;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (this.options) {
|
|
265
|
+
let key;
|
|
266
|
+
options = {};
|
|
267
|
+
|
|
268
|
+
for (key in this.options) {
|
|
269
|
+
if (key == 'assoc_cache' || key == 'init_record') {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
options[key] = this.options[key];
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
result.group = this.group;
|
|
278
|
+
result.options = options;
|
|
279
|
+
|
|
280
|
+
return result;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Clone this instance
|
|
285
|
+
*
|
|
286
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
287
|
+
* @since 1.1.0
|
|
288
|
+
* @version 1.1.0
|
|
289
|
+
*
|
|
290
|
+
* @return {Criteria}
|
|
291
|
+
*/
|
|
292
|
+
Criteria.setMethod(function clone() {
|
|
293
|
+
|
|
294
|
+
var data = JSON.toDryObject(this),
|
|
295
|
+
result;
|
|
296
|
+
|
|
297
|
+
data.model = null;
|
|
298
|
+
result = JSON.undry(data);
|
|
299
|
+
|
|
300
|
+
result.model = this.model;
|
|
301
|
+
|
|
302
|
+
return result;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get the main fields to select
|
|
307
|
+
*
|
|
308
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
309
|
+
* @since 1.1.0
|
|
310
|
+
* @version 1.2.3
|
|
311
|
+
*
|
|
312
|
+
* @return {string[]}
|
|
313
|
+
*/
|
|
314
|
+
Criteria.setMethod(function getFieldsToSelect() {
|
|
315
|
+
|
|
316
|
+
let result;
|
|
317
|
+
|
|
318
|
+
if (this.options?.select?.fields?.length) {
|
|
319
|
+
result = this.options.select.fields.slice(0);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Fields can sometimes be required for a query (like in a join) but they
|
|
323
|
+
// won't be selected if other fields are explicitly set.
|
|
324
|
+
// So in that case: add these special fields to the projection
|
|
325
|
+
if (result && this.options?.select?.query_fields) {
|
|
326
|
+
result.push(...this.options.select.query_fields);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return result || [];
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get the association selects, if any
|
|
334
|
+
*
|
|
335
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
336
|
+
* @since 1.1.0
|
|
337
|
+
* @version 1.1.0
|
|
338
|
+
*
|
|
339
|
+
* @return {Object}
|
|
340
|
+
*/
|
|
341
|
+
Criteria.setMethod(function getAssociationsToSelect() {
|
|
342
|
+
|
|
343
|
+
let result = this.options.select.associations;
|
|
344
|
+
|
|
345
|
+
return result;
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Should the given association be queried?
|
|
350
|
+
*
|
|
351
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
352
|
+
* @since 1.1.0
|
|
353
|
+
* @version 1.1.0
|
|
354
|
+
*
|
|
355
|
+
* @param {string} name
|
|
356
|
+
*
|
|
357
|
+
* @return {boolean}
|
|
358
|
+
*/
|
|
359
|
+
Criteria.setMethod(function shouldQueryAssociation(name) {
|
|
360
|
+
|
|
361
|
+
var result = false;
|
|
362
|
+
|
|
363
|
+
// If there are explicit associations selected,
|
|
364
|
+
// then this one has to be in it!
|
|
365
|
+
if (this.getAssociationsToSelect()) {
|
|
366
|
+
result = this.options.select.shouldQueryAssociation(name);
|
|
367
|
+
} else {
|
|
368
|
+
// There are no explicit associations, look at the recursive level
|
|
369
|
+
result = this.recursive_level > 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return result;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Get association configuration in the current active model
|
|
377
|
+
* or in the options
|
|
378
|
+
*
|
|
379
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
380
|
+
* @since 1.1.0
|
|
381
|
+
* @version 1.1.0
|
|
382
|
+
*
|
|
383
|
+
* @param {string} alias
|
|
384
|
+
*
|
|
385
|
+
* @return {Object}
|
|
386
|
+
*/
|
|
387
|
+
Criteria.setMethod(function getAssociationConfiguration(alias) {
|
|
388
|
+
|
|
389
|
+
if (this.options.associations && this.options.associations[alias]) {
|
|
390
|
+
return this.options.associations[alias];
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return this.model.getAssociation(alias);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get a new criteria for adding associated data
|
|
398
|
+
*
|
|
399
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
400
|
+
* @since 1.1.0
|
|
401
|
+
* @version 1.2.3
|
|
402
|
+
*
|
|
403
|
+
* @param {string} name
|
|
404
|
+
* @param {Object} item
|
|
405
|
+
*
|
|
406
|
+
* @return {Criteria}
|
|
407
|
+
*/
|
|
408
|
+
Criteria.setMethod(function getCriteriaForAssociation(name, item) {
|
|
409
|
+
|
|
410
|
+
if (!this.model) {
|
|
411
|
+
throw new Error('Unable to create criteria for association "' + name + '" without originating model instance');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
let assoc_model = this.model.getAliasModel(name),
|
|
415
|
+
data = item[this.model.name];
|
|
416
|
+
|
|
417
|
+
// @TODO: For deadlock reasons we don't query self-referencing links!
|
|
418
|
+
// (Implemented in Schema fields, BelongsTo and such could still pose problems!)
|
|
419
|
+
if (assoc_model.name == this.options.init_model) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let association = this.getAssociationConfiguration(name);
|
|
424
|
+
|
|
425
|
+
let value = data[association.options.localKey];
|
|
426
|
+
|
|
427
|
+
// If no valid value is found for the associated key, do nothing
|
|
428
|
+
if (value == null) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
let assoc_crit = assoc_model.find(),
|
|
433
|
+
assoc_key = association.options.foreignKey,
|
|
434
|
+
options = this.options,
|
|
435
|
+
select;
|
|
436
|
+
|
|
437
|
+
if (Array.isArray(value)) {
|
|
438
|
+
assoc_crit.where(assoc_key).in(value);
|
|
439
|
+
} else {
|
|
440
|
+
assoc_crit.where(assoc_key).equals(value);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
assoc_crit.setOption('assoc_key', assoc_key);
|
|
444
|
+
assoc_crit.setOption('assoc_value', value);
|
|
445
|
+
|
|
446
|
+
// Make the assoc_cache if it doesn't exist yet
|
|
447
|
+
if (options.create_references !== false && !options.assoc_cache) {
|
|
448
|
+
options.assoc_cache = {};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Add the assoc_cache
|
|
452
|
+
if (options.assoc_cache) {
|
|
453
|
+
assoc_crit.setOption('assoc_cache', options.assoc_cache);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Take over the locale option
|
|
457
|
+
if (options.locale) {
|
|
458
|
+
assoc_crit.setOption('locale', options.locale);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// The debug object, if there is one
|
|
462
|
+
if (options._debugObject) {
|
|
463
|
+
assoc_crit.setOption('_debugObject', options._debugObject);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Don't get the available count
|
|
467
|
+
assoc_crit.setOption('available', false);
|
|
468
|
+
|
|
469
|
+
if (options.select.associations && options.select.associations[name]) {
|
|
470
|
+
select = options.select.associations[name];
|
|
471
|
+
assoc_crit.options.select = select.cloneForCriteria(assoc_crit);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Sort the results
|
|
475
|
+
// @TODO: add sorts
|
|
476
|
+
// if (query.sort && query.sort[alias]) {
|
|
477
|
+
// assocOpts.sort = query.sort[alias];
|
|
478
|
+
// }
|
|
479
|
+
|
|
480
|
+
if (Number.isSafeInteger(options.recursive) && options.recursive > 0) {
|
|
481
|
+
assoc_crit.recursive(options.recursive - 1);
|
|
482
|
+
} else {
|
|
483
|
+
// Disable recursiveness for the next level
|
|
484
|
+
assoc_crit.recursive(0);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Add the model name from where we're adding associated data
|
|
488
|
+
assoc_crit.setOption('init_model', options.init_model || this.model.name);
|
|
489
|
+
assoc_crit.setOption('init_record', options.init_record || item);
|
|
490
|
+
|
|
491
|
+
assoc_crit.setOption('from_alias', options.for_alias);
|
|
492
|
+
assoc_crit.setOption('from_model', options.for_model);
|
|
493
|
+
|
|
494
|
+
assoc_crit.setOption('for_alias', name);
|
|
495
|
+
assoc_crit.setOption('for_model', assoc_model.name);
|
|
496
|
+
|
|
497
|
+
// Honor the original document option
|
|
498
|
+
assoc_crit.setOption('document', options.document);
|
|
499
|
+
|
|
500
|
+
if (options.debug) {
|
|
501
|
+
assoc_crit.setOption('debug', true);
|
|
502
|
+
console.log('Associated criteria:', assoc_model.name, assoc_crit);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return assoc_crit;
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Limit the amount of records to get
|
|
510
|
+
*
|
|
511
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
512
|
+
* @since 1.1.0
|
|
513
|
+
* @version 1.2.7
|
|
514
|
+
*
|
|
515
|
+
* @param {number} amount
|
|
516
|
+
*
|
|
517
|
+
* @return {Criteria}
|
|
518
|
+
*/
|
|
519
|
+
Criteria.setMethod(function limit(amount) {
|
|
520
|
+
|
|
521
|
+
if (typeof amount != 'number') {
|
|
522
|
+
amount = parseInt(amount);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
this.options.limit = amount;
|
|
526
|
+
return this;
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Skip an amount of records
|
|
531
|
+
*
|
|
532
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
533
|
+
* @since 1.1.0
|
|
534
|
+
* @version 1.2.7
|
|
535
|
+
*
|
|
536
|
+
* @param {number} amount
|
|
537
|
+
*
|
|
538
|
+
* @return {Criteria}
|
|
539
|
+
*/
|
|
540
|
+
Criteria.setMethod(function skip(amount) {
|
|
541
|
+
|
|
542
|
+
if (typeof amount != 'number') {
|
|
543
|
+
amount = parseInt(amount);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
this.options.skip = amount;
|
|
547
|
+
return this;
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Get a specific page
|
|
552
|
+
*
|
|
553
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
554
|
+
* @since 1.1.3
|
|
555
|
+
* @version 1.2.7
|
|
556
|
+
*
|
|
557
|
+
* @param {number} page A 1-indexed page number
|
|
558
|
+
* @param {number} page_size
|
|
559
|
+
*
|
|
560
|
+
* @return {Criteria}
|
|
561
|
+
*/
|
|
562
|
+
Criteria.setMethod(function page(page, page_size) {
|
|
563
|
+
|
|
564
|
+
if (typeof page != 'number') {
|
|
565
|
+
page = parseInt(page);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (page_size && typeof page_size != 'number') {
|
|
569
|
+
page_size = parseInt(page_size);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (!page) {
|
|
573
|
+
throw new Error('A page number is required');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!page_size || !isFinite(page_size)) {
|
|
577
|
+
page_size = 10;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
let skip = (page - 1) * page_size;
|
|
581
|
+
|
|
582
|
+
this.options.page = page;
|
|
583
|
+
this.options.page_size = page_size;
|
|
584
|
+
|
|
585
|
+
this.skip(skip);
|
|
586
|
+
return this.limit(page_size);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Select a specific field or association
|
|
591
|
+
*
|
|
592
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
593
|
+
* @since 1.1.0
|
|
594
|
+
* @version 1.2.0
|
|
595
|
+
*
|
|
596
|
+
* @param {string|Array} field
|
|
597
|
+
*
|
|
598
|
+
* @return {Criteria}
|
|
599
|
+
*/
|
|
600
|
+
Criteria.setMethod(function select(field) {
|
|
601
|
+
|
|
602
|
+
var context;
|
|
603
|
+
|
|
604
|
+
if (Object.isIterable(field)) {
|
|
605
|
+
let entry;
|
|
606
|
+
|
|
607
|
+
for (entry of field) {
|
|
608
|
+
context = this.select(entry);
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
|
|
612
|
+
if (this._select) {
|
|
613
|
+
context = this._select.parse(field);
|
|
614
|
+
} else {
|
|
615
|
+
context = this.options.select.parse(field);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Selects don't always change the context
|
|
620
|
+
if (context) {
|
|
621
|
+
return context;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return this;
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Add a specific association
|
|
629
|
+
*
|
|
630
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
631
|
+
* @since 1.1.0
|
|
632
|
+
* @version 1.1.0
|
|
633
|
+
*
|
|
634
|
+
* @param {string} alias
|
|
635
|
+
*
|
|
636
|
+
* @return {Criteria}
|
|
637
|
+
*/
|
|
638
|
+
Criteria.setMethod(['contain', 'populate'], function populate(alias) {
|
|
639
|
+
|
|
640
|
+
if (Array.isArray(alias)) {
|
|
641
|
+
let i;
|
|
642
|
+
|
|
643
|
+
for (i = 0; i < alias.length; i++) {
|
|
644
|
+
this.populate(alias[i]);
|
|
645
|
+
}
|
|
646
|
+
} else {
|
|
647
|
+
let select = this._select || this.options.select;
|
|
648
|
+
|
|
649
|
+
select.addAssociation(alias);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return this;
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* How deep can we go?
|
|
657
|
+
*
|
|
658
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
659
|
+
* @since 1.1.0
|
|
660
|
+
* @version 1.1.0
|
|
661
|
+
*
|
|
662
|
+
* @param {number} amount
|
|
663
|
+
*
|
|
664
|
+
* @return {Criteria}
|
|
665
|
+
*/
|
|
666
|
+
Criteria.setMethod(function recursive(amount) {
|
|
667
|
+
this.options.recursive = amount;
|
|
668
|
+
return this;
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Set the sort
|
|
674
|
+
*
|
|
675
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
676
|
+
* @since 1.1.0
|
|
677
|
+
* @version 1.1.4
|
|
678
|
+
*
|
|
679
|
+
* @param {Array} value
|
|
680
|
+
*
|
|
681
|
+
* @return {Criteria}
|
|
682
|
+
*/
|
|
683
|
+
Criteria.setMethod(function sort(value) {
|
|
684
|
+
|
|
685
|
+
var result;
|
|
686
|
+
|
|
687
|
+
if (value) {
|
|
688
|
+
result = [];
|
|
689
|
+
|
|
690
|
+
// Parse strings
|
|
691
|
+
if (typeof value == 'string') {
|
|
692
|
+
// When it contains a space, we expect something
|
|
693
|
+
// like "_id asc"
|
|
694
|
+
if (value.indexOf(' ') > -1) {
|
|
695
|
+
result.push(value.split(' '));
|
|
696
|
+
} else {
|
|
697
|
+
// Sort ascending by default
|
|
698
|
+
result.push([value, 1]);
|
|
699
|
+
}
|
|
700
|
+
} else if (Array.isArray(value)) {
|
|
701
|
+
if (Array.isArray(value[0])) {
|
|
702
|
+
result = value;
|
|
703
|
+
} else {
|
|
704
|
+
result.push(value);
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
let keys = Object.keys(value),
|
|
708
|
+
key;
|
|
709
|
+
|
|
710
|
+
if (keys.length == 2 && ~keys.indexOf('dir') && ~keys.indexOf('field')) {
|
|
711
|
+
|
|
712
|
+
if (value.field && value.dir) {
|
|
713
|
+
result.push([value.field, value.dir]);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
} else {
|
|
717
|
+
for (key in value) {
|
|
718
|
+
result.push([key, value[key]]);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
let entry,
|
|
724
|
+
i;
|
|
725
|
+
|
|
726
|
+
for (i = 0; i < result.length; i++) {
|
|
727
|
+
entry = result[i];
|
|
728
|
+
|
|
729
|
+
if (typeof entry[1] == 'string') {
|
|
730
|
+
entry[1] = entry[1].toLowerCase();
|
|
731
|
+
|
|
732
|
+
if (entry[1] == 'asc') {
|
|
733
|
+
entry[1] = 1;
|
|
734
|
+
} else if (entry[1] == 'desc') {
|
|
735
|
+
entry[1] = -1;
|
|
736
|
+
} else {
|
|
737
|
+
throw new Error('Unable to parse sort specification "' + entry[1] + '"');
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// @TODO: implement better handling of ModelName.field sort stuff
|
|
743
|
+
// (Because at this moment, it's just ignored!)
|
|
744
|
+
for (entry of result) {
|
|
745
|
+
if (entry[0].indexOf('.') > -1) {
|
|
746
|
+
let pieces = entry[0].split('.'),
|
|
747
|
+
char = pieces[0][0];
|
|
748
|
+
|
|
749
|
+
if (char == char.toUpperCase()) {
|
|
750
|
+
pieces = pieces.slice(1);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
entry[0] = pieces.join('.');
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
} else {
|
|
758
|
+
result = null;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
this.options.sort = result;
|
|
762
|
+
|
|
763
|
+
return this;
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Normalize the criteria by filling in some values on datasources without joins
|
|
768
|
+
*
|
|
769
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
770
|
+
* @since 1.1.0
|
|
771
|
+
* @version 1.1.0
|
|
772
|
+
*
|
|
773
|
+
* @return {Pledge}
|
|
774
|
+
*/
|
|
775
|
+
Criteria.setMethod(function normalize() {
|
|
776
|
+
|
|
777
|
+
if (!this.model) {
|
|
778
|
+
return Pledge.reject(new Error('Unable to normalize criteria without model instance'));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
let that = this,
|
|
782
|
+
tasks = [],
|
|
783
|
+
i;
|
|
784
|
+
|
|
785
|
+
for (i = 0; i < this.all_expressions.length; i++) {
|
|
786
|
+
let expression = this.all_expressions[i];
|
|
787
|
+
|
|
788
|
+
if (!expression) {
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Do we need to normalize association values?
|
|
793
|
+
if (expression.requires_association_normalization) {
|
|
794
|
+
tasks.push(function doNormalize(next) {
|
|
795
|
+
expression.normalizeAssociationValues().done(next);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
let pledge = expression.normalize();
|
|
802
|
+
|
|
803
|
+
if (pledge) {
|
|
804
|
+
tasks.push(pledge);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return Function.parallel(4, tasks);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Compile to MongoDB-like query
|
|
813
|
+
*
|
|
814
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
815
|
+
* @since 1.1.0
|
|
816
|
+
* @version 1.1.0
|
|
817
|
+
*
|
|
818
|
+
* @return {Object}
|
|
819
|
+
*/
|
|
820
|
+
Criteria.setMethod(function compile() {
|
|
821
|
+
|
|
822
|
+
if (!this.datasource) {
|
|
823
|
+
throw new Error('Unable to compile criteria without a datasource target');
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return this.datasource.compileCriteria(this);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Parse an old, mongodb specific options object
|
|
831
|
+
*
|
|
832
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
833
|
+
* @since 1.1.0
|
|
834
|
+
* @version 1.1.0
|
|
835
|
+
*
|
|
836
|
+
* @param {Object} options
|
|
837
|
+
*
|
|
838
|
+
* @return {Criteria}
|
|
839
|
+
*/
|
|
840
|
+
Criteria.setMethod(function applyOldOptions(options) {
|
|
841
|
+
|
|
842
|
+
if (!options || Object.isEmpty(options)) {
|
|
843
|
+
return this;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
let entry,
|
|
847
|
+
key;
|
|
848
|
+
|
|
849
|
+
for (key in options) {
|
|
850
|
+
entry = options[key];
|
|
851
|
+
|
|
852
|
+
switch (key) {
|
|
853
|
+
case 'sort' : this.sort(entry); break;
|
|
854
|
+
case 'limit' : this.limit(entry); break;
|
|
855
|
+
case 'fields' : this.select(entry); break;
|
|
856
|
+
case 'select' : this.select(entry); break;
|
|
857
|
+
case 'recursive' : this.recursive(entry); break;
|
|
858
|
+
case 'offset' : this.skip(entry); break;
|
|
859
|
+
case 'populate' : this.populate(entry); break;
|
|
860
|
+
|
|
861
|
+
case 'conditions':
|
|
862
|
+
this.applyConditions(entry);
|
|
863
|
+
break;
|
|
864
|
+
|
|
865
|
+
default:
|
|
866
|
+
this.setOption(key, entry);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return this;
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* The Criteria Select class
|
|
875
|
+
*
|
|
876
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
877
|
+
* @since 1.1.0
|
|
878
|
+
* @version 1.1.0
|
|
879
|
+
*/
|
|
880
|
+
var Select = Function.inherits('Alchemy.Base', 'Alchemy.Criteria', function Select(criteria) {
|
|
881
|
+
// The parent criteria instance
|
|
882
|
+
this.criteria = criteria;
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Revive the given object
|
|
887
|
+
*
|
|
888
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
889
|
+
* @since 1.1.0
|
|
890
|
+
* @version 1.2.3
|
|
891
|
+
*
|
|
892
|
+
* @return {Select}
|
|
893
|
+
*/
|
|
894
|
+
Select.setStatic(function revive(data, criteria) {
|
|
895
|
+
|
|
896
|
+
if (!data) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
let result = new Select(criteria),
|
|
901
|
+
key;
|
|
902
|
+
|
|
903
|
+
result.fields = data.fields;
|
|
904
|
+
|
|
905
|
+
if (data.associations) {
|
|
906
|
+
result.associations = {};
|
|
907
|
+
|
|
908
|
+
for (key in data.associations) {
|
|
909
|
+
result.associations[key] = Select.revive(data.associations[key], criteria);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (data.association_name) {
|
|
914
|
+
result.association_name = data.association_name;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return result;
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Return object to jsonify
|
|
922
|
+
*
|
|
923
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
924
|
+
* @since 1.1.0
|
|
925
|
+
* @version 1.1.0
|
|
926
|
+
*
|
|
927
|
+
* @return {Object}
|
|
928
|
+
*/
|
|
929
|
+
Select.setMethod(function toJSON() {
|
|
930
|
+
return {
|
|
931
|
+
association_name : this.association_name,
|
|
932
|
+
associations : this.associations,
|
|
933
|
+
fields : this.fields
|
|
934
|
+
};
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Return the elements to checksum in place of this object
|
|
939
|
+
*
|
|
940
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
941
|
+
* @since 1.1.0
|
|
942
|
+
* @version 1.1.0
|
|
943
|
+
*/
|
|
944
|
+
Select.setMethod(Blast.checksumSymbol, function toChecksum() {
|
|
945
|
+
|
|
946
|
+
var result = [];
|
|
947
|
+
|
|
948
|
+
if (this.associations) {
|
|
949
|
+
result.push(this.associations);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (this.fields) {
|
|
953
|
+
result.push(this.fields);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (this.association_name) {
|
|
957
|
+
result.push(this.association_name);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (!result.length) {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
return result;
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Add an association
|
|
969
|
+
*
|
|
970
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
971
|
+
* @since 1.1.0
|
|
972
|
+
* @version 1.3.4
|
|
973
|
+
*
|
|
974
|
+
* @param {string} name
|
|
975
|
+
*
|
|
976
|
+
* @return {Select} This creates a new Select instance
|
|
977
|
+
*/
|
|
978
|
+
Select.setMethod(function addAssociation(name) {
|
|
979
|
+
|
|
980
|
+
if (!this.criteria?.model) {
|
|
981
|
+
throw new Error('Unable to select an association: this Criteria has no model info');
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
var pieces;
|
|
985
|
+
|
|
986
|
+
if (!this.associations) {
|
|
987
|
+
this.associations = {};
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (Array.isArray(name)) {
|
|
991
|
+
pieces = name;
|
|
992
|
+
} else if (name.indexOf('.') > -1) {
|
|
993
|
+
pieces = name.split('.');
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
if (pieces && pieces.length) {
|
|
997
|
+
let context = this;
|
|
998
|
+
|
|
999
|
+
while (pieces.length) {
|
|
1000
|
+
name = pieces.shift();
|
|
1001
|
+
context = context.addAssociation(name);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
return context;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (!this.associations[name]) {
|
|
1008
|
+
this.associations[name] = new Select(this.criteria);
|
|
1009
|
+
this.associations[name].association_name = name;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// Get the association data
|
|
1013
|
+
try {
|
|
1014
|
+
let info = this.criteria.model.getAssociation(name);
|
|
1015
|
+
|
|
1016
|
+
if (info) {
|
|
1017
|
+
// Make sure the localkey is added to the resultset
|
|
1018
|
+
this.requireFieldForQuery(info.options.localKey);
|
|
1019
|
+
}
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
console.warn('Failed to find "' + name + '" association for ' + this.criteria.model.model_name);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
return this.associations[name];
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Require a field for query purposes
|
|
1029
|
+
*
|
|
1030
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1031
|
+
* @since 1.2.0
|
|
1032
|
+
* @version 1.2.0
|
|
1033
|
+
*
|
|
1034
|
+
* @param {string} path
|
|
1035
|
+
*/
|
|
1036
|
+
Select.setMethod(function requireFieldForQuery(path) {
|
|
1037
|
+
|
|
1038
|
+
if (!this.query_fields) {
|
|
1039
|
+
this.query_fields = [];
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
this.query_fields.push(path);
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Add a field
|
|
1047
|
+
*
|
|
1048
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1049
|
+
* @since 1.1.0
|
|
1050
|
+
* @version 1.1.0
|
|
1051
|
+
*
|
|
1052
|
+
* @param {string} path
|
|
1053
|
+
*/
|
|
1054
|
+
Select.setMethod(function addField(path) {
|
|
1055
|
+
|
|
1056
|
+
if (!this.fields) {
|
|
1057
|
+
this.fields = [];
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
this.fields.push(path);
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Parse a path meant to add as a selection
|
|
1065
|
+
*
|
|
1066
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1067
|
+
* @since 1.1.0
|
|
1068
|
+
* @version 1.2.0
|
|
1069
|
+
*
|
|
1070
|
+
* @param {string|Object} path
|
|
1071
|
+
*
|
|
1072
|
+
* @return {Criteria|Null} A criteria object if the context has changed
|
|
1073
|
+
*/
|
|
1074
|
+
Select.setMethod(function parse(path) {
|
|
1075
|
+
|
|
1076
|
+
let context,
|
|
1077
|
+
select = this,
|
|
1078
|
+
parsed;
|
|
1079
|
+
|
|
1080
|
+
if (typeof path == 'object' && path && path.name) {
|
|
1081
|
+
|
|
1082
|
+
if (path.path) {
|
|
1083
|
+
path = path.path;
|
|
1084
|
+
} else {
|
|
1085
|
+
let obj = path;
|
|
1086
|
+
path = obj.name;
|
|
1087
|
+
|
|
1088
|
+
if (obj.association) {
|
|
1089
|
+
path = obj.association + '.' + path;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
parsed = Criteria.parsePath(path, this.criteria);
|
|
1095
|
+
|
|
1096
|
+
// Associations were found,
|
|
1097
|
+
// like "Comment._id" or "Comment.User"
|
|
1098
|
+
if (parsed.association) {
|
|
1099
|
+
let name,
|
|
1100
|
+
i;
|
|
1101
|
+
|
|
1102
|
+
for (i = 0; i < parsed.association.length; i++) {
|
|
1103
|
+
name = parsed.association[i];
|
|
1104
|
+
|
|
1105
|
+
if (this.model && this.model.name == name) {
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
select = select.addAssociation(name);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (parsed.target_path) {
|
|
1114
|
+
select.addField(parsed.target_path);
|
|
1115
|
+
} else if (parsed.association) {
|
|
1116
|
+
// When only an association was given, then the context changes
|
|
1117
|
+
context = this.criteria.augment('select');
|
|
1118
|
+
context._select = select;
|
|
1119
|
+
return context;
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Clone this select for the given criteria
|
|
1125
|
+
*
|
|
1126
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1127
|
+
* @since 1.1.0
|
|
1128
|
+
* @version 1.2.0
|
|
1129
|
+
*
|
|
1130
|
+
* @param {Criteria} criteria
|
|
1131
|
+
*
|
|
1132
|
+
* @return {Select}
|
|
1133
|
+
*/
|
|
1134
|
+
Select.setMethod(function cloneForCriteria(criteria) {
|
|
1135
|
+
|
|
1136
|
+
var clone = new Select(criteria);
|
|
1137
|
+
|
|
1138
|
+
if (this.association_name) {
|
|
1139
|
+
clone.association_name = this.association_name;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
if (this.fields && this.fields.length) {
|
|
1143
|
+
clone.fields = this.fields.slice(0);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (this.query_fields && this.query_fields.length) {
|
|
1147
|
+
clone.query_fields = this.query_fields.slice(0);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (this.associations) {
|
|
1151
|
+
let key;
|
|
1152
|
+
|
|
1153
|
+
clone.associations = {};
|
|
1154
|
+
|
|
1155
|
+
for (key in this.associations) {
|
|
1156
|
+
clone.associations[key] = this.associations[key].cloneForCriteria(criteria);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
return clone;
|
|
1161
|
+
});
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Should the given association be queried according to this select?
|
|
1165
|
+
* (The Criteria instance can also have a recursive level set)
|
|
1166
|
+
*
|
|
1167
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1168
|
+
* @since 1.1.0
|
|
1169
|
+
* @version 1.1.0
|
|
1170
|
+
*
|
|
1171
|
+
* @param {string} name
|
|
1172
|
+
*
|
|
1173
|
+
* @return {boolean}
|
|
1174
|
+
*/
|
|
1175
|
+
Select.setMethod(function shouldQueryAssociation(name) {
|
|
1176
|
+
|
|
1177
|
+
if (this.associations) {
|
|
1178
|
+
return !!this.associations[name];
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
return false;
|
|
1182
|
+
});
|