| | 1 | = emDB = |
| | 2 | |
| | 3 | {{{emDB}}} is a service that provides a database abstraction layer for EdenMobile. |
| | 4 | |
| | 5 | == Introduction == |
| | 6 | |
| | 7 | === Tables === |
| | 8 | |
| | 9 | A '''Table''' object represents a database table, and can be accessed: |
| | 10 | |
| | 11 | ...directly from the {{{emDB}}} service (asynchronously): |
| | 12 | |
| | 13 | {{{#!js |
| | 14 | emDB.table('tablename').then(function(table) { |
| | 15 | ... |
| | 16 | }); |
| | 17 | }}} |
| | 18 | |
| | 19 | ...or, where a {{{Resource}}} is available (synchronously): |
| | 20 | |
| | 21 | {{{#!js |
| | 22 | // Access the table of a Resource |
| | 23 | var table = resource.table; |
| | 24 | |
| | 25 | // Access any table via a Resource |
| | 26 | var table = resource.getTable('tablename'); |
| | 27 | }}} |
| | 28 | |
| | 29 | === Fields === |
| | 30 | |
| | 31 | To access a Field in the Table, the Table object provides the {{{$}}} method: |
| | 32 | |
| | 33 | {{{#!js |
| | 34 | var field = table.$('fieldname'); |
| | 35 | }}} |
| | 36 | |
| | 37 | While it is also possible to access a Field like {{{table.fields['fieldname']}}}, this would not map server-side super-IDs (e.g. {{{pe_id}}}) to the EdenMobile {{{em_object_id}}}. Therefore the {{{$}}} method is preferrable, especially in cases where the field name is a variable. |
| | 38 | |
| | 39 | === Expressions === |
| | 40 | |
| | 41 | With Tables and Fields, it is possible to construct query '''Expressions''', e.g. |
| | 42 | |
| | 43 | {{{#!js |
| | 44 | var query = field.is(5); |
| | 45 | }}} |
| | 46 | |
| | 47 | AND and OR are functions of query expressions: |
| | 48 | {{{#!js |
| | 49 | var query = field.is(5).or(field.greaterThan(6)); |
| | 50 | }}} |
| | 51 | |
| | 52 | If there are multiple operand expressions in an AND or OR, the global {{{allOf()}}} and {{{anyOf()}}} functions can used to improve readability: |
| | 53 | {{{#!js |
| | 54 | // Multiple operand expressions: |
| | 55 | var query = field.is(5).or(field.is(6).or(field.is(7))); |
| | 56 | |
| | 57 | // Alternative with anyOf: |
| | 58 | var query = anyOf(field.is(5), field.is(6), field.is(7)); |
| | 59 | }}} |
| | 60 | |
| | 61 | Both {{{anyOf}}} and {{{allOf}}} can of course be nested indefinitely. |
| | 62 | |
| | 63 | === Sets === |
| | 64 | |
| | 65 | With a table and a query expression, a '''Set''' can be constructed like: |
| | 66 | |
| | 67 | {{{#!js |
| | 68 | var set = table.where(query); |
| | 69 | }}} |
| | 70 | |
| | 71 | To extract data from a Set, use {{{select()}}}. This is an asynchronous method, so the result comes in a callback: |
| | 72 | {{{#!js |
| | 73 | set.select([field], function(rows) { |
| | 74 | if (rows.len) { |
| | 75 | rows.forEach(function(row) { |
| | 76 | // Do something with the row |
| | 77 | }); |
| | 78 | } |
| | 79 | }); |
| | 80 | }}} |
| | 81 | |
| | 82 | The result is an array of Rows. |
| | 83 | |
| | 84 | === Rows === |
| | 85 | |
| | 86 | The Row object implements a {{{$}}} method again (analogously to accessing fields in a table), but this one accepts both field ''names'' and Fields: |
| | 87 | |
| | 88 | {{{#!js |
| | 89 | // Accessing a column via field name |
| | 90 | var value = row.$('fieldname'); |
| | 91 | |
| | 92 | // Accessing a column with a Field instance |
| | 93 | var value = row.$(table.$('fieldname')); |
| | 94 | }}} |
| | 95 | |
| | 96 | Using {{{Field}}}s to access column values is recommended (or even necessary) when selecting from joins. Obviously, you can use a local variable for table.$('fieldname') and use that in both the select() and the subsequent column access. |
| | 97 | |
| | 98 | === Joins === |
| | 99 | |
| | 100 | A join can be constructed by calling the {{{join()}}} (=inner join) or {{{left()}}} method of a Table or a Set: |
| | 101 | |
| | 102 | {{{#!js |
| | 103 | table.left( |
| | 104 | otherTable.on( |
| | 105 | otherTable.$('foreign_key').equals(table.$('id')) |
| | 106 | ) |
| | 107 | ) |
| | 108 | }}} |
| | 109 | |
| | 110 | ...produces a left join of table with otherTable. The {{{on()}}} method of the Table takes a query expression as argument. |
| | 111 | |
| | 112 | The {{{join()}}} or {{{left()}}} methods always return the Set (if called from a Table, they will create a new Set), so they can be chained both with more {{{.join()}}} resp {{{.left()}}} (to construct a multiple-table join), or {{{.where()}}} to filter the set, and eventually a {{{.select()}}} to extract data. |
| | 113 | |
| | 114 | So it can look like this: |
| | 115 | {{{#!js |
| | 116 | table.left( |
| | 117 | otherTable.on(otherTable.$('foreign_key').equals(table.$('id'))) |
| | 118 | ).where( |
| | 119 | table.$('name').equals('some string') |
| | 120 | ).select( |
| | 121 | [table.$('name'), otherTable.$('value')], |
| | 122 | function(rows) { |
| | 123 | ... |
| | 124 | }); |
| | 125 | }}} |
| | 126 | |
| | 127 | (written on multiple lines, this becomes very readable - almost undisturbed from the inevitable JS punctuation) |
| | 128 | |
| | 129 | Is is also possible to chain multiple {{{.where()}}}'s, basically producing an AND of all where() expresions in the chain. Surely, where you construct a Set in place, you wouldn't do multiple .where()'s but rather use allOf(). But if you collect filters (e.g. in a loop), you can easily extend the Set by calling .where() multiple times, i.e. instead of: |
| | 130 | |
| | 131 | {{{#!js |
| | 132 | // Somewhat cumbersome: |
| | 133 | for (...) { |
| | 134 | ... |
| | 135 | if (query) { |
| | 136 | query = query.and(subQuery); |
| | 137 | } else { |
| | 138 | query = subQuery; |
| | 139 | } |
| | 140 | } |
| | 141 | table.where(query).select(...); |
| | 142 | }}} |
| | 143 | |
| | 144 | ...you can do it like: |
| | 145 | |
| | 146 | {{{#!js |
| | 147 | set = table; |
| | 148 | for (...) { |
| | 149 | ... |
| | 150 | set = set.where(subQuery); |
| | 151 | } |
| | 152 | }}} |
| | 153 | |
| | 154 | === Orderby and Limitby === |
| | 155 | |
| | 156 | The {{{select()}}} method accepts an object with arguments as (optional) second parameter, e.g. {{{limitby}}} and {{{orderby}}}: |
| | 157 | |
| | 158 | {{{#!js |
| | 159 | set.select( |
| | 160 | [array of fields], |
| | 161 | { |
| | 162 | limitby: [0,1], |
| | 163 | orderby: field |
| | 164 | }, |
| | 165 | function(rows) {...} |
| | 166 | ); |
| | 167 | }}} |
| | 168 | |
| | 169 | NB {{{limitby: [0,1]}}} can also be written as simply {{{limitby: 1}}} |