-
Notifications
You must be signed in to change notification settings - Fork 0
/
foreign-keys.js
212 lines (202 loc) · 5.68 KB
/
foreign-keys.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
'use strict';
/*
* MySQL object-relational mapping
* ===============================
*
* (C) 2014 Mark K Cowan <mark@battlesnake.co.uk>
*
* https://github.com/battlesnake/node-mysql-orm
*
* Released under GNU General Public License, Version 2
*
*/
var mysql = require('mysql');
var _ = require('underscore');
var async = require('async');
var utils = require('./utils');
var sql = require('./sql');
var names = utils.names;
var parse_args = utils.parse_args;
var ORM = { prototype: {} };
module.exports = ORM.prototype;
// foreign-keys
// ============
// Foreign key support
//
// The options query parameter is a function (format, params, callback),
// such as the mysql connection.query method. This allows intercepting of
// queries (e.g. for logging) and transactional operations even when the ORM
// is using a connection pool.
//
//
// listForeignKeys(table)
// ---------------
//
// Returns an array of names of fields in the table which have a foreign key
// constraint.
//
// ### Example
//
// var names = listForeignKeys(schema.users);
//
ORM.prototype.listForeignKeys = function (table) {
if (_(table).isString()) {
table = this.schema[table];
}
return names(table).filter(function (col) { return !!table[col].references; });
};
//
// lookupForeignId([query] field criteria [options] callback)
// ---------------
//
// Looks up the id of the parent record, identified by search criteria. Returns
// an error if no or if multiple parent records are found. In such a case, the
// second callback paremeter is zero or two for no or multiple records found.
//
// ### Example
//
// lookupForeignKey(schema.users.country, { name: 'Estonia' },
// function (err, value) { ... });
//
ORM.prototype.lookupForeignId = function () {
var args = parse_args(this, arguments, true);
var query = args.query;
var field = args.field;
var criteria = args.data;
var options = args.options;
var callback = args.callback;
var self = this;
var foreign = field.references;
async.parallel([
async.apply(sql.select, this, [foreign.$name]),
async.apply(sql.from, this, foreign.$table),
async.apply(sql.where, this, query, foreign.$table, criteria),
async.apply(sql.limit, this, { count: 2 })
],
function (err, sqlParts) {
query(_(sqlParts).compact().join('\n'), null, function (err, rows) {
if (err) {
self.warn('Error occurred while looking up foreign id');
return callback(err);
}
if (rows.length !== 1) {
return callback(new Error(self.warn(
(rows.length > 1 ? 'Multiple' : 'No') +
' foreign ids found')),
rows.length);
}
var row = rows[0];
callback(null, row[foreign.$name]);
});
});
};
//
// lookupForeignIds([query] table row [options] callback)
// ----------------
//
// Looks up all foreign key values for a row
//
// Any foreign-key fields in row which contain an object are assumed to be
// search criteria. lookupForeignId is used to fill in their corresponding id
// values. Those values of row are replaced with the id values, then the same
// (modified) row object is passed to the callback.
//
// ### Example
//
// lookupForeignIds(schema.users,
// {
// name: 'mark',
// country: { name: 'Estonia' },
// role: { name: 'admin' }
// },
// function (err, value) { ... });
//
// // value.country = 372, value.role = <some id value>
//
ORM.prototype.lookupForeignIds = function () {
var args = parse_args(this, arguments);
var query = args.query;
var table = args.table;
var row = args.data;
var callback = args.callback;
var options = args.options;
var self = this;
var cols = options.cols || this.listForeignKeys(table);
async.each(cols,
function (col, callback) {
var field = table[col];
if (!field) {
throw new Error('Field "' + col + '" not found in table "' +
table.$name + '"');
}
var value = row[col];
if (!_(value).isObject()) {
return callback(null);
}
self.lookupForeignId(query, field, value, function (err, res) {
if (err) {
return callback(err);
}
row[col] = res;
callback(null);
});
},
function (err) {
callback(err, row);
});
};
// lookupForeignValue([query] field id [options] callback)
// ----------------
// Get the data corresponding to a given ID value in a foreign key ralationship
//
ORM.prototype.lookupForeignValue = function () {
var args = parse_args(this, arguments, true);
var query = args.query;
var field = args.field;
var id = args.data;
var callback = args.callback;
var options = args.options;
var self = this;
var foreign = field.references;
var criteria = _.object([foreign.$name], [id]);
this.load(query, foreign.$table, criteria, options, function (err, res) {
if (err) {
self.warn('Error occurred while looking up foreign row');
return callback(err);
}
callback(null, res);
});
};
//
// lookupForeignValues([query] table row [options] callback)
// ----------------
// Uses lookupForeignValue to get data for fields which have foreign key
// relationships
//
ORM.prototype.lookupForeignValues = function () {
var args = parse_args(this, arguments);
var query = args.query;
var table = args.table;
var row = args.data;
var callback = args.callback;
var options = args.options;
var cols = options.cols || this.listForeignKeys(table);
var self = this;
async.each(cols,
function (col, callback) {
var field = table[col], id = row[col], foreign = field.references;
if (_(id).isNull() || _(id).isObject()) {
return callback(null);
}
self.lookupForeignValue(query, field, id, options, function (err, res) {
if (err) {
return callback(err);
}
row[col] = res;
callback(null);
});
},
function (err) {
callback(err, row);
});
};