From db981949bf97308ffc23d657e1ea9558b13894e2 Mon Sep 17 00:00:00 2001 From: wallacen Date: Thu, 12 Jan 2017 16:08:18 -0500 Subject: [PATCH 1/7] develop branch initial --- .npmrc | 3 +++ src/parseFilterString/setKey.ts | 4 ++-- src/parseFilterString/setValue.ts | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.npmrc b/.npmrc index e69de29..9420c28 100644 --- a/.npmrc +++ b/.npmrc @@ -0,0 +1,3 @@ +http_proxy="" +https_proxy="" +all_proxy="" \ No newline at end of file diff --git a/src/parseFilterString/setKey.ts b/src/parseFilterString/setKey.ts index 9fcf97f..4f60010 100644 --- a/src/parseFilterString/setKey.ts +++ b/src/parseFilterString/setKey.ts @@ -10,9 +10,9 @@ export interface IKeyObj { export function setKey (str: string): IKeyObj { let keyObj: IKeyObj = { key: null - } + }; - let key = vrpl.trimQuote(kmtch.getkey(str)[ 0 ].trim()) + let key = vrpl.trimQuote(kmtch.getkey(str)[ 0 ].trim()); if ( key.startsWith("has ") ) { keyObj.checkExists = true; } diff --git a/src/parseFilterString/setValue.ts b/src/parseFilterString/setValue.ts index 0d63504..0270083 100644 --- a/src/parseFilterString/setValue.ts +++ b/src/parseFilterString/setValue.ts @@ -7,7 +7,6 @@ export type TValue = RegExp | string | number | Array export function setValue (obj: string, operator: string): TValue { let value: TValue; let qArr: RegExpMatchArray = mtch.arrayFromComma(obj); - let OperatorsWithArray=["$in","$nin","$mod","$all","$slice"]; if ( OperatorsWithArray.indexOf(operator) > -1 ) { // strings must be wrapped in '' numbers are not From 2a6dd95e9e25c5bb8c3e153d82f1e2c868e83436 Mon Sep 17 00:00:00 2001 From: wallacen Date: Thu, 12 Jan 2017 17:32:47 -0500 Subject: [PATCH 2/7] added aggregate function with match, sort, project --- package.json | 2 +- src/__tests__/aqParser.ts | 12 +++++++++ src/index.ts | 15 ++++++++--- src/parseAggregateQuery/aqParser.ts | 31 ++++++++++++++++++++++ src/parseAggregateQuery/index.ts | 1 + src/parseFilterString/queryStringParser.ts | 6 ++--- 6 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 src/__tests__/aqParser.ts create mode 100644 src/parseAggregateQuery/aqParser.ts create mode 100644 src/parseAggregateQuery/index.ts diff --git a/package.json b/package.json index d00841e..3fe6d38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mongo-qp", - "version": "1.1.6", + "version": "1.2.0", "description": "Creates a object that contains mongo query objects. filter,sort,projection,limit,skip", "main": "./dist/index.js", "scripts": { diff --git a/src/__tests__/aqParser.ts b/src/__tests__/aqParser.ts new file mode 100644 index 0000000..51ba6da --- /dev/null +++ b/src/__tests__/aqParser.ts @@ -0,0 +1,12 @@ +import * as chai from "chai"; +const should = chai.should(); +import { ParseAggregate } from "../index"; + + +describe.only("#ParseAggregate",()=>{ + let str="match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name'"; + it("blah blah blah",(done)=>{ + ParseAggregate(str); + done(); + }) +}) \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 04104df..b7eba02 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,8 @@ import { qsParser } from "./parseFilterString"; import { ssParser } from "./parseSortString"; import { IParserObj } from "./parseFilterString"; -import { getProjection } from "./parseProjectionString" +import { getProjection } from "./parseProjectionString"; +import { aqParser } from "./parseAggregateQuery"; /* * Returns and array of objects that represent a set of query parameters based on mongodb node driver * [,[*, ...keys]] @@ -34,7 +35,7 @@ export function ParseQuery (reqQuery: IReqQuery | string): IParsedObject { let returnObj = { filter: {} }; - if (typeof reqQuery === "string") { + if ( typeof reqQuery === "string" ) { returnObj[ "filter" ] = command.filter(decodeURIComponent(reqQuery)); } else { @@ -48,6 +49,14 @@ export function ParseQuery (reqQuery: IReqQuery | string): IParsedObject { return returnObj; } - +export function ParseAggregate (str: string): Array { + // match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name' + let parser = aqParser() + let agg = str.split(/ THEN /); + let returnObj = new Array(agg.length); + agg.forEach((item, idx) => {returnObj[idx]= parser(item);}) + console.log(returnObj) + return returnObj +} diff --git a/src/parseAggregateQuery/aqParser.ts b/src/parseAggregateQuery/aqParser.ts new file mode 100644 index 0000000..a5b6a66 --- /dev/null +++ b/src/parseAggregateQuery/aqParser.ts @@ -0,0 +1,31 @@ +import {qsParser} from '../parseFilterString'; +import {ssParser} from '../parseSortString'; +import {getProjection} from '../parseProjectionString'; +import {valuesRegExp, keysRegExp} from '../regExp'; +export function aqParser(){ + let memo={}; + let command = { + match: qsParser(), + sort: ssParser, + project:getProjection + } + function parser(str:string){ + let value; + if(str in memo) {return memo[str]} + else{ + let body = valuesRegExp.match.extractValue(str)[0].trim(); + let key = keysRegExp.match.getkey(str)[0].trim(); + + if(command[key]) { + value={} + let action=command[key]; + value["$"+key] = action(valuesRegExp.replace.trimQuote(body)); + + } + + } + + return value + } + return parser; +} \ No newline at end of file diff --git a/src/parseAggregateQuery/index.ts b/src/parseAggregateQuery/index.ts new file mode 100644 index 0000000..ad73ed8 --- /dev/null +++ b/src/parseAggregateQuery/index.ts @@ -0,0 +1 @@ +export * from './aqParser'; \ No newline at end of file diff --git a/src/parseFilterString/queryStringParser.ts b/src/parseFilterString/queryStringParser.ts index da05000..51d731d 100644 --- a/src/parseFilterString/queryStringParser.ts +++ b/src/parseFilterString/queryStringParser.ts @@ -42,15 +42,15 @@ export function qsParser (): (str: string) => IParserObj { value = memo[ str ]; } else { - if ( str.match(regExOr) ) { + if ( regExOr.test(str) ) { value = {}; value[ "$or" ] = getSplit(str, regExOr); } - else if ( str.match(regExAnd) ) { + else if ( regExAnd.test(str) ) { value = {}; value[ "$and" ] = getSplit(str, regExAnd); } - else if ( str.match(regExNor) ) { + else if ( regExNor.test(str) ) { value = {}; value[ "$nor" ] = getSplit(str, regExNor); } From 047eff50f0ee9a5c55c9fcfee6a0751395792f53 Mon Sep 17 00:00:00 2001 From: wallacen Date: Fri, 13 Jan 2017 11:31:32 -0500 Subject: [PATCH 3/7] added unwind parser to aggregate --- src/__tests__/aqParser.ts | 4 +- src/__tests__/mongo.test.ts | 0 src/__tests__/setKey.test.ts | 8 ++-- src/__tests__/setOperator.test.ts | 6 +-- src/index.ts | 8 ++-- src/parseAggregateQuery/aqParser.ts | 50 +++++++++++++--------- src/parseAggregateQuery/unwindParser.ts | 23 ++++++++++ src/parseFilterString/queryStringParser.ts | 7 ++- src/parseFilterString/setValue.ts | 2 +- src/utils/checkNumber.ts | 1 + src/utils/index.ts | 3 +- 11 files changed, 73 insertions(+), 39 deletions(-) create mode 100644 src/__tests__/mongo.test.ts create mode 100644 src/parseAggregateQuery/unwindParser.ts diff --git a/src/__tests__/aqParser.ts b/src/__tests__/aqParser.ts index 51ba6da..238de05 100644 --- a/src/__tests__/aqParser.ts +++ b/src/__tests__/aqParser.ts @@ -3,8 +3,8 @@ const should = chai.should(); import { ParseAggregate } from "../index"; -describe.only("#ParseAggregate",()=>{ - let str="match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name'"; +describe("#ParseAggregate",()=>{ + let str="match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last asc, name' THEN unwind 'path name,preserveNullAndEmptyArrays true'"; it("blah blah blah",(done)=>{ ParseAggregate(str); done(); diff --git a/src/__tests__/mongo.test.ts b/src/__tests__/mongo.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/__tests__/setKey.test.ts b/src/__tests__/setKey.test.ts index a5f9455..699cb5b 100644 --- a/src/__tests__/setKey.test.ts +++ b/src/__tests__/setKey.test.ts @@ -24,7 +24,7 @@ describe("#setKey", () => { [ "'last-name'", "eq 'Mickey'" ] ]; describe("returns valid keys", () => { - normalStr.forEach((item) => { + normalStr.map((item) => { it(`should return the key ${item[ 0 ]}`, (done) => { let key = setKey(item.join(" ")); key.should.be.a("object"); @@ -36,7 +36,7 @@ describe("#setKey", () => { }); }); describe("returns valid keys with checkExists flag true", () => { - normalStr.forEach((item) => { + normalStr.map((item) => { it(`should return the key ${item[ 0 ].trim()} and have checkExists eq true`, (done) => { let key = setKey("has " + item.join(" ")); key.should.be.a("object"); @@ -49,7 +49,7 @@ describe("#setKey", () => { }); }); describe("returns valid keys with checkExists flag false", () => { - normalStr.forEach((item) => { + normalStr.map((item) => { it(`should return the key ${item[ 0 ].trim()} and have checkExists eq false`, (done) => { let key = setKey("!has " + item.join(" ")); key.should.be.a("object"); @@ -62,7 +62,7 @@ describe("#setKey", () => { }); }); describe("returns invalid keys", () => { - invalidStr.forEach((item) => { + invalidStr.map((item) => { it(`should NOT return the key ${item[ 0 ].trim()}`, (done) => { let key = setKey(item.join("")); key.key.should.not.equal(item[0]); diff --git a/src/__tests__/setOperator.test.ts b/src/__tests__/setOperator.test.ts index a223207..d833344 100644 --- a/src/__tests__/setOperator.test.ts +++ b/src/__tests__/setOperator.test.ts @@ -24,7 +24,7 @@ describe("#setOperator", () => { done(); }); - op.forEach(item => { + op.map(item => { it(`should return have a property of operator equal to ${item}`, (done) => { const str = `name ${item.replace("$", "")} 'abc'`; let operator = setOperator(str); @@ -32,7 +32,7 @@ describe("#setOperator", () => { done(); }); }); - pairedop.forEach(item => { + pairedop.map(item => { it(`should pass operator ${item[0]} and return and object that has property of ${item[1]}`, (done) => { const str = `name ${item[0].replace("$", "")} 'abc'`; let operator = setOperator(str); @@ -42,7 +42,7 @@ describe("#setOperator", () => { }); }); describe("falsy operators", () => { - notop.forEach(item => { + notop.map(item => { it(`should return have a property of falsy that is true -${item}`, (done) => { const str = `name ${item.replace("$", "")} 'abc'`; let operator = setOperator(str); diff --git a/src/index.ts b/src/index.ts index b7eba02..c67ff5f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import { ssParser } from "./parseSortString"; import { IParserObj } from "./parseFilterString"; import { getProjection } from "./parseProjectionString"; import { aqParser } from "./parseAggregateQuery"; +import {checkNumber} from "./utils"; /* * Returns and array of objects that represent a set of query parameters based on mongodb node driver * [,[*, ...keys]] @@ -24,7 +25,6 @@ export interface IParsedObject { export function ParseQuery (reqQuery: IReqQuery | string): IParsedObject { - let checkNumber = (arg) => isNaN(Number(arg)) ? null : +arg; let command = { filter: qsParser(), limit: checkNumber, @@ -54,9 +54,9 @@ export function ParseAggregate (str: string): Array { let parser = aqParser() let agg = str.split(/ THEN /); let returnObj = new Array(agg.length); - agg.forEach((item, idx) => {returnObj[idx]= parser(item);}) - console.log(returnObj) - return returnObj + agg.map((item, idx) => {returnObj[idx]= parser(item);}) + + return returnObj; } diff --git a/src/parseAggregateQuery/aqParser.ts b/src/parseAggregateQuery/aqParser.ts index a5b6a66..9a94162 100644 --- a/src/parseAggregateQuery/aqParser.ts +++ b/src/parseAggregateQuery/aqParser.ts @@ -1,31 +1,41 @@ -import {qsParser} from '../parseFilterString'; -import {ssParser} from '../parseSortString'; -import {getProjection} from '../parseProjectionString'; -import {valuesRegExp, keysRegExp} from '../regExp'; -export function aqParser(){ - let memo={}; +import { qsParser } from '../parseFilterString'; +import { ssParser } from '../parseSortString'; +import { getProjection } from '../parseProjectionString'; +import { checkNumber } from '../utils'; +import { valuesRegExp, keysRegExp } from '../regExp'; +import { unwindParser} from "./unwindParser"; +export function aqParser () { + let memo = {}; let command = { - match: qsParser(), - sort: ssParser, - project:getProjection + match: qsParser(), + sort: ssParser, + project: getProjection, + limit: checkNumber, + skip: checkNumber, + unwind: unwindParser, + } - function parser(str:string){ + + function parser (str: string) { let value; - if(str in memo) {return memo[str]} - else{ - let body = valuesRegExp.match.extractValue(str)[0].trim(); - let key = keysRegExp.match.getkey(str)[0].trim(); + if ( str in memo ) { + return memo[ str ] + } + else { + let body = valuesRegExp.match.extractValue(str)[ 0 ].trim(); + let key = keysRegExp.match.getkey(str)[ 0 ].trim(); - if(command[key]) { - value={} - let action=command[key]; - value["$"+key] = action(valuesRegExp.replace.trimQuote(body)); + if ( command[ key ] ) { + value = {}; + let action = command[ key ]; + value[ "$" + key ] = action(valuesRegExp.replace.trimQuote(body)); } } - return value } + return parser; -} \ No newline at end of file +} + diff --git a/src/parseAggregateQuery/unwindParser.ts b/src/parseAggregateQuery/unwindParser.ts new file mode 100644 index 0000000..215686b --- /dev/null +++ b/src/parseAggregateQuery/unwindParser.ts @@ -0,0 +1,23 @@ +import {valuesRegExp, keysRegExp} from "../regExp"; +export function unwindParser(str:string){ + let splStr=str.split(","); + let map = { + path: (val) => /^$/.test(val) ? val : "$" + val, + includeArrayIndex: (val) => /^$+/.test(val) ? val.replace(/^$+/, "") : val, + preserveNullAndEmptyArrays: (val) => val === 'true' ? true : false, + } + + + let returnObj={}; + + splStr.map((item)=>{ + let key=keysRegExp.match.getkey(item)[0].trim(); + let value = valuesRegExp.match.extractValue(item)[0].trim(); + if(map.hasOwnProperty(key)) { + + returnObj[ key ] = map[key](value); + } + + }) + return returnObj; +} \ No newline at end of file diff --git a/src/parseFilterString/queryStringParser.ts b/src/parseFilterString/queryStringParser.ts index 51d731d..2564e6a 100644 --- a/src/parseFilterString/queryStringParser.ts +++ b/src/parseFilterString/queryStringParser.ts @@ -28,12 +28,11 @@ export function qsParser (): (str: string) => IParserObj { let regExNor: RegExp = new RegExp(/ NOR /g); function getSplit (obj: string, regex: RegExp): Array { - let newArr: Array = []; let spl: Array = obj.split(regex); - spl.forEach(item => { - newArr.push(parser(item.trim())); + return spl.map(item => { + return parser(item.trim()); }); - return newArr; + } function parser (str: string): IParserObj { diff --git a/src/parseFilterString/setValue.ts b/src/parseFilterString/setValue.ts index 0270083..2a8a8d3 100644 --- a/src/parseFilterString/setValue.ts +++ b/src/parseFilterString/setValue.ts @@ -11,7 +11,7 @@ export function setValue (obj: string, operator: string): TValue { if ( OperatorsWithArray.indexOf(operator) > -1 ) { // strings must be wrapped in '' numbers are not value = [ qArr.length ] as Array; - qArr.forEach((item, idx) => { + qArr.map((item, idx) => { value[ idx ] = checkType(item); }); } diff --git a/src/utils/checkNumber.ts b/src/utils/checkNumber.ts index e69de29..4004fd0 100644 --- a/src/utils/checkNumber.ts +++ b/src/utils/checkNumber.ts @@ -0,0 +1 @@ +export function checkNumber (arg) { return isNaN(Number(arg)) ? null : +arg;} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index 4e676bc..80fbcd7 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1 +1,2 @@ -export * from "./typeIs"; \ No newline at end of file +export * from "./typeIs"; +export * from "./checkNumber"; \ No newline at end of file From 49d02efe510309f0619cb1fd31ebf964a73fd106 Mon Sep 17 00:00:00 2001 From: noahwallace Date: Mon, 16 Jan 2017 21:29:04 -0500 Subject: [PATCH 4/7] first instance of aggregate query --- README.md | 64 +++++++++++++++++++++-------- src/ParseAggregate.ts | 11 +++++ src/__tests__/aqParser.ts | 13 ++++-- src/__tests__/getProjection.test.ts | 2 +- src/__tests__/parseFilterString.ts | 2 +- src/index.ts | 16 +++++--- 6 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 src/ParseAggregate.ts diff --git a/README.md b/README.md index e6dbf75..18d4582 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ Mongo Query Parser ## Problem -Using the native mongo db driver for node, It is neccesary to easily pass a set of strings that will define the query objects +Using the native mongo db driver for node, It is necessary to easily pass a set of strings that will define the query objects ## Solution This solution allows a front end developer to use plain language text to query a nodejs/mongodb api. - +This package was build on the standard for [MongoDb Node.JS Driver Version 2.2](http://mongodb.github.io/node-mongodb-native/2.2/api/) ##Usage @@ -32,7 +32,8 @@ import {ParseQuery} from 'mongo-qp' let query =ParseQuery(req.query:Object | string) // es5 in browser - + + // files are included in package ``` ### Query @@ -141,7 +142,7 @@ When using contains internal operators MUST be wrapped in curly brackets {"c":{"$eq":d}, {"e":{"$eq":f} ]} -``` +``` ##### EXAMPLE D @@ -177,18 +178,17 @@ Or you can check if a field does not exists ``` const str = "has name nin 'mickey','donald' AND !has last_name" -{"$and":[ - {"name":{ - "$exists":true, - "$nin":["mickey","donald"] - } - }, - {"last_name":{ - "$exists":false - } - } + {"$and":[ + {"name":{ + "$exists":true, + "$nin":["mickey","donald"] + } + }, + {"last_name":{ + "$exists":false + } + } ]} - ``` #### Operators @@ -207,8 +207,8 @@ const str = "has name nin 'mickey','donald' AND !has last_name" |mod | [',] | Comma Separated string with length of 2 (ie 4,0)| |regex | '\' or ['\']|Requires a regex string pattern that starts with / and ends with /. (gim) is optional| |all | ['\' or \] |Comma Separated string (ie 'abc','def',123)]| -|size | \ | | -|contains | queryString |Add query string surrounded by single quotes
EXCEPTION : any logical operators(AND\|OR\|NOR) inside the string MUST be wrapped in brackets ( {AND} )| +|size | \ | | +|contains | queryString |Add query string surrounded by single quotes
EXCEPTION : any logical operators(AND,OR,NOR) inside the string MUST be wrapped in brackets ( {AND} )| NOTE: Any Operator in the above list can be prefaces with 'not' (ie name not eq 'abc') @@ -283,7 +283,6 @@ Mongo 3.4 supports projection operators. mongo-qp will also support projection o **(1.1.6)** mongo-qp now supports $elemMatch(projection). Usage is the same as query. (mongodb >3.4) - #### Example ``` @@ -303,4 +302,33 @@ Mongo 3.4 supports projection operators. mongo-qp will also support projection o } */ +``` + + +# Aggregation +**(1.2.0)** Now supports simple aggregation pipline querys. +## Usage + +Use the keyword THEN to separate operations in the query pipeline. all values MUST be wrapped in single quote + +| Allowed | Restriction |Comments | +|:--------| ------------------------ |:-----------------------------------------| +|match | '\' | use the match operator to return a filter query| +|sort | '\' or \ | Comma Separated string as above | +|limit | \ | number as above | +|skip | \ | number as above | +|unwind | '\' or \ | | + + +#### Example + +```javascript + let str="match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last asc, name' THEN unwind 'path name,preserveNullAndEmptyArrays true'"; + let aggregatePipline = ParseAggregate(str) + /* + [ { '$match': { name: [Object] } }, + { '$project': { _id: 1, name: 1, created: 1 } }, + { '$sort': [ [Object], 'name' ] }, + { '$unwind': { path: '$name', preserveNullAndEmptyArrays: true } } ] + */ ``` \ No newline at end of file diff --git a/src/ParseAggregate.ts b/src/ParseAggregate.ts new file mode 100644 index 0000000..bf10715 --- /dev/null +++ b/src/ParseAggregate.ts @@ -0,0 +1,11 @@ +import {aqParser} from './parseAggregateQuery'; + +export function ParseAggregate (str: string): Array { + // match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name' + let parser = aqParser() + let agg = str.split(/ THEN /); + let returnObj = new Array(agg.length); + agg.forEach((item, idx) => {returnObj[idx]= parser(item);}) + + return returnObj +} diff --git a/src/__tests__/aqParser.ts b/src/__tests__/aqParser.ts index 238de05..dc19250 100644 --- a/src/__tests__/aqParser.ts +++ b/src/__tests__/aqParser.ts @@ -1,12 +1,19 @@ import * as chai from "chai"; const should = chai.should(); -import { ParseAggregate } from "../index"; +import { ParseAggregate } from "../ParseAggregate"; describe("#ParseAggregate",()=>{ let str="match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last asc, name' THEN unwind 'path name,preserveNullAndEmptyArrays true'"; - it("blah blah blah",(done)=>{ - ParseAggregate(str); + it("should be an array and have proper values",(done)=>{ + let aggregationArray = ParseAggregate(str); + aggregationArray.should.be.a("Array"); + aggregationArray.should.have.length(4); + aggregationArray[0].hasOwnProperty("$match"); + aggregationArray[0].hasOwnProperty("$project"); + aggregationArray[0].hasOwnProperty("$sort"); + aggregationArray[0].hasOwnProperty("$unwind"); + done(); }) }) \ No newline at end of file diff --git a/src/__tests__/getProjection.test.ts b/src/__tests__/getProjection.test.ts index 4af7a93..8bfc538 100644 --- a/src/__tests__/getProjection.test.ts +++ b/src/__tests__/getProjection.test.ts @@ -6,7 +6,7 @@ import { getProjection } from "../parseProjectionString"; describe("#getProjection",()=>{ it("",(done)=>{ let str = "name contains 'object eq 'abc' {AND} score eq 23',_id"; - console.log(JSON.stringify(getProjection(str))) + done(); }) }) \ No newline at end of file diff --git a/src/__tests__/parseFilterString.ts b/src/__tests__/parseFilterString.ts index 95c1e3f..673fdda 100644 --- a/src/__tests__/parseFilterString.ts +++ b/src/__tests__/parseFilterString.ts @@ -247,7 +247,7 @@ describe("#parseString", () => { it("should return a valid AND and OR object with Complexities ", (done) => { const str = "grades contains 'grade eq 'B' {AND} score eq 23'"; let newFilter = parseString(str); - console.log(JSON.stringify(newFilter)) + done(); }); }); diff --git a/src/index.ts b/src/index.ts index d752ebb..b7196ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,19 @@ import {IReqQuery, IParsedObject, ParseQuery} from "./ParseQuery"; +import {ParseAggregate} from './ParseAggregate'; interface Window { - ParseQuery:(reqQuery: IReqQuery | string)=>IParsedObject + ParseQuery: (reqQuery: IReqQuery | string) => IParsedObject; + ParseAggregate: (str: string) => any } -declare let window:Window; +declare let window: Window; (function () { - if (typeof module !== "undefined" && module.exports && typeof window === 'undefined') {} - else { window.ParseQuery = ParseQuery;} + if (typeof module !== "undefined" && module.exports && typeof window === 'undefined') { + } + else { + window.ParseQuery = ParseQuery; + window.ParseAggregate = ParseAggregate; + } })(); -export {ParseQuery}; +export {ParseQuery, ParseAggregate}; From 8b5d73e09d662cc967e519de7fd84169d532e963 Mon Sep 17 00:00:00 2001 From: noahwallace Date: Mon, 16 Jan 2017 21:34:47 -0500 Subject: [PATCH 5/7] regex bugfix for unwind parser --- lib/mongoqp.js | 171 ++++++++++++++++++++++-- lib/mongoqp.min.js | 2 +- src/parseAggregateQuery/unwindParser.ts | 4 +- 3 files changed, 165 insertions(+), 12 deletions(-) diff --git a/lib/mongoqp.js b/lib/mongoqp.js index c39db1c..9e29e5b 100644 --- a/lib/mongoqp.js +++ b/lib/mongoqp.js @@ -47,10 +47,14 @@ "use strict"; var ParseQuery_1 = __webpack_require__(1); exports.ParseQuery = ParseQuery_1.ParseQuery; + var ParseAggregate_1 = __webpack_require__(16); + exports.ParseAggregate = ParseAggregate_1.ParseAggregate; (function () { - if (typeof module !== "undefined" && module.exports && typeof window === 'undefined') { } + if (typeof module !== "undefined" && module.exports && typeof window === 'undefined') { + } else { window.ParseQuery = ParseQuery_1.ParseQuery; + window.ParseAggregate = ParseAggregate_1.ParseAggregate; } })(); @@ -114,12 +118,10 @@ var regExAnd = new RegExp(/ AND /g); var regExNor = new RegExp(/ NOR /g); function getSplit(obj, regex) { - var newArr = []; var spl = obj.split(regex); - spl.forEach(function (item) { - newArr.push(parser(item.trim())); + return spl.map(function (item) { + return parser(item.trim()); }); - return newArr; } function parser(str) { var value; @@ -127,15 +129,15 @@ value = memo[str]; } else { - if (str.match(regExOr)) { + if (regExOr.test(str)) { value = {}; value["$or"] = getSplit(str, regExOr); } - else if (str.match(regExAnd)) { + else if (regExAnd.test(str)) { value = {}; value["$and"] = getSplit(str, regExAnd); } - else if (str.match(regExNor)) { + else if (regExNor.test(str)) { value = {}; value["$nor"] = getSplit(str, regExNor); } @@ -206,7 +208,7 @@ if (OperatorsWithArray.indexOf(operator) > -1) { // strings must be wrapped in '' numbers are not value = [qArr.length]; - qArr.forEach(function (item, idx) { + qArr.map(function (item, idx) { value[idx] = checkType(item); }); } @@ -453,6 +455,157 @@ ; +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + var parseAggregateQuery_1 = __webpack_require__(17); + function ParseAggregate(str) { + // match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name' + var parser = parseAggregateQuery_1.aqParser(); + var agg = str.split(/ THEN /); + var returnObj = new Array(agg.length); + agg.forEach(function (item, idx) { returnObj[idx] = parser(item); }); + return returnObj; + } + exports.ParseAggregate = ParseAggregate; + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + } + __export(__webpack_require__(18)); + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + var parseFilterString_1 = __webpack_require__(2); + var parseSortString_1 = __webpack_require__(12); + var parseProjectionString_1 = __webpack_require__(14); + var utils_1 = __webpack_require__(19); + var regExp_1 = __webpack_require__(6); + var unwindParser_1 = __webpack_require__(22); + function aqParser() { + var memo = {}; + var command = { + match: parseFilterString_1.qsParser(), + sort: parseSortString_1.ssParser, + project: parseProjectionString_1.getProjection, + limit: utils_1.checkNumber, + skip: utils_1.checkNumber, + unwind: unwindParser_1.unwindParser, + }; + function parser(str) { + var value; + if (str in memo) { + return memo[str]; + } + else { + var body = regExp_1.valuesRegExp.match.extractValue(str)[0].trim(); + var key = regExp_1.keysRegExp.match.getkey(str)[0].trim(); + if (command[key]) { + value = {}; + var action = command[key]; + value["$" + key] = action(regExp_1.valuesRegExp.replace.trimQuote(body)); + } + } + return value; + } + return parser; + } + exports.aqParser = aqParser; + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + } + __export(__webpack_require__(20)); + __export(__webpack_require__(21)); + + +/***/ }, +/* 20 */ +/***/ function(module, exports) { + + "use strict"; + function typeIs(obj, type) { + switch (type.toLowerCase()) { + case "string": + return typeof obj === "string" || obj instanceof String; + case "array": + return Array.isArray(obj) || obj instanceof Array; + case "object": + return Object.prototype.toString.call(obj) === "[object Object]"; + case "null": + return typeof obj === "object" && obj === null; + case "undefined": + return obj === void 0; + case "function": + return typeof obj === "function"; + case "boolean": + return typeof obj === "boolean"; + case "number": + return typeof obj === "number"; + case "symbol": + return typeof obj === "symbol"; + case "date": + return typeof obj === "object" && obj instanceof Date; + default: + return false; + } + } + exports.typeIs = typeIs; + + +/***/ }, +/* 21 */ +/***/ function(module, exports) { + + "use strict"; + function checkNumber(arg) { return isNaN(Number(arg)) ? null : +arg; } + exports.checkNumber = checkNumber; + + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + var regExp_1 = __webpack_require__(6); + function unwindParser(str) { + var splStr = str.split(","); + var map = { + path: function (val) { return /^\$/.test(val) ? val : "$" + val; }, + includeArrayIndex: function (val) { return /^\$+/.test(val) ? val.replace(/^\$+/, "") : val; }, + preserveNullAndEmptyArrays: function (val) { return val === 'true' ? true : false; }, + }; + var returnObj = {}; + splStr.map(function (item) { + var key = regExp_1.keysRegExp.match.getkey(item)[0].trim(); + var value = regExp_1.valuesRegExp.match.extractValue(item)[0].trim(); + if (map.hasOwnProperty(key)) { + returnObj[key] = map[key](value); + } + }); + return returnObj; + } + exports.unwindParser = unwindParser; + + /***/ } /******/ ]); //# sourceMappingURL=mongoqp.js.map \ No newline at end of file diff --git a/lib/mongoqp.min.js b/lib/mongoqp.min.js index adbffbb..b8448cf 100644 --- a/lib/mongoqp.min.js +++ b/lib/mongoqp.min.js @@ -1 +1 @@ -!function(e){function t(n){if(r[n])return r[n].exports;var s=r[n]={exports:{},id:n,loaded:!1};return e[n].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";var n=r(1);t.ParseQuery=n.ParseQuery,function(){"undefined"!=typeof e&&e.exports&&"undefined"==typeof window||(window.ParseQuery=n.ParseQuery)}()},function(e,t,r){"use strict";function n(e){var t=function(e){return isNaN(Number(e))?null:+e},r={filter:s.qsParser(),limit:t,sort:o.ssParser,skip:t,project:i.getProjection},n={filter:{}};if("string"==typeof e)n.filter=r.filter(decodeURIComponent(e));else for(var a in e)if(r.hasOwnProperty(a)){var u=r[a];n[a]=u(decodeURIComponent(e[a]))}return n}var s=r(2),o=r(12),i=r(14);t.ParseQuery=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(3))},function(e,t,r){"use strict";function n(){function e(e,r){var n=[],s=e.split(r);return s.forEach(function(e){n.push(t(e.trim()))}),n}function t(t){var a;return t in r?a=r[t]:t.match(n)?(a={},a.$or=e(t,n)):t.match(o)?(a={},a.$and=e(t,o)):t.match(i)?(a={},a.$nor=e(t,i)):a=s.getValueObject(t),a}var r={},n=new RegExp(/ OR /g),o=new RegExp(/ AND /g),i=new RegExp(/ NOR /g);return t}var s=r(4);t.qsParser=n},function(e,t,r){"use strict";function n(e){var t=i.setOperator(e),r=o.setKey(e),n={},a=s.setValue(e,t.operator),u=(c={},c[t.operator]=a,c);return r.hasOwnProperty("checkExists")&&(n[r.key]={$exists:r.checkExists}),n[r.key]&&n[r.key].hasOwnProperty("$exists")?n[r.key].$exists&&t.operator&&(t.falsy?n[r.key].$not=u:n[r.key][t.operator]=a):n[r.key]=t.falsy?{$not:u}:u,n;var c}var s=r(5),o=r(10),i=r(11);t.getValueObject=n},function(e,t,r){"use strict";function n(e,t){var r,n=c.arrayFromComma(e),i=["$in","$nin","$mod","$all","$slice"];if(i.indexOf(t)>-1)r=[n.length],n.forEach(function(e,t){r[t]=s(e)});else{if("$elemMatch"==t){var u=c.extractValue(e)[0].trim(),p=a.trimCurlysFromLogicalOps(u);return o.qsParser()(a.trimQuote(p))}var f=c.extractValue(e)[0].trim(),l={true:!0,false:!1};if(r=l[f]||"false"===f?l[f]:s(f),("$gt"===t||"$gte"===t)&&"string"==typeof r&&(!r||r.match(/^\s+$/)))return"~"}return r}function s(e){var t=a.trimQuote(e);if(u.isRegExString(t)){var r=a.getRegExString(t),n=a.getRegExOptions(t);return new RegExp(r,n)}return isNaN(Number(e))?t:+e}var o=r(3),i=r(6),a=i.valuesRegExp.replace,u=i.valuesRegExp.test,c=i.valuesRegExp.match;t.setValue=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(7)),n(r(8)),n(r(9))},function(e,t){"use strict";t.operatorsRegExp={regex:{operators:new RegExp(/\s+(not)*\s*(eq|gt|gte|lt|lte|ne|in|nin|is|type|mod|regex|all|size|contains)\s+/)},test:{hasOperator:function(e){return t.operatorsRegExp.regex.operators.test(e)}},match:{getOperator:function(e){return e.match(t.operatorsRegExp.regex.operators)}},replace:{removeNot:function(e){return e.replace(/^not\s+/i,"")}}}},function(e,t){"use strict";t.valuesRegExp={match:{arrayFromComma:function(e){return e.match(/((\d+)(?=\s*,*\s*)|(('.+?')(?=\s*,\s*)|('.+'$)))/gi)},extractValue:function(e){return e.match(/(('.+')+(,'.+')*|\s{1}\S+$)/i)}},replace:{trimCurlyBraces:function(e){return e.replace(/(^\{|\}$)/g,"")},trimQuote:function(e){return e.replace(/(^'|'$)/g,"")},getRegExString:function(e){return e.replace(/(^\/|\/([gim]*)?$)/g,"")},getRegExOptions:function(e){return e.replace(/(^\/.+\/)(?=[gim]?)/,"")},trimCurlysFromLogicalOps:function(e){return e.replace(/\{(AND|OR|NOR){1}\}/g,function(e){return t.valuesRegExp.replace.trimCurlyBraces(e)})}},test:{isRegExString:function(e){return/(^\/|\/([gim]*)?$)/g.test(e)}}}},function(e,t){"use strict";t.keysRegExp={match:{getkey:function(e){return e.match(/(^(\s*!?has\s+)?([^\s]+)|(^\s*[^\s]+))/i)}},replace:{removeHas:function(e){return e.replace(/^(!?has\s+)/,"")}}}},function(e,t,r){"use strict";function n(e){var t={key:null},r=o.trimQuote(a.getkey(e)[0].trim());return r.startsWith("has ")?t.checkExists=!0:r.startsWith("!has ")&&(t.checkExists=!1),t.key=i.removeHas(r).trim(),t}var s=r(6),o=s.valuesRegExp.replace,i=s.keysRegExp.replace,a=s.keysRegExp.match;t.setKey=n},function(e,t,r){"use strict";function n(e){var t=i.hasOperator(e)?a.getOperator(e)[0].trim():null,r={falsy:!1,operator:null};return t&&(t.startsWith("not ")&&(r.falsy=!0),r.operator="$"+s(t)),r}function s(e){var t={is:"type",contains:"elemMatch"};return u.removeNot(t[e]||e)}var o=r(6),i=o.operatorsRegExp.test,a=o.operatorsRegExp.match,u=o.operatorsRegExp.replace;t.setOperator=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(13))},function(e,t){"use strict";function r(e){var t=[],r=e.split(","),n=!1;return r.map(function(e){var r=e.trim().split(/\s+/);r.length>1?t.push(r):(t.push(r[0]),n=1===r[0].length)}),n?t[0]:t}t.ssParser=r},function(e,t,r){"use strict";var n=r(15);t.getProjection=n.getProjection},function(e,t,r){"use strict";function n(e){var t=e.split(","),r={_id:0};return t.forEach(function(e){var t=/ contains /i;if(t.test(e)){var n=s.getValueObject(e);for(var o in n)r[o]=n[o]}else r[e.trim()]=1}),r}var s=r(4);t.getProjection=n}]); \ No newline at end of file +!function(e){function t(n){if(r[n])return r[n].exports;var s=r[n]={exports:{},id:n,loaded:!1};return e[n].call(s.exports,s,s.exports,t),s.loaded=!0,s.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){"use strict";var n=r(1);t.ParseQuery=n.ParseQuery;var s=r(16);t.ParseAggregate=s.ParseAggregate,function(){"undefined"!=typeof e&&e.exports&&"undefined"==typeof window||(window.ParseQuery=n.ParseQuery,window.ParseAggregate=s.ParseAggregate)}()},function(e,t,r){"use strict";function n(e){var t=function(e){return isNaN(Number(e))?null:+e},r={filter:s.qsParser(),limit:t,sort:a.ssParser,skip:t,project:i.getProjection},n={filter:{}};if("string"==typeof e)n.filter=r.filter(decodeURIComponent(e));else for(var o in e)if(r.hasOwnProperty(o)){var u=r[o];n[o]=u(decodeURIComponent(e[o]))}return n}var s=r(2),a=r(12),i=r(14);t.ParseQuery=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(3))},function(e,t,r){"use strict";function n(){function e(e,r){var n=e.split(r);return n.map(function(e){return t(e.trim())})}function t(t){var o;return t in r?o=r[t]:n.test(t)?(o={},o.$or=e(t,n)):a.test(t)?(o={},o.$and=e(t,a)):i.test(t)?(o={},o.$nor=e(t,i)):o=s.getValueObject(t),o}var r={},n=new RegExp(/ OR /g),a=new RegExp(/ AND /g),i=new RegExp(/ NOR /g);return t}var s=r(4);t.qsParser=n},function(e,t,r){"use strict";function n(e){var t=i.setOperator(e),r=a.setKey(e),n={},o=s.setValue(e,t.operator),u=(c={},c[t.operator]=o,c);return r.hasOwnProperty("checkExists")&&(n[r.key]={$exists:r.checkExists}),n[r.key]&&n[r.key].hasOwnProperty("$exists")?n[r.key].$exists&&t.operator&&(t.falsy?n[r.key].$not=u:n[r.key][t.operator]=o):n[r.key]=t.falsy?{$not:u}:u,n;var c}var s=r(5),a=r(10),i=r(11);t.getValueObject=n},function(e,t,r){"use strict";function n(e,t){var r,n=c.arrayFromComma(e),i=["$in","$nin","$mod","$all","$slice"];if(i.indexOf(t)>-1)r=[n.length],n.map(function(e,t){r[t]=s(e)});else{if("$elemMatch"==t){var u=c.extractValue(e)[0].trim(),f=o.trimCurlysFromLogicalOps(u);return a.qsParser()(o.trimQuote(f))}var p=c.extractValue(e)[0].trim(),l={true:!0,false:!1};if(r=l[p]||"false"===p?l[p]:s(p),("$gt"===t||"$gte"===t)&&"string"==typeof r&&(!r||r.match(/^\s+$/)))return"~"}return r}function s(e){var t=o.trimQuote(e);if(u.isRegExString(t)){var r=o.getRegExString(t),n=o.getRegExOptions(t);return new RegExp(r,n)}return isNaN(Number(e))?t:+e}var a=r(3),i=r(6),o=i.valuesRegExp.replace,u=i.valuesRegExp.test,c=i.valuesRegExp.match;t.setValue=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(7)),n(r(8)),n(r(9))},function(e,t){"use strict";t.operatorsRegExp={regex:{operators:new RegExp(/\s+(not)*\s*(eq|gt|gte|lt|lte|ne|in|nin|is|type|mod|regex|all|size|contains)\s+/)},test:{hasOperator:function(e){return t.operatorsRegExp.regex.operators.test(e)}},match:{getOperator:function(e){return e.match(t.operatorsRegExp.regex.operators)}},replace:{removeNot:function(e){return e.replace(/^not\s+/i,"")}}}},function(e,t){"use strict";t.valuesRegExp={match:{arrayFromComma:function(e){return e.match(/((\d+)(?=\s*,*\s*)|(('.+?')(?=\s*,\s*)|('.+'$)))/gi)},extractValue:function(e){return e.match(/(('.+')+(,'.+')*|\s{1}\S+$)/i)}},replace:{trimCurlyBraces:function(e){return e.replace(/(^\{|\}$)/g,"")},trimQuote:function(e){return e.replace(/(^'|'$)/g,"")},getRegExString:function(e){return e.replace(/(^\/|\/([gim]*)?$)/g,"")},getRegExOptions:function(e){return e.replace(/(^\/.+\/)(?=[gim]?)/,"")},trimCurlysFromLogicalOps:function(e){return e.replace(/\{(AND|OR|NOR){1}\}/g,function(e){return t.valuesRegExp.replace.trimCurlyBraces(e)})}},test:{isRegExString:function(e){return/(^\/|\/([gim]*)?$)/g.test(e)}}}},function(e,t){"use strict";t.keysRegExp={match:{getkey:function(e){return e.match(/(^(\s*!?has\s+)?([^\s]+)|(^\s*[^\s]+))/i)}},replace:{removeHas:function(e){return e.replace(/^(!?has\s+)/,"")}}}},function(e,t,r){"use strict";function n(e){var t={key:null},r=a.trimQuote(o.getkey(e)[0].trim());return r.startsWith("has ")?t.checkExists=!0:r.startsWith("!has ")&&(t.checkExists=!1),t.key=i.removeHas(r).trim(),t}var s=r(6),a=s.valuesRegExp.replace,i=s.keysRegExp.replace,o=s.keysRegExp.match;t.setKey=n},function(e,t,r){"use strict";function n(e){var t=i.hasOperator(e)?o.getOperator(e)[0].trim():null,r={falsy:!1,operator:null};return t&&(t.startsWith("not ")&&(r.falsy=!0),r.operator="$"+s(t)),r}function s(e){var t={is:"type",contains:"elemMatch"};return u.removeNot(t[e]||e)}var a=r(6),i=a.operatorsRegExp.test,o=a.operatorsRegExp.match,u=a.operatorsRegExp.replace;t.setOperator=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(13))},function(e,t){"use strict";function r(e){var t=[],r=e.split(","),n=!1;return r.map(function(e){var r=e.trim().split(/\s+/);r.length>1?t.push(r):(t.push(r[0]),n=1===r[0].length)}),n?t[0]:t}t.ssParser=r},function(e,t,r){"use strict";var n=r(15);t.getProjection=n.getProjection},function(e,t,r){"use strict";function n(e){var t=e.split(","),r={_id:0};return t.forEach(function(e){var t=/ contains /i;if(t.test(e)){var n=s.getValueObject(e);for(var a in n)r[a]=n[a]}else r[e.trim()]=1}),r}var s=r(4);t.getProjection=n},function(e,t,r){"use strict";function n(e){var t=s.aqParser(),r=e.split(/ THEN /),n=new Array(r.length);return r.forEach(function(e,r){n[r]=t(e)}),n}var s=r(17);t.ParseAggregate=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(18))},function(e,t,r){"use strict";function n(){function e(e){var n;if(e in t)return t[e];var s=u.valuesRegExp.match.extractValue(e)[0].trim(),a=u.keysRegExp.match.getkey(e)[0].trim();if(r[a]){n={};var i=r[a];n["$"+a]=i(u.valuesRegExp.replace.trimQuote(s))}return n}var t={},r={match:s.qsParser(),sort:a.ssParser,project:i.getProjection,limit:o.checkNumber,skip:o.checkNumber,unwind:c.unwindParser};return e}var s=r(2),a=r(12),i=r(14),o=r(19),u=r(6),c=r(22);t.aqParser=n},function(e,t,r){"use strict";function n(e){for(var r in e)t.hasOwnProperty(r)||(t[r]=e[r])}n(r(20)),n(r(21))},function(e,t){"use strict";function r(e,t){switch(t.toLowerCase()){case"string":return"string"==typeof e||e instanceof String;case"array":return Array.isArray(e)||e instanceof Array;case"object":return"[object Object]"===Object.prototype.toString.call(e);case"null":return"object"==typeof e&&null===e;case"undefined":return void 0===e;case"function":return"function"==typeof e;case"boolean":return"boolean"==typeof e;case"number":return"number"==typeof e;case"symbol":return"symbol"==typeof e;case"date":return"object"==typeof e&&e instanceof Date;default:return!1}}t.typeIs=r},function(e,t){"use strict";function r(e){return isNaN(Number(e))?null:+e}t.checkNumber=r},function(e,t,r){"use strict";function n(e){var t=e.split(","),r={path:function(e){return/^\$/.test(e)?e:"$"+e},includeArrayIndex:function(e){return/^\$+/.test(e)?e.replace(/^\$+/,""):e},preserveNullAndEmptyArrays:function(e){return"true"===e}},n={};return t.map(function(e){var t=s.keysRegExp.match.getkey(e)[0].trim(),a=s.valuesRegExp.match.extractValue(e)[0].trim();r.hasOwnProperty(t)&&(n[t]=r[t](a))}),n}var s=r(6);t.unwindParser=n}]); \ No newline at end of file diff --git a/src/parseAggregateQuery/unwindParser.ts b/src/parseAggregateQuery/unwindParser.ts index 215686b..68f2e9c 100644 --- a/src/parseAggregateQuery/unwindParser.ts +++ b/src/parseAggregateQuery/unwindParser.ts @@ -2,8 +2,8 @@ import {valuesRegExp, keysRegExp} from "../regExp"; export function unwindParser(str:string){ let splStr=str.split(","); let map = { - path: (val) => /^$/.test(val) ? val : "$" + val, - includeArrayIndex: (val) => /^$+/.test(val) ? val.replace(/^$+/, "") : val, + path: (val) => /^\$/.test(val) ? val : "$" + val, + includeArrayIndex: (val) => /^\$+/.test(val) ? val.replace(/^\$+/, "") : val, preserveNullAndEmptyArrays: (val) => val === 'true' ? true : false, } From e168d6fccea2c6a083b76b4b88c41292517a0cbb Mon Sep 17 00:00:00 2001 From: noahwallace Date: Mon, 16 Jan 2017 21:41:08 -0500 Subject: [PATCH 6/7] package update --- package.json | 5 +++-- webpack.config.js | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index f8c5e58..a2f0bf2 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "mongo-qp", - "version": "1.1.7", + "version": "1.2.0", "description": "Creates a object that contains mongo query objects. filter,sort,projection,limit,skip", "main": "./dist/index.js", "scripts": { "test": "mocha dist/__tests__", - "build": "tsc && webpack" + "build": "webpack && tsc" }, "engines": { "node": ">=6.5.0" @@ -31,6 +31,7 @@ "@types/node": "^6.0.54", "assert": "^1.4.1", "chai": "^3.5.0", + "clean-webpack-plugin": "^0.1.15", "mocha": "^3.2.0", "mongodb": "^2.2.16", "ts-loader": "^1.3.3", diff --git a/webpack.config.js b/webpack.config.js index 6adc175..20dee6b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,5 +1,6 @@ let webpackUglifyJsPlugin = require('webpack-uglify-js-plugin'); -let path=require('path') +let path=require('path'); +let CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry:{ "mongoqp":"./src", @@ -34,6 +35,11 @@ module.exports = { compressor: { warnings: false } + }), + new CleanWebpackPlugin(['dist', 'lib'], { + root: __dirname + '/', + verbose: true, + dry: false }) ], ts:{ From ba5218e2afe1fa526d8e7ba869d2571769461afa Mon Sep 17 00:00:00 2001 From: noahwallace Date: Mon, 16 Jan 2017 22:09:24 -0500 Subject: [PATCH 7/7] added callbacks --- README.md | 14 ++++++++------ package.json | 2 +- src/ParseAggregate.ts | 11 +++++++++-- src/ParseQuery.ts | 30 +++++++++++++++--------------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 18d4582..17cd474 100644 --- a/README.md +++ b/README.md @@ -21,15 +21,17 @@ npm install mongo-qp ``` //es6 js -let parser = require('mongo-qp') -let ParseQuery= parser.ParseQuery; - -let query =ParseQuery(req.query) + let parser = require('mongo-qp'); + let ParseQuery= parser.ParseQuery; + let ParseAggregate= parser.ParseAggregate + ParseQuery(queryObj[, callback]); + ParseAggregate(str[, callback]); //typescript 2.1 -import {ParseQuery} from 'mongo-qp' +import {ParseQuery} from 'mongo-qp'; -let query =ParseQuery(req.query:Object | string) +ParseQuery(req.query: Object | string[, callback:(result)=>any]) +ParseAggregate(str: string[, callback:(result)=>any]); // es5 in browser diff --git a/package.json b/package.json index a2f0bf2..9ae5032 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mongo-qp", - "version": "1.2.0", + "version": "1.2.1", "description": "Creates a object that contains mongo query objects. filter,sort,projection,limit,skip", "main": "./dist/index.js", "scripts": { diff --git a/src/ParseAggregate.ts b/src/ParseAggregate.ts index bf10715..95ad5f9 100644 --- a/src/ParseAggregate.ts +++ b/src/ParseAggregate.ts @@ -1,6 +1,13 @@ import {aqParser} from './parseAggregateQuery'; - -export function ParseAggregate (str: string): Array { +export interface IParseAggregate{ + $match?: any, + $sort?: Array | string>, + $project?: any, + $limit?: number, + $skip?: number, + $unwind?: any, +} +export function ParseAggregate (str: string, callback?:(response)=>any): Array { // match 'name eq 'abc'' THEN project '_id,name,created' THEN sort 'last,name' let parser = aqParser() let agg = str.split(/ THEN /); diff --git a/src/ParseQuery.ts b/src/ParseQuery.ts index 66b53df..e4b8e63 100644 --- a/src/ParseQuery.ts +++ b/src/ParseQuery.ts @@ -1,7 +1,7 @@ -import { qsParser } from "./parseFilterString"; -import { ssParser } from "./parseSortString"; -import { IParserObj } from "./parseFilterString"; -import { getProjection } from "./parseProjectionString" +import {qsParser} from "./parseFilterString"; +import {ssParser} from "./parseSortString"; +import {IParserObj} from "./parseFilterString"; +import {getProjection} from "./parseProjectionString" /* * Returns and array of objects that represent a set of query parameters based on mongodb node driver * [,[*, ...keys]] @@ -22,28 +22,28 @@ export interface IParsedObject { } -export function ParseQuery (reqQuery: IReqQuery | string): IParsedObject { +export function ParseQuery(reqQuery: IReqQuery | string, callback?: (result: IParsedObject) => any): IParsedObject { let checkNumber = (arg) => isNaN(Number(arg)) ? null : +arg; let command = { - filter: qsParser(), - limit: checkNumber, - sort: ssParser, - skip: checkNumber, + filter: qsParser(), + limit: checkNumber, + sort: ssParser, + skip: checkNumber, project: getProjection }; let returnObj = { filter: {} }; if (typeof reqQuery === "string") { - returnObj[ "filter" ] = command.filter(decodeURIComponent(reqQuery)); + returnObj["filter"] = command.filter(decodeURIComponent(reqQuery)); } else { - for ( const key in reqQuery ) { - if ( command.hasOwnProperty(key) ) { - let action = command[ key ]; - returnObj[ key ] = action(decodeURIComponent(reqQuery[ key ])); + for (const key in reqQuery) { + if (command.hasOwnProperty(key)) { + let action = command[key]; + returnObj[key] = action(decodeURIComponent(reqQuery[key])); } } } - return returnObj; + return callback ? callback(returnObj) : returnObj; } \ No newline at end of file