Aggregate allow multiple of same stage (#4835)
* Aggregate Allow Multiple Stages * remove testing files * nit * spread them
This commit is contained in:
committed by
Florent Vilmart
parent
197190fc29
commit
df01846361
@@ -744,4 +744,47 @@ describe('Parse.Query Aggregate testing', () => {
|
|||||||
fail(err);
|
fail(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it_exclude_dbs(['postgres'])('aggregate allow multiple of same stage', (done) => {
|
||||||
|
const pointer1 = new TestObject({ value: 1});
|
||||||
|
const pointer2 = new TestObject({ value: 2});
|
||||||
|
const pointer3 = new TestObject({ value: 3});
|
||||||
|
|
||||||
|
const obj1 = new TestObject({ pointer: pointer1, name: 'Hello' });
|
||||||
|
const obj2 = new TestObject({ pointer: pointer2, name: 'Hello' });
|
||||||
|
const obj3 = new TestObject({ pointer: pointer3, name: 'World' });
|
||||||
|
|
||||||
|
const options = Object.assign({}, masterKeyOptions, {
|
||||||
|
body: [{
|
||||||
|
match: { name: "Hello" },
|
||||||
|
}, {
|
||||||
|
// Transform className$objectId to objectId and store in new field tempPointer
|
||||||
|
project: {
|
||||||
|
tempPointer: { $substr: [ "$_p_pointer", 11, -1 ] }, // Remove TestObject$
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
// Left Join, replace objectId stored in tempPointer with an actual object
|
||||||
|
lookup: {
|
||||||
|
from: "test_TestObject",
|
||||||
|
localField: "tempPointer",
|
||||||
|
foreignField: "_id",
|
||||||
|
as: "tempPointer"
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
// lookup returns an array, Deconstructs an array field to objects
|
||||||
|
unwind: {
|
||||||
|
path: "$tempPointer",
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
match : { "tempPointer.value" : 2 },
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
Parse.Object.saveAll([pointer1, pointer2, pointer3, obj1, obj2, obj3]).then(() => {
|
||||||
|
return rp.get(Parse.serverURL + '/aggregate/TestObject', options);
|
||||||
|
}).then((resp) => {
|
||||||
|
expect(resp.results.length).toEqual(1);
|
||||||
|
expect(resp.results[0].tempPointer.value).toEqual(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,60 +4,56 @@ import * as middleware from '../middlewares';
|
|||||||
import Parse from 'parse/node';
|
import Parse from 'parse/node';
|
||||||
import UsersRouter from './UsersRouter';
|
import UsersRouter from './UsersRouter';
|
||||||
|
|
||||||
const ALLOWED_KEYS = [
|
const BASE_KEYS = ['where', 'distinct'];
|
||||||
'where',
|
|
||||||
'distinct',
|
const PIPELINE_KEYS = [
|
||||||
'project',
|
'addFields',
|
||||||
'match',
|
|
||||||
'redact',
|
|
||||||
'limit',
|
|
||||||
'skip',
|
|
||||||
'unwind',
|
|
||||||
'group',
|
|
||||||
'sample',
|
|
||||||
'sort',
|
|
||||||
'geoNear',
|
|
||||||
'lookup',
|
|
||||||
'out',
|
|
||||||
'indexStats',
|
|
||||||
'facet',
|
|
||||||
'bucket',
|
'bucket',
|
||||||
'bucketAuto',
|
'bucketAuto',
|
||||||
'sortByCount',
|
'collStats',
|
||||||
'addFields',
|
|
||||||
'replaceRoot',
|
|
||||||
'count',
|
'count',
|
||||||
|
'currentOp',
|
||||||
|
'facet',
|
||||||
|
'geoNear',
|
||||||
'graphLookup',
|
'graphLookup',
|
||||||
|
'group',
|
||||||
|
'indexStats',
|
||||||
|
'limit',
|
||||||
|
'listLocalSessions',
|
||||||
|
'listSessions',
|
||||||
|
'lookup',
|
||||||
|
'match',
|
||||||
|
'out',
|
||||||
|
'project',
|
||||||
|
'redact',
|
||||||
|
'replaceRoot',
|
||||||
|
'sample',
|
||||||
|
'skip',
|
||||||
|
'sort',
|
||||||
|
'sortByCount',
|
||||||
|
'unwind',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ALLOWED_KEYS = [...BASE_KEYS, ...PIPELINE_KEYS];
|
||||||
|
|
||||||
export class AggregateRouter extends ClassesRouter {
|
export class AggregateRouter extends ClassesRouter {
|
||||||
|
|
||||||
handleFind(req) {
|
handleFind(req) {
|
||||||
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
|
||||||
const options = {};
|
const options = {};
|
||||||
const pipeline = [];
|
let pipeline = [];
|
||||||
|
|
||||||
for (const key in body) {
|
if (Array.isArray(body)) {
|
||||||
if (ALLOWED_KEYS.indexOf(key) === -1) {
|
pipeline = body.map((stage) => {
|
||||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, `Invalid parameter for query: ${key}`);
|
const stageName = Object.keys(stage)[0];
|
||||||
|
return this.transformStage(stageName, stage);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const stages = [];
|
||||||
|
for (const stageName in body) {
|
||||||
|
stages.push(this.transformStage(stageName, body));
|
||||||
}
|
}
|
||||||
if (key === 'group') {
|
pipeline = stages;
|
||||||
if (body[key].hasOwnProperty('_id')) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.INVALID_QUERY,
|
|
||||||
`Invalid parameter for query: group. Please use objectId instead of _id`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!body[key].hasOwnProperty('objectId')) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.INVALID_QUERY,
|
|
||||||
`Invalid parameter for query: group. objectId is required`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
body[key]._id = body[key].objectId;
|
|
||||||
delete body[key].objectId;
|
|
||||||
}
|
|
||||||
pipeline.push({ [`$${key}`]: body[key] });
|
|
||||||
}
|
}
|
||||||
if (body.distinct) {
|
if (body.distinct) {
|
||||||
options.distinct = String(body.distinct);
|
options.distinct = String(body.distinct);
|
||||||
@@ -76,6 +72,32 @@ export class AggregateRouter extends ClassesRouter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transformStage(stageName, stage) {
|
||||||
|
if (ALLOWED_KEYS.indexOf(stageName) === -1) {
|
||||||
|
throw new Parse.Error(
|
||||||
|
Parse.Error.INVALID_QUERY,
|
||||||
|
`Invalid parameter for query: ${stageName}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stageName === 'group') {
|
||||||
|
if (stage[stageName].hasOwnProperty('_id')) {
|
||||||
|
throw new Parse.Error(
|
||||||
|
Parse.Error.INVALID_QUERY,
|
||||||
|
`Invalid parameter for query: group. Please use objectId instead of _id`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!stage[stageName].hasOwnProperty('objectId')) {
|
||||||
|
throw new Parse.Error(
|
||||||
|
Parse.Error.INVALID_QUERY,
|
||||||
|
`Invalid parameter for query: group. objectId is required`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
stage[stageName]._id = stage[stageName].objectId;
|
||||||
|
delete stage[stageName].objectId;
|
||||||
|
}
|
||||||
|
return { [`$${stageName}`]: stage[stageName] };
|
||||||
|
}
|
||||||
|
|
||||||
mountRoutes() {
|
mountRoutes() {
|
||||||
this.route('GET','/aggregate/:className', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleFind(req); });
|
this.route('GET','/aggregate/:className', middleware.promiseEnforceMasterKeyAccess, req => { return this.handleFind(req); });
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user