query.js 5.66 KB
Newer Older
1
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
2 3
/*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit:
  true, select: true, _export: true, stringEscapeRegexpCharacters: true,
4
  deepClone, RSVP, sequence */
5

6 7 8 9 10 11 12
/**
 * The query to use to filter a list of objects.
 * This is an abstract class.
 *
 * @class Query
 * @constructor
 */
13
function Query() {
14 15 16 17 18 19 20 21

  /**
   * Called before parsing the query. Must be overridden!
   *
   * @method onParseStart
   * @param  {Object} object The object shared in the parse process
   * @param  {Object} option Some option gave in parse()
   */
22
  this.onParseStart = emptyFunction;
23 24 25 26 27 28 29 30

  /**
   * Called when parsing a simple query. Must be overridden!
   *
   * @method onParseSimpleQuery
   * @param  {Object} object The object shared in the parse process
   * @param  {Object} option Some option gave in parse()
   */
31
  this.onParseSimpleQuery = emptyFunction;
32 33 34 35 36 37 38 39

  /**
   * Called when parsing a complex query. Must be overridden!
   *
   * @method onParseComplexQuery
   * @param  {Object} object The object shared in the parse process
   * @param  {Object} option Some option gave in parse()
   */
40
  this.onParseComplexQuery = emptyFunction;
41 42 43 44 45 46 47 48

  /**
   * Called after parsing the query. Must be overridden!
   *
   * @method onParseEnd
   * @param  {Object} object The object shared in the parse process
   * @param  {Object} option Some option gave in parse()
   */
49
  this.onParseEnd = emptyFunction;
50

51
}
52

53 54 55 56 57 58 59 60 61 62 63 64 65 66
/**
 * Filter the item list with matching item only
 *
 * @method exec
 * @param  {Array} item_list The list of object
 * @param  {Object} [option] Some operation option
 * @param  {String} [option.wildcard_character="%"] The wildcard character
 * @param  {Array} [option.select_list] A object keys to retrieve
 * @param  {Array} [option.sort_on] Couples of object keys and "ascending"
 *                 or "descending"
 * @param  {Array} [option.limit] Couple of integer, first is an index and
 *                 second is the length.
 */
Query.prototype.exec = function (item_list, option) {
67
  var i, promises = [];
68 69 70 71 72 73 74 75 76 77 78 79 80
  if (!Array.isArray(item_list)) {
    throw new TypeError("Query().exec(): Argument 1 is not of type 'array'");
  }
  if (option === undefined) {
    option = {};
  }
  if (typeof option !== 'object') {
    throw new TypeError("Query().exec(): " +
                        "Optional argument 2 is not of type 'object'");
  }
  if (option.wildcard_character === undefined) {
    option.wildcard_character = '%';
  }
81 82 83
  for (i = 0; i < item_list.length; i += 1) {
    if (!item_list[i]) {
      promises.push(RSVP.resolve(false));
84
    } else {
85
      promises.push(this.match(item_list[i], option.wildcard_character));
86
    }
87
  }
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
  return sequence([function () {
    return RSVP.all(promises);
  }, function (answers) {
    var j;
    for (j = answers.length - 1; j >= 0; j -= 1) {
      if (!answers[j]) {
        item_list.splice(j, 1);
      }
    }
    if (option.sort_on) {
      return sortOn(option.sort_on, item_list);
    }
  }, function () {
    if (option.limit) {
      return limit(option.limit, item_list);
    }
  }, function () {
    return select(option.select_list || [], item_list);
  }, function () {
    return item_list;
  }]);
109 110 111 112 113 114 115
};

/**
 * Test if an item matches this query
 *
 * @method match
 * @param  {Object} item The object to test
Tristan Cavelier's avatar
Tristan Cavelier committed
116
 * @param  {String} wildcard_character The wildcard character to use
117 118
 * @return {Boolean} true if match, false otherwise
 */
Tristan Cavelier's avatar
Tristan Cavelier committed
119
Query.prototype.match = function () {
120
  return RSVP.resolve(true);
121
};
122

123

124 125 126 127 128 129 130 131 132 133 134 135 136 137
/**
 * Browse the Query in deep calling parser method in each step.
 *
 * `onParseStart` is called first, on end `onParseEnd` is called.
 * It starts from the simple queries at the bottom of the tree calling the
 * parser method `onParseSimpleQuery`, and go up calling the
 * `onParseComplexQuery` method.
 *
 * @method parse
 * @param  {Object} option Any options you want (except 'parsed')
 * @return {Any} The parse result
 */
Query.prototype.parse = function (option) {
  var that = this, object;
138
  /**
139
   * The recursive parser.
140
   *
141 142 143
   * @param  {Object} object The object shared in the parse process
   * @param  {Object} options Some options usable in the parseMethods
   * @return {Any} The parser result
144
   */
145
  function recParse(object, option) {
146
    var query = object.parsed;
147
    if (query.type === "complex") {
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
      return sequence([function () {
        return sequence(query.query_list.map(function (v, i) {
          /*jslint unparam: true */
          return function () {
            sequence([function () {
              object.parsed = query.query_list[i];
              return recParse(object, option);
            }, function () {
              query.query_list[i] = object.parsed;
            }]);
          };
        }));
      }, function () {
        object.parsed = query;
        return that.onParseComplexQuery(object, option);
      }]);
    }
    if (query.type === "simple") {
      return that.onParseSimpleQuery(object, option);
167
    }
168 169
  }
  object = {"parsed": JSON.parse(JSON.stringify(that.serialized()))};
170 171 172 173 174 175 176 177 178
  return sequence([function () {
    return that.onParseStart(object, option);
  }, function () {
    return recParse(object, option);
  }, function () {
    return that.onParseEnd(object, option);
  }, function () {
    return object.parsed;
  }]);
179
};
180

181 182 183 184 185 186 187 188 189
/**
 * Convert this query to a parsable string.
 *
 * @method toString
 * @return {String} The string version of this query
 */
Query.prototype.toString = function () {
  return "";
};
190

191 192 193 194 195 196 197 198 199 200
/**
 * Convert this query to an jsonable object in order to be remake thanks to
 * QueryFactory class.
 *
 * @method serialized
 * @return {Object} The jsonable object
 */
Query.prototype.serialized = function () {
  return undefined;
};
201 202

_export("Query", Query);