Support for Aggregate Queries#4207
Conversation
Codecov Report
@@ Coverage Diff @@
## master #4207 +/- ##
==========================================
+ Coverage 92.5% 92.63% +0.12%
==========================================
Files 118 119 +1
Lines 8256 8400 +144
==========================================
+ Hits 7637 7781 +144
Misses 619 619
Continue to review full report at Codecov.
|
|
@flovilmart Can you think of any other tests? For Postgres we can add more functionality as it is requested. Also the pipeline is ordered so we don't have to worry about that. |
flovilmart
left a comment
There was a problem hiding this comment.
Tests look great, a few nits. I’ll do a larger review later tonight! Awesome job man!
spec/ParseQuery.Aggregate.spec.js
Outdated
| it('group sum query', (done) => { | ||
| const options = Object.assign({}, masterKeyOptions, { | ||
| body: { | ||
| group: { _id: null, total: { $sum: '$score' } }, |
There was a problem hiding this comment.
_id should be transformed (from objectId), which would be true for all keys passed here :)
There was a problem hiding this comment.
So group: { objectId: null, total: { $sum: '$score' } }
And it should return [ { objectId: null, total: ... } ] instead of [ { _id: null, total: ... } ]
and group: { objectId: '$name', total: { $sum: '$score' } }
Should return [ { name: ..., total: ... } ]
There was a problem hiding this comment.
Yep, in and out, those should not be the internal storage columns but the externals
There was a problem hiding this comment.
Should an error be thrown if _id is used?
| results[0][countField] = parseInt(results[0][countField], 10); | ||
| } | ||
| results.forEach(result => { | ||
| if (result.hasOwnProperty('_id')) { |
There was a problem hiding this comment.
Can’t we use the usual transformers here?
There was a problem hiding this comment.
I was just about to change it
There was a problem hiding this comment.
I don't think we can use the usual transformers like buildWhereClause. We might be able to reuse skip, limit, sort that is in find(). It not complete yet but I could try to refactor it in the second iteration
|
@dplewis great job! You could also add a config attribute for the |
|
@benishak Thanks, I don't know much about class level permissions. Are CLP needed here or are they overriden when you use masterKey which is required here? |
|
CLP don’t really matter for that iteration, as all endpoints are protected by masterKey. It seems unsafe to allow any call to the aggregation pipeline without a masterKey. |
|
yeah I fully agree, default behavior should be masterkey only, but I imagine there could be also use cases where non master user would need to run such query like Data analytics dashboards, CRM ... etc. Therefore I thought to put some flexibility but make sure that the default is masterkey only. |
|
@vitaly-t more bad habits, can you look this over? |
|
@dplewis same story, in terms of the connections usage it all seems fine, but in terms of using ES6 strings to format queries - it is all over the place :) |
| .then((resp) => { | ||
| expect(resp.results[0].hasOwnProperty('objectId')).toBe(true); | ||
| expect(resp.results[0].objectId).toBe(null); | ||
| expect(resp.results[0].total).toBe(50); |
There was a problem hiding this comment.
Just curious what happens if one of the score values happens to be unset/null? I'm assuming it just omits it but would be nice to know for sure.
There was a problem hiding this comment.
Just tried. If unset/null it gets ignored
| }).catch(done.fail); | ||
| }); | ||
|
|
||
| it('sort ascending query', (done) => { |
There was a problem hiding this comment.
This looks like it duplicates functionality we already have in standard queries, is this needed for something in particular where the original won't do? Read through the rest, nvm this comment.
There was a problem hiding this comment.
As the user builds their aggregate queries they have the option to limit, sort, skip. This test is also there for Postgres support
There was a problem hiding this comment.
Yeah I realized that right after I noted this, you're good here.
spec/ParseQuery.Aggregate.spec.js
Outdated
| }).catch(done.fail); | ||
| }); | ||
|
|
||
| it('distint query with where', (done) => { |
spec/ParseQuery.Aggregate.spec.js
Outdated
| }).catch(done.fail); | ||
| }); | ||
|
|
||
| it('distint query with where string', (done) => { |
| } | ||
|
|
||
| distinct(className, schema, query, fieldName) { | ||
| debug('distinct', className, query); |
There was a problem hiding this comment.
Just curious what's the Nvm, logger.debug mark here for?
There was a problem hiding this comment.
Passing PARSE_SERVER_LOG_LEVEL=debug
It really helps for testing PG
| debug('distinct', className, query); | ||
| let field = fieldName; | ||
| let column = fieldName; | ||
| if (fieldName.indexOf('.') >= 0) { |
There was a problem hiding this comment.
Although this will work the value won't ever be 0, just -1 or > 1. Doesn't mean this needs to be changed, but it could also be >= 1 for be more specific about what's expected.
There was a problem hiding this comment.
const str = '.';
str.indexOf('.') will return 0;
It will never happen but it covers all cases
| if (fieldName.indexOf('.') === -1) { | ||
| return results.map(object => object[field]); | ||
| } | ||
| const child = fieldName.split('.')[1]; |
There was a problem hiding this comment.
If I pass a field name like myField. I think this would bug out. Can you check on/add a case to look at nested behavior where the dot is missing following text?
There was a problem hiding this comment.
Additionally something like .myField should be invalid. I'm not sure if they are currently prevented though, if they aren't they should probably be screened ahead.
There was a problem hiding this comment.
Wouldn't myfield. be an invalid field name?
There was a problem hiding this comment.
Yep, just covering bases. Took a look, this is good too.
| if (sort[key] === 1) { | ||
| return `"${key}" ASC`; | ||
| } | ||
| return `"${key}" DESC`; |
There was a problem hiding this comment.
Perhaps a bit silly, but you could mark sort with a value of 2 or greater and it would technically fall into the -1 descending category.
| body[key]._id = body[key].objectId; | ||
| delete body[key].objectId; | ||
| } | ||
| pipeline.push({ [`$${key}`]: body[key] }); |
There was a problem hiding this comment.
$$? Double checking that this isn't just a working typo...
There was a problem hiding this comment.
Scratch this, you're good here as well 👍
montymxb
left a comment
There was a problem hiding this comment.
Changes look good, just a couple typos from what I could tell.
|
@flovilmart as it stands I think this is ready, but I want to have a second opinion before we go ahead and merge it in. |
|
@flovilmart can we get this one in? |
|
Yes, let’s go! |
|
@dplewis just a quick thing, looks like we still need to properly clear out |
|
Hi @dplewis there are some docs on how to use it? What about the client SDK's? for iOS / Android ? thanks, |
|
@ranhsd To use this master key is required. iOS and Android cant use master key. |
|
Hi @dplewis yes just noticed that. so I think we need to mention there that it can be achieved via cloud code. |
* Support for Aggregate Queries * improve pg and coverage * Mongo 3.4 aggregates and tests * replace _id with objectId * improve tests for objectId * project with group query * typo
Adds a new endpoint for distinct and aggregate queries.
Basic support for Postgres aggregates (WIP).
Closes: #4197, #2238