Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,26 @@ query().sort('lastName.toLowerCase()');

The second query above will sort by last name irrespective of casing.

### Complex queries

Operators can work on two fields instead of just on a field and a value:

```js
query('field1').is(query.field('field2'));
```

Likewise, you can first give a value and then a field:

```js
query(query.value('value1')).is(query.field('field2'));
```

Or compare two plain values:

```js
query(query.value('value1')).is('value2');
```

## Backbone Support and Adding query to Backbone.Collections

query was built with Backbone in mind. Though you may use `query("get('firstName')").is("John")` to effectively work with
Expand Down
51 changes: 42 additions & 9 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ function query(field) {
this._reduce = [];
}

query.boxed = function (field) {
this.value = field;
};

query.field = function (field) {
if ( !(this instanceof query.field) ) {
return new query.field(field);
}
query.boxed.apply(this, arguments);
};
query.field.prototype = new query.boxed();

query.value = function (val) {
if ( !(this instanceof query.value) ) {
return new query.value(val);
}
query.boxed.apply(this, arguments);
};
query.value.prototype = new query.boxed();

function select(array) {
if ( !(this instanceof select) ) {
return new select(array);
Expand Down Expand Up @@ -157,15 +177,15 @@ query.select = select;
return this._oper(this._store(lookup));
},
has: function(value) {
if (this._expression) this._expression.template = '%not(%term != null && %term.indexOf(%value) != -1)';
if (this._expression) this._expression.template = '%not(%term != null && %term.indexOf(%term) != -1)';
return this._oper(null, value);
},
startsWith: function(value) {
if (this._expression) this._expression.template = '%not(%term != null && %term.substr(0, %operator) == %value)';
if (this._expression) this._expression.template = '%not(%term != null && %term.substr(0, %operator) == %term)';
return this._oper(value.length, value);
},
endsWith: function(value) {
if (this._expression) this._expression.template = '%not(%term != null && %term.substr(%term.length - %operator) == %value)';
if (this._expression) this._expression.template = '%not(%term != null && %term.substr(%term.length - %operator) == %term)';
return this._oper(value.length, value);
},
gt: function(value) {
Expand All @@ -185,7 +205,7 @@ query.select = select;
return this._oper(this._store(value));
},
same: function(value) {
if (this._expression) this._expression.template = '%not(JSON.stringify(%term) %operator %value)';
if (this._expression) this._expression.template = '%not(JSON.stringify(%term) %operator %term)';
value = JSON.stringify(value);
return this._oper('==', value);
},
Expand Down Expand Up @@ -213,7 +233,7 @@ query.select = select;
}
return this._oper(this._store(value));
} else {
if (this._expression) this._expression.template = '%not(type(%term) %operator %value)';
if (this._expression) this._expression.template = '%not(type(%term) %operator %term)';
return this._oper('==', value);
}
},
Expand Down Expand Up @@ -274,22 +294,35 @@ query.select = select;
this.operator = operator;
this.value = value;
this.not = not;
this.template = '%not(%term %operator %value)';
this.template = '%not(%term %operator %term)';
}

Expression.prototype = {
toString: function() {
var self = this;
var asDate;
var terms = [ this.term, this.value ].map(function (term, k) {
if (!(term instanceof query.boxed)) {
term = new query[ (k === 0) ? 'field' : 'val'](term);
}
asDate |= term.value instanceof Date || term.forceDate;
return term;
});
function getTerm(t) {
if (t instanceof query.field) {
return '(obj.' + t.value + ' || (typeof obj.get === "function" && obj.get("' + t.value + '")))' + (asDate ? '.getTime()' : '');
} else {
return JSON.stringify(asDate ? t.value.getTime() : t.value);
}
}
return this.template.replace(/%\w+/g, function(match) {
switch(match) {
case '%not':
return self.not ? '!' : '';
case '%term':
return '(obj.' + self.term + ' || (typeof obj.get === "function" && obj.get("' + self.term + '")))' + (self.value instanceof Date ? '.getTime()' : '');
return getTerm(terms.shift());
case '%operator':
return self.operator;
case '%value':
return JSON.stringify(self.value instanceof Date ? self.value.getTime() : self.value);
}
});
}
Expand Down