From 177f0cf995def93b3d0f3fcee393f7fa7357ea0b Mon Sep 17 00:00:00 2001 From: Phil Batey Date: Fri, 5 Apr 2019 13:09:51 -0600 Subject: [PATCH] Allow keywords override --- README.md | 30 ++++++++++++++++++++++++++++-- index.js | 26 ++++++++++++++++---------- test/query.js | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 28a0dd9..959f362 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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: ``` { @@ -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: diff --git a/index.js b/index.js index 7d434db..ec29545 100644 --- a/index.js +++ b/index.js @@ -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])) @@ -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 @@ -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) { @@ -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) @@ -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 diff --git a/test/query.js b/test/query.js index a7ffd32..3afa1e9 100644 --- a/test/query.js +++ b/test/query.js @@ -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}) })