UNPKG

7.46 kBJavaScriptView Raw
1'use strict';
2
3var util = require('util');
4var lodash = require('lodash');
5
6var Query = require('./node/query');
7var Column = require('./column');
8var TableNode = require('./node/table');
9var JoinNode = require('./node/join');
10var LiteralNode = require('./node/literal');
11var Joiner = require('./joiner');
12var ForeignKeyNode = require('./node/foreignKey');
13
14var Table = function(config) {
15 this._schema = config.schema;
16 this._name = config.name;
17 this._initialConfig = config;
18 this.columnWhiteList = !!config.columnWhiteList;
19 this.isTemporary=!!config.isTemporary;
20 this.snakeToCamel = !!config.snakeToCamel;
21 this.columns = [];
22 this.foreignKeys = [];
23 this.table = this;
24 if (!config.sql) {
25 config.sql = require('./index');
26 }
27 this.sql = config.sql;
28};
29
30Table.define = function(config) {
31 var table = new Table(config);
32 // allow hash of columns as well as array
33 if (config.columns && !util.isArray(config.columns)) {
34 var cols = [];
35
36 for (var key in config.columns) {
37 if (config.columns.hasOwnProperty(key)) {
38 var col = config.columns[key];
39 col.name = key;
40 cols.push(col);
41 }
42 }
43
44 config.columns = cols;
45 }
46
47 for (var i = 0; i < config.columns.length; i++) {
48 table.addColumn(config.columns[i]);
49 }
50
51 if(config.foreignKeys !== undefined) {
52 if(util.isArray(config.foreignKeys)) {
53 for(i = 0; i < config.foreignKeys.length; i++) {
54 table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys[i]));
55 }
56 } else {
57 table.foreignKeys.push(new ForeignKeyNode(config.foreignKeys));
58 }
59 }
60 return table;
61};
62
63Table.prototype.clone = function(config) {
64 return Table.define(lodash.extend({
65 schema: this._schema,
66 name: this._name,
67 sql: this.sql,
68 columnWhiteList: !!this.columnWhiteList,
69 snakeToCamel: !!this.snakeToCamel,
70 columns: this.columns,
71 foreignKeys: this.foreignKeys
72 }, config || {}));
73};
74
75Table.prototype.createColumn = function(col) {
76 if(!(col instanceof Column)) {
77 if(typeof col === 'string') {
78 col = { name: col };
79 }
80
81 col.table = this;
82 col = new Column(col);
83
84 // Load subfields from array into an object of form name: Column
85 if(util.isArray(col.subfields)) {
86 col.subfields = lodash.chain(col.subfields)
87 .map(lodash.bind(function (subfield) {
88 return [subfield, new Column({
89 table: this,
90 subfieldContainer: col,
91 name: subfield
92 })];
93 }, this))
94 .fromPairs()
95 .value();
96 }
97 }
98
99 return col;
100};
101
102Table.prototype.addColumn = function(col, options) {
103 col = this.createColumn(col);
104 options = lodash.extend({
105 noisy: true
106 }, options || {});
107
108 if(this.hasColumn(col)) {
109 if (options.noisy) {
110 throw new Error('Table ' + this._name + ' already has column or property by the name of ' + col.name);
111 } else {
112 return this;
113 }
114 } else if(!!this[col.name] && (process.env.NODE_ENV === 'debug')) {
115 console.log('Please notice that you have just defined the column "' + col.name + '". In order to access it, you need to use "table.getColumn(\'' + col.name + '\');"!');
116 }
117 this.columns.push(col);
118
119 function snakeToCamel(snakeName) {
120 return snakeName.replace(/[\-_]([a-z])/g, function(m, $1){ return $1.toUpperCase(); });
121 }
122
123 var property = col.property = col.property || (this.snakeToCamel ? snakeToCamel(col.name) : col.name);
124 this[property] = this[property] || col;
125 return this;
126};
127
128Table.prototype.hasColumn = function(col) {
129 var columnName = col instanceof Column ? col.name : col;
130 return this.columns.some(function(column) {
131 return column.property === columnName || column.name === columnName;
132 });
133};
134
135Table.prototype.getColumn =
136Table.prototype.get =
137function(colName) {
138 for(var i = 0; i < this.columns.length; i++) {
139 var col = this.columns[i];
140 if (colName === col.property || colName === col.name) {
141 return col;
142 }
143 }
144 if(this.columnWhiteList)
145 return null;
146 throw new Error('Table ' + this._name + ' does not have a column or property named ' + colName);
147};
148
149Table.prototype.getSchema = function() {
150 return this._schema;
151};
152
153Table.prototype.setSchema = function(schema) {
154 this._schema = schema;
155};
156
157Table.prototype.getName = function() {
158 if (this.sql && this.sql.dialectName=="mssql" && this.isTemporary) return "#"+this._name;
159 return this._name;
160};
161
162Table.prototype.star = function(options) {
163 options = options || {};
164 if (options.prefix) {
165 return this.columns.map(function(column) {
166 return this[column.name].as(options.prefix + column.name);
167 }.bind(this));
168 }
169
170 return new Column({table: this, star: true});
171};
172
173Table.prototype.literal = function(literal) {
174 return new LiteralNode(literal);
175};
176
177Table.prototype.count = function(alias) {
178 var name = this.alias || this._name,
179 col = new Column({table: this, star: true});
180 // ColumnNode
181 return col.count(alias || name + '_count');
182};
183
184Table.prototype.select = function() {
185 // create the query and pass it off
186 var query = new Query(this);
187 if (arguments.length === 0) {
188 query.select.call(query, this.star());
189 } else {
190 query.select.apply(query, arguments);
191 }
192 return query;
193};
194
195Table.prototype.subQuery = function(alias) {
196 // create the query and pass it off
197 var query = new Query(this);
198 query.type = 'SUBQUERY';
199 query.alias = alias;
200 query.join = function(other) {
201 return new JoinNode('INNER', this.toNode(), other.toNode(), other);
202 };
203 return query;
204};
205
206Table.prototype.insert = function() {
207 var query = new Query(this);
208 if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){
209 query.select.call(query, this.star());
210 query.where.apply(query,["1=2"]);
211 } else {
212 query.insert.apply(query, arguments);
213 }
214 return query;
215};
216
217Table.prototype.replace = function() {
218 var query = new Query(this);
219 if(!arguments[0] || (util.isArray(arguments[0]) && arguments[0].length === 0)){
220 query.select.call(query, this.star());
221 query.where.apply(query,["1=2"]);
222 } else {
223 query.replace.apply(query, arguments);
224 }
225 return query;
226};
227
228Table.prototype.toNode = function() {
229 return new TableNode(this);
230};
231
232Table.prototype.join = function(other) {
233 return new JoinNode('INNER', this.toNode(), other.toNode(), other);
234};
235
236Table.prototype.leftJoin = function(other) {
237 return new JoinNode('LEFT', this.toNode(), other.toNode());
238};
239
240// auto-join tables based on column intropsection
241Table.prototype.joinTo = function(other) {
242 return Joiner.leftJoin(this, other);
243};
244
245Table.prototype.as = function(alias) {
246 // TODO could this be cleaner?
247 var t = Table.define(this._initialConfig);
248 t.alias = alias;
249 return t;
250};
251
252// called in shorthand when not calling select
253Table.prototype.__defineGetter__("nodes", function() {
254 return this.select(this.star()).nodes;
255});
256
257Table.prototype.and = function() {
258 var query = new Query(this);
259 query.where.apply(query, arguments);
260 return query;
261};
262
263Table.prototype.indexes = function() {
264 return new Query(this).indexes();
265};
266
267var queryMethods = [
268 'alter',
269 'create',
270 'delete',
271 'drop',
272 'from',
273 'limit',
274 'offset',
275 'or',
276 'order',
277 'truncate',
278 'update',
279 'where'
280];
281
282queryMethods.forEach(function (method) {
283 Table.prototype[method] = function () {
284 var query = new Query(this);
285 query[method].apply(query, arguments);
286 return query;
287 };
288});
289
290module.exports = Table;