UNPKG

10.8 kBJavaScriptView Raw
1var _ = require("underscore");
2var DataLayer = require("./DataLayer");
3
4var operatorsRE = [
5 /^=$/i,
6 /^between$/i,
7 /^not\sbetween$/i,
8 /^[<>]=?$/i,
9 /^<=?>$/i,
10 /^!=$/i,
11 /^in$/i,
12 /^not\sin$/i,
13 /^is$/i,
14 /^is\snot$/i,
15 /^like$/i,
16 /^not\slike$/i,
17 /^not$/i,
18];
19
20function isOperator(val){
21 return _.some(operatorsRE, function(re){
22 return re.test(val);
23 });
24}
25
26function wrapArrayValues(operator, values, field, mysql){
27 switch(operator.toUpperCase()){
28 case "=":
29 case "!=":
30 case ">":
31 case "<":
32 case "<>":
33 case "<=>":
34 case ">=":
35 case "<=":
36 case "NOT":
37 case "IS":
38 case "IS NOT":
39 case "LIKE":
40 case "NOT LIKE":
41 return [field, operator.toUpperCase(), mysql.escape(values[0])];
42 case "BETWEEN":
43 return Array.isArray(values[0])? [field, "BETWEEN", mysql.escape(values[0][0]), "AND", mysql.escape(values[0][1])]
44 :[field, "BETWEEN", mysql.escape(values[0]), "AND", mysql.escape(values[1])];
45 case "IN":
46 case "NOT IN":
47 default:
48 return [field, operator.toUpperCase(), "(", mysql.escape(Array.isArray(values[0])?values[0]:values ), ")"];
49 }
50}
51
52module.exports = DataLayer.extend("MySQLLayer", {
53
54
55
56
57
58 // SELECT count(distinct p.products_id) as total
59 // FROM products p
60 // LEFT JOIN manufacturers m USING(manufacturers_id)
61 // LEFT JOIN products_description pd using products_id
62 // LEFT JOIN categories c USING (categories_id)
63 // LEFT JOIN products_to_categories p2c USING (products_id)
64 // LEFT JOIN meta_tags_products_description mtpd
65 // ON mtpd.products_id= p2c.products_id
66 // AND mtpd.language_id = 1
67 // WHERE (p.products_status = 1
68 // AND pd.language_id = 1
69 // AND (
70 // (pd.products_name LIKE '%3220%'
71 // OR p.products_model LIKE '%3220%'
72 // OR m.manufacturers_name LIKE '%3220%'
73 // OR (mtpd.metatags_keywords LIKE '%3220%'
74 // AND mtpd.metatags_keywords !='')
75 // OR
76 // (mtpd.metatags_description LIKE '%3220%'
77 // AND mtpd.metatags_description !='')
78 // OR
79 // pd.products_description LIKE '%3220%'
80 // )
81 // )
82 // )
83 // OR (p.products_id=3220)
84
85 parseArguments: function(args){
86 switch(args.length){
87 case 0: return false;
88 case 1:
89 if(typeof args[0] !== "function") return false;
90 else return [undefined,{},args[0]];
91 case 2:
92 if(typeof args[1] !== "function") return false;
93 else return [args[0],{}, args[1]];
94 case 3:
95 if(typeof args[2] !== "function") return false;
96 else return [args[0],args[1], args[2]];
97 default: return false;
98 }
99 },
100
101 parseData: function(data, options){
102 var result = {options: {}};
103 var dataFields = _.pick(data, _.keys(this.fields));
104
105 if(options.count){
106 result.options.fields = "COUNT(*)";
107 }
108 else if(options.fields){
109 result.options.fields = _.intersection(this.publicFields || _.keys(this.fields), options.fields);
110 }
111 else result.options.fields = this.publicFields || "*";
112
113 if(result.options.fields.length === 0) {
114 result.options.fields = this.publicFields || "*";
115 }
116
117 if(options.limit){ result.options.limit = "LIMIT "+this.mysql.escape(options.limit); }
118 else{ result.options.limit = ""; }
119
120 if(options.count === true){
121 result.options.count = ",COUNT(*)";
122 }
123 else result.options.count = "";
124
125 if(options.order){
126 var orderFields = typeof options.order === "string"?[options.order]:options.order, direction = "";
127 if(_.last(options.order).match(/^(a|de)sc$/i)){
128 direction = options.order.pop();
129 }
130 orderFields = _.intersection(this.publicFields || _.keys(this.fields), orderFields);
131 if(orderFields.length){
132 result.options.order = "ORDER BY "+orderFields.join(", ")+" "+direction.toUpperCase();
133 }
134 else{
135 result.options.order = "";
136 }
137 }
138 else result.options.order = "";
139
140 if(options.where && typeof options.where !== "string" ){
141 var cond_fields = _.intersection(this.publicFields || _.keys(this.fields), _.keys(options.where));
142 if(cond_fields.length===0) result.options.where = "";
143 else{
144 var conditions = [];
145 var allowed_conditions = _.pick(options.where, cond_fields);
146 for(var cond_field in allowed_conditions){
147 var cond_value = allowed_conditions[cond_field];
148 if(_.some([_.isNumber, _.isString, _.isDate, _.isNull], function(iterator){ return iterator(cond_value); })){
149 conditions.push([cond_field, "=", this.mysql.escape(cond_value)].join(" "));
150 }
151 else if(Array.isArray(cond_value)){
152 if(isOperator(cond_value[0])){
153 var operator = cond_value[0];
154 var rest = cond_value.slice(1);
155 conditions.push(wrapArrayValues(operator, rest, cond_field, this.mysql).join(" "));
156 }
157 else{
158 conditions.push(wrapArrayValues("IN", cond_value, cond_field, this.mysql).join(" "));
159 }
160 }
161 else if(_.isFunction(cond_value)){
162 throw new Error("TODO - computed where condition");
163 }
164 else if(_.isObject(cond_value)){
165 throw new Error("TODO - where condition as object");
166 }
167 else result.options.where = "";
168
169 }
170 result.options.where = "WHERE "+conditions.join(" AND ");
171 }
172 }
173 else if(typeof options.where == "string"){
174 result.options.where = this.mysql.format(options.where, {options: _.extend({},options,_.pick(this, ["primaryKey", "publicFields", "tableName"])), values: data});
175 }
176 else result.options.where = "";
177
178 _.defaults(result.options, _.omit(options, ["where", "order", "limit", "fields"]));
179
180
181 result.values = data;
182 return result;
183 },
184
185 create: function(obj, options, cb){
186 var data = this.parseData(obj, options), self = this;
187 data.options.fields = _.intersection(this.publicFields || _.keys(this.fields), _.keys(obj));
188 var valuesTemplate = data.options.fields.map(function(fieldName){return "#"+fieldName}).join(",");
189 this.query("INSERT INTO @tableName (@fields) VALUES ("+valuesTemplate+");", data, function(err, result){
190 if(err) return cb(err);
191 obj[self.primaryKey] = result.insertId;
192 cb(null, obj);
193 });
194 },
195
196 find: function(pattern, options, cb){
197 if(typeof pattern === "number" || typeof pattern === "string"){
198 options.where = {}, options.limit = 1;
199 options.where[this.primaryKey] = pattern;
200 }
201 else if(Array.isArray(pattern)){
202 options.where = {}, options.limit = pattern.length;
203 options.where[this.primaryKey] = ["IN", pattern];
204 }
205 else{
206 options.where = pattern;
207 }
208 var data = this.parseData(pattern, options), self = this;
209 this.query("SELECT @fields FROM @tableName @where @order @order @limit;", data, function(err, models){
210 // avoid third argument
211 cb(err, models);
212 });
213 },
214
215 count: function(pattern, options, cb){
216 options.count = true;
217 this.find(pattern, options, function(err, result){
218 if(err) return cb(err);
219 cb(null, result[0]["COUNT(*)"]);
220 });
221 },
222
223 findOne: function(pattern, options, cb){
224 options.limit = 1;
225 return this.find(pattern, options, cb);
226 },
227
228 update: function(pattern, options, cb){
229 var self = this;
230 var fields = _.intersection(this.publicFields || _.keys(this.fields), _.keys(pattern));
231 if(fields.length === 0) return cb("Invalid fields to update");
232
233 if(fields.indexOf(this.primaryKey)!==-1){
234 options.where = _.pick(pattern, [this.primaryKey]);
235 fields = _.without(fields, this.primaryKey);
236 if(Array.isArray(pattern[this.primaryKey])) {
237 options.limit = pattern[this.primaryKey].length;
238 if(!isOperator(options.where[this.primaryKey][0])) {
239
240 options.where[this.primaryKey] = ["IN", options.where[this.primaryKey]];
241 }
242 }
243 else options.limit = 1;
244 }
245 var data = this.parseData(pattern, options), self = this;
246 var updates = fields.map(function(field){
247 return field +" = #"+field;
248 });
249 data.options.updates = updates;
250
251 this.query("UPDATE @tableName SET @updates @where @order @limit;", data, function(err, models){
252 // avoid third argument
253 cb(err, models);
254 });
255
256
257 },
258
259 delete: function(pattern, options, cb){
260
261 if(typeof pattern === "number" || typeof pattern === "string"){
262 options.where = {}, options.limit = 1;
263 options.where[this.primaryKey] = pattern;
264 }
265 else if(Array.isArray(pattern)){
266 options.where = {}, options.limit = pattern.length;
267 options.where[this.primaryKey] = ["IN", pattern];
268 }
269 else{
270 options.where = pattern;
271 }
272
273 var data = this.parseData(pattern, options), self = this;
274
275 this.query("DELETE FROM @tableName @where @order @limit;", data, function(err, models){
276 // avoid third argument
277 cb(err, models);
278 });
279 },
280
281
282}, {
283
284 setupDatabase: function(self, env, name){
285 var Prototype = this;
286 self.setupNode = function(cb){ Prototype.setupTable(self, env, function(err){
287 if(err) return cb(err);
288 env.i.do("log.sys", "DataLayer:mysql", name);
289 cb();
290 }); }
291 },
292
293 setupTable: function(instance, env, cb){
294 instance.mysql = env.engines.mysql;
295 var _ = require("underscore");
296 instance.query = function(query, data, cb){
297 _.extend(data.options, this.options, {tableName: this.tableName, primaryKey: this.primaryKey});
298 if(data.options.debug){
299 env.i.do("log.debug", ["[ MySQL debug ]",
300 [ " V V V V V V V V V V V V V V V V ","==============================================================================================>>>",
301 data.options.debug,
302 "SQL> "+instance.mysql.format(query, data),
303 "==============================================================================================<<<",""
304 ].join("\n")
305 ]);
306 delete data.options.debug;
307 }
308 return this.mysql.query(query, data, cb);
309 }
310 cb();
311 },
312
313 extend: function(name, props, statics){
314 this.setMethods(this.prototype, props);
315 return DataLayer.extend.apply(this, arguments);
316 }
317});
318
319
320
\No newline at end of file