wiki:DeveloperGuidelines/EdenMobile/Services/emDB

Version 4 (modified by Dominic König, 4 years ago) ( diff )

--

emDB

emDB is a service that provides a database abstraction layer for EdenMobile.

Introduction

Tables

A Table object represents a database table, and can be accessed:

...directly from the emDB service (asynchronously):

emDB.table('tablename').then(function(table) {
    ...
});

...or, where a Resource is available (synchronously):

// Access the table of a Resource
var table = resource.table;

// Access any table via a Resource
var table = resource.getTable('tablename');

Fields

To access a Field in the Table, the Table object provides the $ method:

var field = table.$('fieldname');

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.

Expressions

With Tables and Fields, it is possible to construct query Expressions, e.g.

var query = field.is(5);

AND and OR are functions of query expressions:

var query = field.is(5).or(field.greaterThan(6));

If there are multiple operand expressions in an AND or OR, the global allOf() and anyOf() functions can used to improve readability:

// Multiple operand expressions:
var query = field.is(5).or(field.is(6).or(field.is(7)));

// Alternative with anyOf:
var query = anyOf(field.is(5), field.is(6), field.is(7));

Both anyOf and allOf can of course be nested indefinitely.

Negation has two alternatives as well:

// Global not() function
var query = not(field.in([5, 6, 7]));

// Expression method
var query = field.in([5, 6, 7]).not();

Obviously, the former provides for better readability - whilst the latter can be used if the local context overrides the global not for some reason.

Sets

With a table and a query expression, a Set can be constructed like:

var set = table.where(query);

To extract data from a Set, use select(). This is an asynchronous method, so the result comes in a callback:

set.select([field], function(rows) {
    if (rows.len) {
        rows.forEach(function(row) {
            // Do something with the row
        });
    }
});

The result is an array of Rows.

Rows

The Row object implements a $ method again (analogously to accessing fields in a table), but this one accepts both field names and Fields:

// Accessing a column via field name
var value = row.$('fieldname');

// Accessing a column with a Field instance
var value = row.$(table.$('fieldname'));

Using Fields to access column values is recommended (or even necessary) when selecting from joins. Obviously, you can set a local variable for table.$('fieldname'), and then use it in both the select() and the subsequent column access.

Joins

A join can be constructed by calling the join() (=inner join) or left() method of a Table or a Set:

table.left(
    otherTable.on(
        otherTable.$('foreign_key').equals(table.$('id'))
    )
)

...produces a left join of table with otherTable. The on() method of the Table takes a query expression as argument.

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.

So it can look like this:

table.left(
    otherTable.on(otherTable.$('foreign_key').equals(table.$('id')))
).where(
    table.$('name').equals('some string')
).select(
    [table.$('name'), otherTable.$('value')], 
    function(rows) {
        ...
    });

(written on multiple lines, this becomes very readable - almost undisturbed from the inevitable JS punctuation)

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:

// Somewhat cumbersome:
for (...) {
    ...
    if (query) {
        query = query.and(subQuery);
    } else {
        query = subQuery;
    }
}
table.where(query).select(...);

...you can do it like:

set = table;
for (...) {
    ...
    set = set.where(subQuery);
}

Orderby and Limitby

The select() method accepts an object with arguments as (optional) second parameter, e.g. limitby and orderby:

set.select(
    [array of fields], 
    {
        limitby: [0,1], 
        orderby: field
    },
    function(rows) {...}
);

NB limitby: [0,1] can also be written as simply limitby: 1

Note: See TracWiki for help on using the wiki.