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