appscot/sails-orientdb

View on GitHub
lib/record.js

Summary

Maintainability
B
4 hrs
Test Coverage
"use strict";
/**
 * Module Dependencies
 */
var _ = require('lodash'),
    RID = require('orientjs').RID,
    utils = require('./utils'),
    hop = utils.object.hop,
    log = require('debug-logger')('sails-orientdb:record');

/**
 * Record
 *
 * Represents a single record in a collection. Responsible for serializing values before
 * writing to a collection.
 *
 * @param {Object} values
 * @param {Object} schema
 * @api private
 */

var Record = module.exports = function Record(values, schema, connection, operation) {

  // Keep track of the current record's values
  this.values = {};

  // Grab the schema for normalizing values
  this.schema = schema || {};
  
  // Connection
  this.connection = connection;
  
  // operation type (create / insert)
  this.operation = operation;

  // If values were passed in, use the setter
  if(values){
    var newValues = this.setValues(values);
    this.values = newValues.values;
  }

  return this;
};



/////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
/////////////////////////////////////////////////////////////////////////////////


/**
 * Set values
 *
 * Normalizes values into proper formats.
 *
 * @param {Object} values
 * @return {Object}
 * @api private
 */

Record.prototype.setValues = function setValues(values) {
  var results = this.serializeValues(values);
  this.normalizeId(results.values);

  return results;
};


/**
 * Normalize ID's
 *
 * Moves values.id into the preferred orientDB @rid field.
 *
 * @param {Object} values
 * @api private
 */
Record.prototype.normalizeId = function normalizeId(values) {
  
  if(!values.hasOwnProperty('id') && !values.hasOwnProperty('@rid')) return;
  
  var id = values.id || values['@rid'];

  // Check if data.id looks like a RecordID
  if(_.isString(values.id) && utils.matchRecordId(values.id)) {
    id = new RID(values.id);
  }
  
  if(this.operation && this.operation === 'insert'){
    delete values['@rid'];
  } else {
    values['@rid'] = id;
  }
  delete values.id;
};


/**
 * Serialize Insert Values
 *
 * @param {Object} values
 * @return {Object}
 * @api private
 */
Record.prototype.serializeValues = function serializeValues(values) {
  var self = this;
  var returnResult = {};

  Object.keys(values).forEach(function(key) {
    var schemaKey = key;
    if (!hop(self.schema, key)) {
      // only return if key is not a columnName
      var isColumnName = false;
      Object.keys(self.schema).forEach(function(attributeName) {
        if (self.schema[attributeName].columnName === key) {
          schemaKey = attributeName;
          isColumnName = true;
        }
      });
      if (!isColumnName){
        return;
      }
    }
    
    var type = self.schema[schemaKey].type;

    var foreignKey = self.schema[schemaKey].foreignKey || false;

    if (_.isUndefined(values[key]) || _.isNull(values[key])){
      return;
    }
      
    var targetCollectionName = self.schema[schemaKey].model || self.schema[schemaKey].collection;

    // If a foreignKey, check if value matches a orientDB id and if so turn it into a recordId
    if (foreignKey && utils.matchRecordId(values[key])) {
      values[key] = new RID(values[key]);
    }
    else if (targetCollectionName && utils.matchRecordId(values[key])) {
      values[key] = new RID(values[key]);
    } 
    else if (targetCollectionName && typeof values[key] === 'object') {
      log.warn('Unexpected behaviour: nested association for key [' + key + '], values: ', values[key]);
    }
    else if(targetCollectionName) {
      var targetCollection = self.connection.collectionsByIdentity[targetCollectionName];
      if(targetCollection.primaryKey === 'id'){
        log.warn('Nullifying foreign key [' + key + '] (value: [' + values[key] +
          ']) as value is not a RID and Waterline does not expect an error.');
        values[key] = null;
      }
    }
    else if(foreignKey) {
      log.warn('Nullifying foreign key [' + key + '] (value: [' + values[key] +
        ']) as value is not a RID and Waterline does not expect an error.');
      values[key] = null;
    }
    
    else if(type === 'json'){
      if(!values[key]['@type']){
        values[key]['@type'] = 'd';
      }
      // workaround for #116: prevent sails-orientdb from sending null or empty @class
      // TODO: it probably can be made more elegant
      if(!values[key]['@class']){
        delete values[key]['@class'];
      }
    }
    
    // TODO: should just be "type === 'binary'" but for some reason type never seems to
    // be equal to 'binary'. Waterline issue?
    else if ((type === 'binary' || !type) && Buffer.isBuffer(values[key])) {
      values[key] = values[key].toString('base64');
    }
  }); 

  returnResult.values = values;

  return returnResult;
};