docs: function checkValue(value, test, topData) {
    return _.every(test, function (val, operator) {
      val = getRef(val, topData);
      if (operator === '$eq') {
        if (_.isArray(value) && !_.isArray(val)) {
          return value.indexOf(val) > -1;
        }
        return _.isEqual(value, val);
      }
      if (operator === '$ne') {
        return !_.isEqual(value, val);
      }
      if (operator === '$gt') {
        return value > val;
      }
      if (operator === '$gte') {
        return value >= val;
      }
      if (operator === '$lt') {
        return value < val;
      }
      if (operator === '$lte') {
        return value <= val;
      }
      if (operator === '$in') {
        if (!_.isArray(val)) {
          throw new Error('$in must be an array');
        }
        if (_.isArray(value)) {
          return _.find(value, function (v) {
            return inArray(v, val, topData);
          })
        }
        return inArray(value, val, topData);
      }
      if (operator === '$nin') {
        if (!_.isArray(val)) {
          throw new Error('$nin must be an array');
        }
        if (_.isArray(value)) {
          return _.every(value, function (v) {
            return !inArray(v, val, topData);
          })
        }
        return !inArray(value, val, topData);
      }
      if (operator === '$not') {
        if (val instanceof RegExp) {
          return !(typeof value === 'string' && val.test(value));
        } else if (isExpression(val)) {
          return !checkValue(value, val, topData);
        } else {
          throw new Error('$not needs a regex or a document');
        }
      }
      if (operator === '$regex') {
        if (test.$options !== undefined && typeof test.$options !== 'string') {
          throw new Error('$options has to be a string');
        }
        if (typeof val === 'string') {
          val = new RegExp(val, test.$options);
        } else if (val instanceof RegExp) {
          if (test.$options) {
            val.compile(val.source, test.$options);
          }
        } else {
          throw new Error('$regex has to be a string or RegExp');
        }
        if (typeof value === 'string') {
          return val.test(value);
        }
        return false;
      }
      if (operator === '$options') {
        if (!test.$regex) {
          throw new Error('$options needs a $regex');
        }
        return true;
      }
  
      if (operator === '$all') {
        if (!_.isArray(val)) {
          throw new Error('$all has to be an array');
        }
        if (!val.length) return false;
        for (var i in val) {
          var el = val[i];
          if (_.isArray(el)) {
            return _.isEqual(el, value);
          }
          if (isExpression(el)) {
            if (!el.$elemMatch) {
              throw new Error('no $ expressions in $all');
            }
            // $all with $elemMatch
            return _.find(value, function (v) {
              return checkDepends(el.$elemMatch, v, topData);
            });
          }
          if (!findArrayElement(value, el, topData)) {
            return false;
          }
        }
        return true;
      }
  
      if (operator === '$elemMatch') {
        if (!operator || typeof val !== 'object') {
          throw new Error('$elemMatch needs an Object');
        }
        if (!_.isArray(value)) return false;
        if (isExpression(val)) {
          return _.find(value, function (v) {
            return checkValue(v, val, topData);
          });
        }
        // Object
        return _.find(value, function (v) {
          return checkDepends(val, v, topData);
        });
      }
  
      if (operator === '$size') {
        if (typeof val !== 'number') {
          throw new Error('$size needs an number');
        }
        return _.isArray(value) && value.length === val;
      }
  
      if (operator === '$exists') {
        if (typeof val !== 'boolean') {
          throw new Error('$exists needs a boolean');
        }
        var res = value === undefined;
        return val ? !res : res;
      }
      throw new Error('unsupported comparison operator: ' + operator);
    })
  }