Code coverage report for src/conditionalparameters.js

Statements: 94.29% (99 / 105)      Branches: 85.71% (66 / 77)      Functions: 100% (10 / 10)      Lines: 94.29% (99 / 105)      Ignored: none     

All files » src/ » conditionalparameters.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174        1       1 1     1   65   4 4 2 8   2   4   4       65 55 36 55 55 55     65 32 32 66   32     65 89 6   83     65 33     65   99     65 90 59 90 90 90 90 2   90   90 59 90 90 125 125 125 120 120 120 31   89   104 104     104 104 102   86     16 102   104 104 1   103 4   99                 120       90 2   88     65 65 59 65       65 59 6 6 6 6       59 59   59 58 59 48 59         1   1 1 1     1 1   1 1 1 1   1     1  
/*
 * Builds a string from mongodb-like-query object
 */
 
Iif (typeof window === 'object') {
  var _ = window._;
  var helpers = window.Neo4jMapper.helpers;
} else {
  var _ = require('underscore');
  var helpers = require('./helpers');
}
 
var ConditionalParameters = function ConditionalParameters(conditions, options) {
 
  ConditionalParameters.parameterRuleset = {
    $IN: function(value) {
      var s = '';
      if ((typeof value === 'object') && (value.length > 0)) {
        for (var i=0; i < value.length; i++) {
          value[i] = (typeof value[i] === 'string') ? "'"+helpers.escapeString(value[i])+"'" : helpers.valueToStringForCypherQuery(value[i]);
        }
        s = value.join(', ');
      }
      return 'IN [ ' + s + ' ]';
    },
    $in: function(value) { return this.$IN(value); }
 
  };
 
  ConditionalParameters.prototype.addValue = function(value) {
    if (!this.parameters)
      this.parameters = {};
    var property = '_value'+(this.parametersStartCountAt + this.parametersCount())+'_';
    this.parameters[property] = value;
    return '{'+property+'}';
  }
 
  ConditionalParameters.prototype.values = function() {
    var values = [];
    for (var prop in this.parameters) {
      values.push(this.parameters[prop]);
    }
    return values;
  }
 
  ConditionalParameters.prototype.parametersCount = function() {
    if ((typeof this.parameters !== 'object')||(this.parameters === null))
      return 0;
    else
      return Object.keys(this.parameters).length;
  }
 
  ConditionalParameters.prototype.hasParameters = function() {
    return (this.parametersCount() > 0);
  }
 
  ConditionalParameters.prototype.cypherKeyValueToString = function(key, originalValue, identifier) {
    // call cypherKeyValueToString with this object context
    return helpers.cypherKeyValueToString(key, originalValue, identifier, this);
  }
 
  ConditionalParameters.prototype.convert = function(condition, operator) {
    if (typeof condition === 'undefined')
      condition = this.conditions;
    var options = _.extend({}, this.defaultOptions, this.options);
    Eif (options.firstLevel)
      options.firstLevel = false;
    if (options.parametersStartCountAt)
      this.parametersStartCountAt = options.parametersStartCountAt;
    // TODO: if $not : [ {name: 'a'}] ~> NOT (name = a)
    Iif (typeof condition === 'string')
      condition = [ condition ];
    if (typeof operator === 'undefined')
      operator = this.operator; // AND
    Eif (typeof condition === 'object')
      for (var key in condition) {
        var value = condition[key];
        var property = null;
        if (_.isObject(condition[key])) {
          var properties = [];
          var firstKey = (_.keys(value)) ? _.keys(value)[0] : null;
          if ((firstKey)&&(ConditionalParameters.is_operator.test(firstKey))) {
            properties.push(this.convert(condition[key][firstKey], firstKey.replace(/\$/g,' ').trim().toUpperCase(), options));
          } else {
            for (var k in condition[key]) {
              // k = key/property, remove identifier e.g. n.name
              var property = k.replace(/^[nmrp]\./,'');
              value = condition[key][k];
 
              // only check for attributes if not s.th. like `n.name? = …`
              var identifierWithProperty = (/\?$/.test(property)) ? '' : property;
              if (identifierWithProperty) {
                if (options.identifier)
                  // we have s.th. like options.identifier = n; property = '`'+identifierWithProperty+'`'
                  identifierWithProperty = options.identifier + '.' + identifierWithProperty;
                else
                  // we have no explicit identifier, so we use the complete key/property and expecting it contains identifier
                  identifierWithProperty = k;
                identifierWithProperty = helpers.escapeProperty(identifierWithProperty);
              }
              var hasAttribute = (identifierWithProperty) ? 'HAS ('+identifierWithProperty+') AND ' : '';
              if (value === k) {
                properties.push(hasAttribute+value);
              // do we have s.th. like { name: { $IN: [ … ] } }
              } else if ((typeof value === 'object')&&(value !== null)&&(Object.keys(value).length === 1)&&(typeof ConditionalParameters.parameterRuleset[Object.keys(value)[0]] === 'function')) {
                properties.push(hasAttribute+' '+(identifierWithProperty || k)+' '+ConditionalParameters.parameterRuleset[Object.keys(value)[0]](value[Object.keys(value)[0]]));
              } else {
                properties.push(hasAttribute+this.cypherKeyValueToString(k, value,
                  // only add an identifier if we have NOT s.th. like
                  // n.name = ''  or r.since …
                  (/^[a-zA-Z\_\-]+\./).test(k) ? null : options.identifier
                ));
              }
            }
          }
          // merge sub conditions
          condition[key] = properties.join(' '+operator+' ');
        }
      }
 
    if ((condition.length === 1)&&(options.firstLevel === false)&&(/NOT/i.test(operator)))
      return operator + ' ( '+condition.join('')+' )';
    else
      return '( '+condition.join(' '+operator+' ')+' )';
  }
 
  ConditionalParameters.prototype.toString = function() {
    if (this.conditions)
      this._s = this.convert();
    return this._s;
  }
 
  // assign parameters and option(s)
  if (typeof conditions === 'object') {
    this.conditions = (conditions) ? conditions : {};
  } else Eif (typeof conditions === 'string') {
    this.conditions = null;
    this._s = '( ' + conditions + ' )';
    return;
  } else {
    throw Error('First argument must be an object with conditional parameters or a plain string');
  }
  Eif (typeof options === 'object') {
    this.options = options;
    // assign some options if they exists to current object
    if (typeof this.options.valuesToParameters !== 'undefined')
      this.valuesToParameters = this.options.valuesToParameters;
    if (typeof this.options.identifier !== 'undefined')
      this.identifier = this.options.identifier;
    Iif (typeof this.options.operator !== 'undefined')
      this.operator = this.options.operator;
  }
}
 
ConditionalParameters.is_operator = /^\$(AND|OR|XOR|NOT|AND\$NOT|OR\$NOT)$/i;
 
ConditionalParameters.prototype.operator               = 'AND';
ConditionalParameters.prototype.identifier             = 'n';
ConditionalParameters.prototype.conditions             = null;
 
// options are used to prevent overriding object attributes on recursive calls
ConditionalParameters.prototype.options                = null;
ConditionalParameters.prototype.defaultOptions         = { firstLevel: true, identifier: null };
 
ConditionalParameters.prototype.parameters             = null;
ConditionalParameters.prototype.valuesToParameters     = true;
ConditionalParameters.prototype._s                     = '';
ConditionalParameters.prototype.parametersStartCountAt = 0;
 
Iif (typeof window === 'object') {
  window.Neo4jMapper.ConditionalParameters = ConditionalParameters;
} else {
  module.exports = exports = ConditionalParameters;
}