Tutorial: 006 - Select - Ad-Hoc Converters

006 - Select - Ad-Hoc Converters

In the last example you may have noticed that the hasStoreKeys property, which is defined as a BIT in the database, is serialized as a Buffer. Usually, however, it's nice to convert BIT fields to booleans. node-data-mapper allows developers to attach converters to selected columns on the fly. (Note that there's also a way to attach converters at initialization time, which allows developers to, for example, convert all bit-type columns to booleans. This is convered in the Schema tutorials.)

node-data-mapper has some built-in converters in the converter directory. Let's take a look at the pre-defined BooleanConverter object:

'use strict';

/** A converter that converts buffers and numbers to booleans and back. */
class BooleanConverter {
  /**
   * Convert the "bit" to a boolean.
   * @param {number|Buffer} bit - Either an instance of a Buffer containing a 1
   * or a 0, or a number.
   * @return {boolean} The bit's representation as a boolean.
   */
  onRetrieve(bit) {
    if (bit === null || bit === undefined || bit === '')
      return null;

    if (Buffer.isBuffer(bit))
      return bit[0] === 1;

    return bit === '1' || bit === 1;
  }

  /**
   * Convert a boolean to a bit.
   * @param {boolean} bool - A boolean value.
   * @return {number} The boolean's representation as a number (1 or 0),
   * or null if bool is null or undefined.
   */
  onSave(bool) {
    if (bool === null || bool === undefined || bool === '')
      return null;
    return bool ? 1 : 0;
  }
}
  

A converter is an object with two functions: onRetrieve and onSave. Each function takes in a value and transforms it. The former transforms for serialization, and the latter transforms for saving (INSERT or UPDATE). In the above code, the BooleanConverter#onRetrieve method takes a Buffer or Number instance and converts it to a boolean (or null).

The following code snippet gives an example of using the BooleanConverter#onRetrieve converter on the fly. It also uses a simple in-line converter to convert firstName to uppercase.

'use strict';

const booleanConverter = require('node-data-mapper').booleanConverter;
const MySQLDriver      = require('node-data-mapper-mysql').MySQLDriver;
const driver           = new MySQLDriver(require('../bikeShopConOpts.json'));

driver
  .initialize()
  .then(runQuery)
  .then(printResult)
  .catch(console.error)
  .finally(() => driver.end());

function runQuery(dataContext) {
  const query = dataContext
    .from('staff s')
    .select(
      's.staffID',
      // Convert "hasStoreKeys" to boolean.
      {column: 's.hasStoreKeys', convert: booleanConverter.onRetrieve},
      // Convert "firstName" to upper case.
      {column: 's.firstName',    convert: fName => fName.toUpperCase()}
    );

  console.log('Query:');
  console.log(query.toString(), '\n');

  return query
    .execute();
}

function printResult(result) {
  console.log('Result:');
  console.log(result);
}
  

And here is the result ($ node example/retrieve/adHocConverter.js):

Query:
SELECT  `s`.`staffID` AS `s.staffID`,
        `s`.`hasStoreKeys` AS `s.hasStoreKeys`,
        `s`.`firstName` AS `s.firstName`
FROM    `staff` AS `s` 

Result:
{ staff: 
   [ { staffID: 1, hasStoreKeys: false, firstName: 'RANDY' },
     { staffID: 2, hasStoreKeys: true, firstName: 'JOHN' },
     { staffID: 3, hasStoreKeys: false, firstName: 'TINA' },
     { staffID: 4, hasStoreKeys: true, firstName: 'ABE' },
     { staffID: 5, hasStoreKeys: true, firstName: 'SAL' },
     { staffID: 6, hasStoreKeys: true, firstName: 'VALERIE' },
     { staffID: 7, hasStoreKeys: false, firstName: 'KIMBERLY' },
     { staffID: 8, hasStoreKeys: false, firstName: 'MICHAEL' } ] }