README.md
1# node-mysql
2
3An enhancement to the mysql lib to make it a bit easier to use. Based on the existing funtionalities of (npm) mysql, it also
4
5* Handles transactions.
6* Provides a simple ORM, which
7 * Detects table schema automatically.
8 * Provides handy functions for creating and updating rows.
9 * Handles optimistic lock through versioning transparently.
10 * Maintains "date_created" and "last_updated" automatically.
11 * Provides database row level lock functionality.
12
13## Install
14
15```text
16npm install node-mysql
17```
18
19### Dependencies
20
21```json
22 "dependencies": {
23 "better-js-class": "*",
24 "cps": "*",
25 "mysql": "*",
26 "underscore": "*"
27 }
28```
29
30## Use
31
32```javascript
33var db = require('node-mysql');
34var DB = db.DB;
35var BaseRow = db.Row;
36var BaseTable = db.Table;
37```
38
39## APIs
40
41* DB
42 * [new DB](#new-DB)
43 * [db.connect](#db-connect)
44 * [db.transaction](#db-transaction)
45 * [db.cursor](#db-cursor)
46 * [db.end](#db-end)
47 * [db.add](#db-add)
48 * [db.get](#db-get)
49 * [DB.format](#DB-format)
50* Table
51 * [new Table](#new-Table)
52 * [table.create](#table-create)
53 * [table.clone](#table-clone)
54 * [table.find](#table-find)
55 * [table.findById](#table-findById)
56 * [table.lockById](#table-lockById)
57 * [table.findAll](#table-findAll)
58 * [table.baseQuery](#table-baseQuery)
59 * [table.linksTo](#table-linksTo)
60 * [table.linkedBy](#table-linkedBy)
61 * [table.relatesTo](#table-relatesTo)
62* Row
63 * [new Row](#new-Row)
64 * [row.update](#row-update)
65 * [row.updateWithoutOptimisticLock](#row-updateWithoutOptimisticLock)
66 * [row.get](#row-get)
67 * [row.getId](#row-getId)
68 * [row.linksTo](#row-linksTo)
69 * [row.linkedBy](#row-linkedBy)
70 * [row.relatesTo](#row-relatesTo)
71
72<a name="new-DB"/>
73### new DB(conf)
74
75Please refer to the the [connection pool conf](https://github.com/felixge/node-mysql#pooling-connections) in mysql package for the format of "conf".
76
77__Example__
78
79```javascript
80var box = new DB({
81 host : 'localhost',
82 user : 'root',
83 password : '',
84 database : 'prod_clone'
85});
86```
87
88Beyond the configuration fields provided by mysql package, there are two additional configuration fields that can be used in "conf":
89
90* useTransaction
91* useCursor
92
93
94#### useTransaction
95
96Only if "useTransaction" is provided can "[db.transaction](#db-transaction)" API be called. Otherwise, calls to "[db.transaction](#db-transaction)" will throw an error with the message "transation-not-setup-error". The "useTransaction" field itself is an configuration object that overrides the fields in "conf" to set up a connection pool for transactions. For instance:
97
98__Example__
99
100```javascript
101var box = new DB({
102 host : 'localhost',
103 user : 'root',
104 password : '',
105 database : 'prod_clone',
106 connectionLimit: 50,
107 useTransaction: {
108 connectionLimit: 1
109 }
110});
111```
112
113will allow the db object "box" to use "box.transaction" API, with a connection pool for transactions set up the same way as the normal connection pool except for the connectionLimit field being overridden to 1. So in "box", there are two mysql connection pools, for normal db requests and transactional db requests, repectively. The normal connection pool's configuration is:
114
115```javascript
116{
117 host : 'localhost',
118 user : 'root',
119 password : '',
120 database : 'prod_clone',
121 connectionLimit: 50
122}
123```
124
125while the transactional connection pool's configuration is:
126
127```javascript
128{
129 host : 'localhost',
130 user : 'root',
131 password : '',
132 database : 'prod_clone',
133 connectionLimit: 1
134}
135```
136
137#### useCursor
138
139Similarly to "useTransaction", only if "useCursor" is provided can "[db.cursor](#db-cursor)" API be called. Otherwise, calls to "[db.cursor](#db-cursor)" will throw an exception with the error message "cursor-not-setup-error". The field "useCursor" is very similar to the field "useTransaction", with the only difference that it is for setting up the mysql connection pool for cursors rather than transactions. "useCursor" is also an overriding object based upon the connection pool configuration for normal connections. For instance:
140
141__Example__
142
143```javascript
144var box = new DB({
145 host : 'localhost',
146 user : 'root',
147 password : '',
148 database : 'prod_clone',
149 connectionLimit: 50,
150 useCursor: {
151 connectionLimit: 1
152 }
153});
154```
155
156will allow the API "box.cursor" to be called, with a connection pool for cursors set up the same way as the normal connection pool except for the connectionLimit field being overridden to 1. So in "box", there are two mysql connection pools, for normal db requests and cursor db requests, repectively. The normal connection pool's configuration is:
157
158```javascript
159{
160 host : 'localhost',
161 user : 'root',
162 password : '',
163 database : 'prod_clone',
164 connectionLimit: 50
165}
166```
167
168while the cursor connection pool's configuration is:
169
170```javascript
171{
172 host : 'localhost',
173 user : 'root',
174 password : '',
175 database : 'prod_clone',
176 connectionLimit: 1
177}
178```
179
180#### Use Both
181
182"useTransaction" and "useCursor" can be used together:
183
184__Example__
185
186```javascript
187var box = new DB({
188 host : 'localhost',
189 user : 'root',
190 password : '',
191 database : 'prod_clone',
192 connectionLimit: 50,
193 useTransaction: {
194 connectionLimit: 20
195 },
196 useCursor: {
197 connectionLimit: 1
198 }
199});
200```
201
202This will allow all of the three APIs, "box.connect", "box.transaction" and "box.cursor" to be called. In this case, box hold three connection pools, for normal connections, transactional connections and cursor connections, respectively. The normal connection pool is configured as:
203
204```javascript
205{
206 host : 'localhost',
207 user : 'root',
208 password : '',
209 database : 'prod_clone',
210 connectionLimit: 50
211}
212```
213
214the transactional connection pool is configured as:
215
216```javascript
217{
218 host : 'localhost',
219 user : 'root',
220 password : '',
221 database : 'prod_clone',
222 connectionLimit: 20
223}
224```
225
226and the cursor connection pool is configured as:
227
228```javascript
229{
230 host : 'localhost',
231 user : 'root',
232 password : '',
233 database : 'prod_clone',
234 connectionLimit: 1
235}
236```
237
238
239<a name="db-connect">
240### db.connect(procedure, callback)
241
242The procedure is a function of the type:
243
244```javascript
245function(connection, callback) {
246 // work with the database connection
247}
248```
249
250__Example__
251
252```javascript
253var basicTest = function(cb) {
254 box.connect(function(conn, cb) {
255 cps.seq([
256 function(_, cb) {
257 conn.query('select * from users limit 1', cb);
258 },
259 function(res, cb) {
260 console.log(res);
261 cb();
262 }
263 ], cb);
264 }, cb);
265};
266```
267
268<a name="db-transaction"/>
269### db.transaction(db_connection, procedure, callback)
270
271The procedure is a function of the type:
272
273```javascript
274function(connection, callback) {
275 // work with the database connection
276}
277```
278
279Note that db.transaction takes one more arguemnt than the db.connect,
280which is a database connection object. If this connection object is
281already a transactional, then it will be used directly in the provided
282procedure. Otherwise, the connection will be "made transactional" and
283then used in the provided procedure.
284
285__Example__
286
287```javascript
288var txnTest = function(cb) {
289 var add2Rows = function(conn, b, cb) {
290 dw.transaction(
291 conn/*This is a non-transactional connection.*/,
292 function(
293 conn/*A new transactional connection is
294 created to handle the transactional session.*/,
295 cb
296 ) {
297 cps.seq([
298 function(_, cb) {
299 Model.Table.create(conn, getSampleDto(), cb);
300 },
301 function(_, cb) {
302 dw.transaction(
303 conn/*This is already a transactional connection.*/,
304 function(
305 conn/*No new transactional connection created.
306 This connection is the same one as
307 in the calling context*/,
308 cb
309 ) {
310 console.log(conn.__transaction__)
311 Model.Table.create(conn, getSampleDto(), cb);
312 },
313 cb
314 );
315 },
316 function(_, cb) {
317 if (b) {
318 cb(null, "Commit");
319 } else {
320 throw new Error("Roll back");
321 }
322 }
323 ], cb);
324 },
325 cb
326 );
327 };
328
329 dw.connect(function(conn, cb) {
330 /* Uncommenting the following line will merge the two calls
331 to add2Rows into one transaction. */
332 // dw.transaction(conn, function(conn, cb) {
333 cps.seq([
334 function(_, cb) {
335 add2Rows(conn, true, cb);
336 },
337 function(_, cb) {
338 add2Rows(conn, true, cb);
339 }
340 ], cb);
341 // }, cb);
342 }, cb);
343};
344```
345
346<a name="db-cursor"/>
347### db.cursor(query_string, procedure, callback)
348
349This API can be used to cursor thought the (potentially very long list
350of) results of a query. The procedure parameter is the operation to
351be applied to each row in the results of the query. Please note the following:
352
353* db.cursor will create a separate db connection for cursoring
354 purpose. That is why this API does not take a db_connection in the
355 parameter list, unlike most of the other db related APIs.
356
357* If there is an exception thrown out of the row handling procedure,
358 the cursoring will be stopped and the exception will be thrown out
359 to the top level callback (the 3rd parameter to db.cursor). If you
360 intend for the cursor to go over all the results, you need to catch
361 any exceptions in the row handling procedure (using cps.rescue).
362
363__Example__
364
365```javascript
366// cursor through all the users and update the active status to "1"
367var cursorTest = function(cb) {
368 db.connect(function(conn, cb) {
369 var q = 'select * from users';
370
371 db.cursor(q, function(row, cb) {
372 cps.rescur({
373 'try': function() {
374 cps.seq([
375 function(_, cb) {
376 var user = new User.Row(row);
377 user.update(conn, {active: 1}, cb);
378 },
379 function(res, cb) {
380 console.log(res);
381 cb();
382 }
383 ], cb);
384 },
385 'catch': function(err, cb) {
386 console.log(err);
387 cb();
388 }
389 }, cb);
390 }, cb);
391 }, cb);
392};
393```
394
395
396
397
398<a name="db-end" />
399### db.end();
400
401This function destructs the db object.
402
403<a name="db-add"/>
404
405### db.add(config);
406
407This function is used to define a model that belongs to the db object. The model config object is of the following format:
408
409```json
410{
411 "name": {
412 "type": "String",
413 "description": "the name of the table"
414 },
415 "idFieldName": {
416 "type": "String",
417 "optional": true,
418 "default": "id",
419 "description": "the name of the primary id column"
420 },
421 "versionFieldName": {
422 "type": "String",
423 "optional": true,
424 "default": "version",
425 "description": "optimistic lock version"
426 },
427 "createdFieldName": {
428 "type": "String",
429 "optional": true,
430 "default": "date_created",
431 "description": "creation time of the row"
432 },
433 "updatedFieldName": {
434 "type": "String",
435 "optional": true,
436 "default": "last_updated",
437 "description": "last update time of the row"
438 },
439 "Row": {
440 "type": "Object",
441 "optional": true,
442 "default": "{}",
443 "description": "the overriding method definition for the Row class of this model"
444 },
445 "Table": {
446 "type": "Object",
447 "optional": true,
448 "default": "{}",
449 "description": "the overriding method definition for the Table class of this model"
450 }
451}
452```
453
454Note that db.add returns a Table object, which can be further chained
455with linking/joining functions such as [linksTo](#table-linksTo),
456[linkedBy](#table-linkedBy) and [relatesTo](#table-relatesTo).
457
458__Example__
459
460```javascript
461db.add({
462 name: 'orders',
463 idFieldName: 'order_id',
464 Row: {
465 getFirstOrderItem: function(conn, cb) {
466 var me = this;
467
468 cps.seq([
469 function(_, cb) {
470 me.linkedBy(conn, 'items', cb);
471 },
472 function(items, cb) {
473 cb(null, items[0]);
474 }
475 ], cb);
476 }
477 },
478 Table: {
479 createOrderUsingCoupon: function(conn, dto, coupon, cb) {
480 dto['coupon_id'] = coupon.getId();
481 this.create(conn, dto, cb);
482 }
483 }
484})
485 .linkedBy({
486 name: 'items',
487 key: 'order_id',
488 table: 'order_items'
489 })
490 .linksTo({
491 name: 'user',
492 key: 'user_id',
493 table: 'users'
494 })
495 .linksTo({
496 name: 'coupon',
497 key: 'coupon_id',
498 table: 'coupons'
499 })
500 .linksTo({
501 name: 'credit_card',
502 key: 'credit_card_id',
503 table: 'credit_cards'
504 })
505;
506
507```
508
509In this example:
510
511* We created a model for the table "orders" in the database.
512* We add a method getFirstOrderItem into the Row class of this model.
513* We add a method createOrderWithCoupon into the Table class of this model.
514* We follow the foreign key relations of the "orders" table to define some link relations.
515
516<a name="db-get"/>
517
518### db.get(table_name)
519
520Once you use db.add to create a model in the db object, you can then
521use db.get to retrieve it by name. The return value of
522db.get if a object of the following format:
523
524```json
525{
526 "Row": {
527 "type": "Row object"
528 },
529 "Table": {
530 "type": "Table object"
531 }
532}
533```
534
535__Example__
536
537```javascript
538var Order = db.get('orders');
539var Coupon = db.get('coupons');
540
541db.connect(function(conn, cb) {
542 cps.seq([
543 function(_, cb) {
544 Coupon.Table.findByCode(conn, '10-percent-off', cb);
545 }
546 function(coupon, cb) {
547 var dto = {/*dto data*/};
548 Order.createOrderWithCoupon(conn, dto, coupon, cb);
549 },
550 function(order, cb) {
551 order.getFirstOrderItem(conn, cb);
552 },
553 function(firstItem, cb) {
554 console.log(firstItem);
555 cb()
556 }
557 ], cb);
558}, cb);
559```
560
561
562<a name="DB-format" />
563### DB.format(query_string, variable_bindings)
564
565This is a wrapper of the query string formatting functionality
566provided by the mysql package. Note that this is a global static
567method defined on the class DB. It is NOT an instance method defined
568a a DB instance db.
569
570__Example__
571
572```javascript
573DB.format('select * from users where id = ?' [userId]);
574```
575<a name="new-Table"/>
576### new Table(table_config)
577
578The table config schema is defined as follows:
579
580```json
581{
582 "name": {
583 "type": "String",
584 "optional": false,
585 "description": "the name of the database table"
586 },
587 "idFieldName": {
588 "type": "String",
589 "optional": true,
590 "default": "id",
591 "description": "the name of the primary id column"
592 },
593 "versionFieldName": {
594 "type": "String",
595 "optional": true,
596 "default": "version",
597 "description": "optimistic lock version"
598 },
599 "createdFieldName": {
600 "type": "String",
601 "optional": true,
602 "default": "date_created",
603 "description": "creation time of the row"
604 },
605 "updatedFieldName": {
606 "type": "String",
607 "optional": true,
608 "default": "last_updated",
609 "description": "last update time of the row"
610 },
611 "rowClass": {
612 "type": "Row class",
613 "optional": false,
614 "description": "the Row class of this table"
615 },
616 "db": {
617 "type": "DB class instance",
618 "optional": false,
619 "description": "the DB instance that the table belongs to"
620 }
621}
622```
623
624See [here](#row-table-instantiation) for an example of creating a table.
625
626
627<a name="table-create"/>
628
629### table.create(database_connection, data_object, callback)
630
631The callback here takes a Row object as result.
632
633__Example__
634
635```javascript
636var createTest = function(cb) {
637 dw.connect(function(conn, cb) {
638 cps.seq([
639 function(_, cb) {
640 User.Table.create(conn, {
641 first_name: 'Hannah',
642 last_name: 'Mckay',
643 gender: 'female'
644 // ....
645 }, cb);
646 },
647 function(user, cb) { // user is an object of the class User.Row
648 console.log(user.get('first_name')); // print out 'Hannah'
649 cb();
650 }
651 ], cb);
652 }, cb);
653};
654```
655
656In the input data object, please do NOT specify the following fields:
657
658* primary ID
659* date_created
660* last_udpated
661* version
662
663All of the these fields will be filled by the invocation to table.create.
664
665
666<a name="table-clone"/>
667
668### table.clone(database_connection, data_object, callback)
669
670This API is very similar to table.create. The key difference is that
671it does not mask out any data field carried in data_object. Instead,
672it literally uses every thing in data_object to create a new row. In
673other words, it'll honor the values of the following fields in
674data_object:
675
676* primary ID
677* date_created
678* last_udpated
679* version
680
681This API can be useful when one attempts to clone a row in a table
682literally to another table (which might be in another database).
683
684<a name="table-find"/>
685### table.find(database_connection, query_string, callback)
686
687This function is not too different from doing a query directly on a
688database connection. The only extra thing it does is to turn the
689result from a list of simple hash objects to a list of Row objects of
690the corresponding table's "rowClass".
691
692__Example__
693
694```javascript
695dw.connect(function(conn, cb) {
696 var o;
697 cps.seq([
698 function(_, cb) {
699 User.Table.find(conn, 'select * from users', cb);
700 },
701 function(users, cb) { // users is a list of user object of the class User.Row
702 console.log(users[0]); // print the information of the first user
703 cb();
704 }
705 ], cb);
706}, cb);
707```
708
709<a name="table-findById"/>
710### table.findById(database_connection, row_id, callback)
711
712This is simply a short-hand for:
713
714```javascript
715cps.seq([
716 function(_, cb) {
717 table.find(
718 conn,
719 DB.format('select * from table_name where primary_id = ?', [row_id]),
720 cb
721 );
722 },
723 function(res, cb) {
724 cb(res[0]);
725 }
726], cb);
727```
728
729It finds a row in a table by its primary ID and returns a single row
730object of the table's corresponding rowClass.
731
732<a name="table-lockById"/>
733### table.lockById(database_connection, row_id, callback)
734
735This function does the same thing as findById and additionally, it
736locks the corresponding row for an atomic update. lockById can ONLY
737be used in a transaction context. Without a transaction context, it
738behaves the same as findById. Once a row is locked in one
739transaction, attempts of locking the same row in other transactions
740will hang until the current transaction either commits or rolls back,
741which release the current lock.
742
743__Example__
744
745```javascript
746var lockTest = function(cb) {
747 var exclusiveUpdate = function(conn, delay, value, cb) {
748 dw.transaction(null, function(conn, cb) {
749 cps.seq([
750 function(_, cb) {
751 Model.Table.lockById(conn, 1, cb);
752 },
753 function(res, cb) {
754 setTimeout(function() {
755 cb(null, res);
756 }, delay);
757 },
758 function(row, cb) {
759 row.update(conn, {'subscription_status': value}, cb);
760 },
761 function(res, cb) {
762 cb();
763 }
764 ], cb)
765 }, cb);
766
767 };
768
769 var conn;
770
771 dw.transaction(conn, function(conn, cb) {
772 cps.seq([
773 function(_, cb) {
774 cps.parallel([
775 function(cb) {
776 exclusiveUpdate(conn, 2000, 'foo1', cb);
777 },
778 function(cb) {
779 exclusiveUpdate(conn, 0, 'bar1', cb);
780 }
781 ], cb);
782 },
783 function(res, cb) {
784 console.log(res);
785 cb();
786 }
787 ], cb);
788 }, cb);
789};
790```
791
792In this example, two threads are executed in parallel. The thread of
793setting value "bar1" will be block by the thread of setting value
794"foo1".
795
796<a name="table-findAll"/>
797### table.findAll(database_connection, callback)
798
799This finds all the rows in a table.
800
801<a name="table-baseQuery"/>
802### table.baseQuery(query_string, variable_bindings)
803
804This is a short-hand for:
805
806```javascript
807DB.format('select * from table_name' + query_string, variable_bindings);
808```
809
810It simply prepend a partial string indicating from which table the
811query is being performed. This might come handy in many cases.
812
813<a name="table-linksTo"/>
814### table.linksTo(config)
815
816The config object has the following schema:
817
818```json
819{
820 "name": {
821 "type": "String",
822 "description": "The name of the field to add to the row's data."
823 },
824 "key": {
825 "type": "String",
826 "description": "The key that belongs to the current table and links to another table."
827 },
828 "table": {
829 "type": "String",
830 "description": "The name of the table that the current table links to."
831 }
832}
833```
834
835__Example__
836
837```javascript
838 Order.Table
839 .linksTo({
840 name: 'credit_card',
841 key: 'credit_card_id'
842 table: 'credit_cards'
843 })
844 .linksTo({
845 name: 'shipping_address',
846 key: 'shipping_address_id'
847 table: 'addresses'
848 })
849 ;
850```
851
852Note that for "linksTo", the (join) key is on the current table. Once
853a "linksTo" is set up, a row object that corresponds to this table can
854call the "linksTo" method to pull more (associated) data into the row.
855See examples [here](#row-linksTo).
856
857<a name="table-linkedBy"/>
858### table.linkedBy(config)
859
860The config object has the following schema:
861
862```json
863{
864 "name": {
865 "type": "String",
866 "description": "The name of the field to add to the row's data."
867 },
868 "key": {
869 "type": "String",
870 "description": "The key that belongs to the other table and links to the current table."
871 },
872 "table": {
873 "type": "String",
874 "description": "The name of the table that the current table is linked by."
875 }
876}
877```
878
879__Example__
880
881```javascript
882 Order.Table
883 .linkedBy({
884 name: 'items',
885 table: OrderItem.Table,
886 key: 'order_id'
887 })
888 ;
889```
890
891Once a "linkedBy" is set up on a table, a row object corresponding to
892this table can call the "linkedBy" method to pull more (associated)
893data into the row. See examples [here](#row-linkedBy).
894
895<a name="table-relatesTo"/>
896
897### table.relatesTo(config)
898
899The config object has the following schema:
900
901```json
902{
903 "name": {
904 "type": "String",
905 "description": "The name of the field to add to the row's data."
906 },
907 "through": {
908 "type": "String",
909 "description": "The name of the through table, which joins both the current table and the target table."
910 },
911 "leftKey": {
912 "type": "String",
913 "description": "The key that belongs to the current table and joins with the through table."
914 },
915 "table": {
916 "type": "String",
917 "description": "The name of the target table that the current table is joining thourgh the through-table."
918 },
919 "rightKey": {
920 "type": "String",
921 "description": "The key that belongs to the target table and joins with the through table."
922 }
923}
924```
925
926__Example__
927
928```javascript
929 Order.Table
930 .relatesTo({
931 name: 'coupons',
932 leftKey: 'order_id',
933 through: 'order_coupons',
934 rightKey: 'coupon_id',
935 table: 'coupons'
936 })
937 ;
938```
939
940table.relatesTo is designed to represent ORM of a many-to-many
941relation. Once a "relatesTo" is set up on a table, a row object
942corresponding to this table can call the "relatesTo" method to pull
943more (associated) data into the row. See examples
944[here](#row-relatesTo).
945
946<a name="new-Row" />
947### new Row(row_data)
948
949After having a concrete Row class, row instances can be created using
950it. The row_data parameter is an object mapping database table column
951names to their corresponding values.
952
953__Example__
954
955```javascript
956new User.Row({
957 first_name: 'Hannah',
958 last_name: 'Mckay',
959 gender: 'female'
960 //....
961});
962```
963<a name="row-update"/>
964
965### row.update(database_connection, update_object, callback)
966
967This function will set the following column automatically:
968
969* last_updated. This field will be set to the present time stamp.
970* version. This field will be increased.
971
972Other than these columns, only columns listed in the update_object will be updated.
973
974__Example__
975
976```javascript
977var findAndUpdateTest = function(cb) {
978 dw.connect(function(conn, cb) {
979 cps.seq([
980 function(_, cb) {
981 User.Table.findById(conn, id, cb);
982 },
983 function(user, cb) {
984 var dto = {
985 'last_name': 'Morgan'
986 };
987 user.update(conn, dto, cb);
988 }
989 ], cb);
990 }, cb);
991};
992```
993
994<a name="row-updateWithoutOptimisticLock"/>
995### row.updateWithoutOptimisticLock(database_connection, update_object, callback)
996
997This function is the same as row.update with only one difference: it
998does not care about the optimistic lock version field. It neither
999looks at this field nor update field. This might be useful
1000ocasionally when optimistic lock functionality needs to be overriden.
1001
1002
1003<a name="row-get"/>
1004### row.get(column_name)
1005
1006Get the value of a certain column from the row object.
1007
1008<a name="row-getId"/>
1009### row.getId()
1010
1011Get the primary ID of the row object.
1012
1013<a name="row-linksTo"/>
1014### row.linksTo(database_connection, field_name, callback)
1015
1016Given a "linksTo" setup in the corresponding "Table" object, row.linksTo pulls further relevant data into the row.
1017
1018__Example__
1019
1020```javascript
1021var order;
1022
1023cps.seq([
1024 function(_, cb) {
1025 Order.Table.findById(conn, id, cb);
1026 },
1027 function(res, cb) {
1028 order = res;
1029 order.linksTo(conn, 'credit_card', cb);
1030 },
1031 function(_, cb) {
1032 order.linksTo(conn, 'shipping_address', cb);
1033 },
1034 function(_, cb) {
1035 console.log(order.get('credit_card').getId());
1036 console.log(order.get('shipping_address').getId());
1037 cb();
1038 }
1039], cb);
1040```
1041
1042<a name="row-linkedBy"/>
1043### row.linkedBy(database_connection, field_name, callback)
1044
1045Given a "linkedBy" setup in the corresponding "Table" object, row.linkedBy pulls further relevant data into the row.
1046
1047__Example__
1048
1049```javascript
1050var order;
1051
1052cps.seq([
1053 function(_, cb) {
1054 Order.Table.findById(conn, id, cb);
1055 },
1056 function(res, cb) {
1057 order = res;
1058 order.linkedBy(conn, 'items', cb);
1059 },
1060 function(items, cb) {
1061 // items will both be bound to the return value and be assigned to the 'items' field.
1062 console.log(items);
1063 console.log(order.get('items'));
1064 cb();
1065 }
1066], cb);
1067```
1068
1069<a name="row-relatesTo"/>
1070### row.relatesTo(database_connection, field_name, callback)
1071
1072Given a "relatesTo" setup in the corresponding "Table" object, row.relatesTo pulls further relevant data into the row.
1073
1074__Example__
1075
1076```javascript
1077var order;
1078
1079cps.seq([
1080 function(_, cb) {
1081 Order.Table.findById(conn, id, cb);
1082 },
1083 function(res, cb) {
1084 order = res;
1085 order.relatesTo(conn, 'coupons', cb);
1086 },
1087 function(coupons, cb) {
1088 // coupons will both be bound to the return value and be assigned to the 'coupons' field.
1089 console.log(coupons);
1090 console.log(order.get('coupons'));
1091 cb();
1092 }
1093], cb);
1094```
1095