Skip to content

Commit

Permalink
Allow keywords override
Browse files Browse the repository at this point in the history
  • Loading branch information
pbatey committed Apr 5, 2019
1 parent 8c8fe03 commit 177f0cf
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 15 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ console.log(query)

#### options:
* **maxLimit** The maximum limit (default is none)
* **ignore** List of criteria to ignore in addition to those used for query options ("fields", "sort", "offset", "limit")
* **ignore** List of criteria to ignore in addition to keywords used for query options ("fields", "omit", "sort", "offset", "limit")
* **parser** Query parser to use instead of _querystring_. Must implement `parse(string)` and `stringify(obj)`.
* **keywords** Override the keywords used for query options ("fields", "omit", "sort", "offset", "limit"). For example: `{fields:'$fields', omit:'$omit', sort:'$sort', offset:'$offset', limit:'$limit'}`

#### returns:
* **criteria** Mongo query criteria.
Expand Down Expand Up @@ -137,7 +138,7 @@ For example, if _offset_ was 20, _limit_ was 10, and _count_ was 95, the followi
These pagination links can be used to populate the [express response links](http://expressjs.com/4x/api.html#res.links).

### Filtering
Any query parameters other then _fields_, _omit_, _sort_, _offset_, and _limit_ are interpreted as query criteria. For example `name=john&age>21` results in a _criteria_ value of:
Any query parameters other then the keywords _fields_, _omit_, _sort_, _offset_, and _limit_ are interpreted as query criteria. For example `name=john&age>21` results in a _criteria_ value of:

```
{
Expand All @@ -164,6 +165,31 @@ Comparisons on embedded documents should use mongo's [dot notation](http://docs.

Although exact matches are handled for either method, comparisons (such as `foo[bar]!=value`) are not supported because the 'extended' parser expects an equals sign after the nested object reference; if it's not an equals the remainder is discarded.

### A note on overriding keywords
You can adjust the keywords (_fields_, _omit_, _sort_, _offset_, and _limit_) by providing an alternate set as an option. For example:

```
altKeywords = {fields:'$fields', omit:'$omit', sort:'$sort', offset:'$offset', limit:'$limit'}
var q = q2m(res.query, {keywords: altKeywords});
```

This will then interpret the standard keywords as query parameters instead of options. For example a query of `age>21&omit=false&$omit=a` results in a _criteria_ value of:

```
{
'age': { $gt: 21 },
'omit': false
}
```

and an _option_ value of:

```
q.option = {
fields: { a: false }
}
```

## Development
There's a *test* script listed in package.json that will execute the mocha tests:

Expand Down
26 changes: 16 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ function hasOrdinalKeys(obj) {
function queryCriteriaToMongo(query, options) {
var hash = {}, p, v, deep
options = options || {}

for (var key in query) {
if (Object.prototype.hasOwnProperty.call(query, key) && (!options.ignore || options.ignore.indexOf(key) == -1)) {
deep = (typeof query[key] === 'object' && !hasOrdinalKeys(query[key]))
Expand Down Expand Up @@ -182,9 +183,9 @@ function queryCriteriaToMongo(query, options) {
// for example {fields:'a,b',offset:8,limit:16} becomes {fields:{a:true,b:true},skip:8,limit:16}
function queryOptionsToMongo(query, options) {
var hash = {},
fields = fieldsToMongo(query.fields),
omitFields = omitFieldsToMongo(query.omit),
sort = sortToMongo(query.sort),
fields = fieldsToMongo(query[options.keywords.fields]),
omitFields = omitFieldsToMongo(query[options.keywords.omit]),
sort = sortToMongo(query[options.keywords.sort]),
maxLimit = options.maxLimit || 9007199254740992,
limit = options.maxLimit || 0

Expand All @@ -194,8 +195,8 @@ function queryOptionsToMongo(query, options) {
if (omitFields) hash.fields = omitFields
if (sort) hash.sort = sort

if (query.offset) hash.skip = Number(query.offset)
if (query.limit) limit = Math.min(Number(query.limit), maxLimit)
if (query[options.keywords.offset]) hash.skip = Number(query[options.keywords.offset])
if (query[options.keywords.limit]) limit = Math.min(Number(query[options.keywords.limit]), maxLimit)
if (limit) {
hash.limit = limit
} else if (options.maxLimit) {
Expand All @@ -208,13 +209,18 @@ function queryOptionsToMongo(query, options) {
module.exports = function(query, options) {
query = query || {};
options = options || {}
options.keywords = options.keywords || {}

defaultKeywords = {fields:'fields', omit:'omit', sort:'sort', offset:'offset', limit:'limit'}
options.keywords = Object.assign(defaultKeywords, options.keywords)
ignoreKeywords = [options.keywords.fields, options.keywords.omit, options.keywords.sort, options.keywords.offset, options.keywords.limit]

if (!options.ignore) {
options.ignore = []
} else {
options.ignore = (typeof options.ignore === 'string') ? [options.ignore] : options.ignore
}
options.ignore = options.ignore.concat(['fields', 'omit', 'sort', 'offset', 'limit'])
options.ignore = options.ignore.concat(ignoreKeywords)
if (!options.parser) options.parser = querystring

if (typeof query === 'string') query = options.parser.parse(query)
Expand All @@ -234,18 +240,18 @@ module.exports = function(query, options) {
options = options || {}

if (offset > 0) {
query.offset = Math.max(offset - limit, 0)
query[options.keywords.offset] = Math.max(offset - limit, 0)
links['prev'] = url + '?' + options.parser.stringify(query)
query.offset = 0
query[options.keywords.offset] = 0
links['first'] = url + '?' + options.parser.stringify(query)
}
if (offset + limit < totalCount) {
last.pages = Math.ceil(totalCount / limit)
last.offset = (last.pages - 1) * limit

query.offset = Math.min(offset + limit, last.offset)
query[options.keywords.offset] = Math.min(offset + limit, last.offset)
links['next'] = url + '?' + options.parser.stringify(query)
query.offset = last.offset
query[options.keywords.offset] = last.offset
links['last'] = url + '?' + options.parser.stringify(query)
}
return links
Expand Down
34 changes: 31 additions & 3 deletions test/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,46 @@ describe("query-to-mongo(query) =>", function () {
assert.ok(results.options)
assert.deepEqual(results.options, {fields: {b:0}})
})
it("should create sort option", function () {
var results = q2m("sort=a,+b,-c")
assert.ok(results.options)
assert.deepEqual(results.options, {sort: {a:1, b:1, c:-1}})
})
it("should limit queries", function () {
var results = q2m("limit=100", {maxLimit: 50})
assert.ok(results.options)
assert.deepEqual(results.options, {limit: 50})
})
})

describe(".options (altKeywords)", function () {
it("should create paging options", function () {
var altKeywords = {fields:'$fields', offset:'$offset', limit:'$limit', sort: '$sort', omit: '$omit'}
var results = q2m("$offset=8&$limit=16", {keywords: altKeywords})
assert.ok(results.options)
assert.deepEqual(results.options, {skip: 8, limit: 16})
})
it("should create field option", function () {
var altKeywords = {fields:'$fields', offset:'$offset', limit:'$limit', sort: '$sort', omit: '$omit'}
var results = q2m("$fields=a,b,c", {keywords: altKeywords})
assert.ok(results.options)
assert.deepEqual(results.options, {fields: {a:1, b:1, c:1}})
})
it("should create omit option", function () {
var results = q2m("omit=b")
var altKeywords = {fields:'$fields', offset:'$offset', limit:'$limit', sort: '$sort', omit: '$omit'}
var results = q2m("$omit=b", {keywords: altKeywords})
assert.ok(results.options)
assert.deepEqual(results.options, {fields: {b:0}})
})
it("should create sort option", function () {
var results = q2m("sort=a,+b,-c")
var altKeywords = {fields:'$fields', offset:'$offset', limit:'$limit', sort: '$sort', omit: '$omit'}
var results = q2m("$sort=a,+b,-c", {keywords: altKeywords})
assert.ok(results.options)
assert.deepEqual(results.options, {sort: {a:1, b:1, c:-1}})
})
it("should limit queries", function () {
var results = q2m("limit=100", {maxLimit: 50})
var altKeywords = {fields:'$fields', offset:'$offset', limit:'$limit', sort: '$sort', omit: '$omit'}
var results = q2m("$limit=100", {maxLimit: 50, keywords: altKeywords})
assert.ok(results.options)
assert.deepEqual(results.options, {limit: 50})
})
Expand Down

0 comments on commit 177f0cf

Please sign in to comment.