Skip to content

Uncaught error while using DOT '.' notation on undefined field #5030

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
seza443 opened this issue Sep 5, 2018 · 5 comments · Fixed by #5917
Closed

Uncaught error while using DOT '.' notation on undefined field #5030

seza443 opened this issue Sep 5, 2018 · 5 comments · Fixed by #5917

Comments

@seza443
Copy link

seza443 commented Sep 5, 2018

Issue Description

When building a query with a matchesKeyInQuery operator with a DOT notation (made available in #4399 thanks for that), an uncaught error occurs if the first part of the DOT notation is undefined.

Steps to reproduce

let subQuery = new Parse.Query('SubClass');
subQuery.equalTo('condition', 'not_important'); // any condition // don't care

let parentQuery = new Parse.Query('ParentClass');
parentQuery.matchesKeyInQuery('objectId', 'parent.objectId', subQuery); // "parent" is undefined for some of the sub-results

parentQuery.find({
    success: (results) => {
        console.log(results);
    },
    error: (error) => {
        console.log(error);
    }
});

SubClass has a Pointer parent to ParentClass but sometimes, it is undefined.

Expected Results

I expect to see the results matching the filters and ignoring the fields that are undefined

Actual Outcome

I receive an Undefined error

ParseError { code: 1, message: undefined }

Server logs:

{"level":"error","message":"Uncaught internal server error. TypeError: Cannot read property 'objectId' of undefined\n    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)\n    at Array.reduce (native)\n    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)\n    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)\n    at process._tickDomainCallback (internal/process/next_tick.js:129:7) TypeError: Cannot read property 'objectId' of undefined\n    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)\n    at Array.reduce (native)\n    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)\n    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)\n    at process._tickDomainCallback (internal/process/next_tick.js:129:7)"}
TypeError: Cannot read property 'objectId' of undefined
    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)
    at Array.reduce (native)
    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)
    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)
    at process._tickDomainCallback (internal/process/next_tick.js:129:7)

Environment Setup

  • Server

    • parse-server version (Be specific! Don't say 'latest'.) : 2.7.4
    • Operating System: Ubuntu 16.04
    • Hardware: Intel i7 16 GO RAM
    • Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [FILL THIS OUT]
  • Database

    • MongoDB version: 3.2
    • Storage engine: S3
    • Hardware: Intel i7 16 GO RAM
    • Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): Local

Logs/Trace

{"level":"error","message":"Uncaught internal server error. TypeError: Cannot read property 'objectId' of undefined\n    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)\n    at Array.reduce (native)\n    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)\n    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)\n    at process._tickDomainCallback (internal/process/next_tick.js:129:7) TypeError: Cannot read property 'objectId' of undefined\n    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)\n    at Array.reduce (native)\n    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)\n    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)\n    at process._tickDomainCallback (internal/process/next_tick.js:129:7)"}
TypeError: Cannot read property 'objectId' of undefined
    at values.push.key.split.reduce (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:50)
    at Array.reduce (native)
    at transformSelect (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:330:32)
    at subquery.execute.then.response (/home/app/parse/node_modules/parse-server/lib/RestQuery.js:369:5)
    at process._tickDomainCallback (internal/process/next_tick.js:129:7)
@seza443
Copy link
Author

seza443 commented Sep 5, 2018

I guess it as simple as adding a if before this line

values.push(key.split('.').reduce((o, i) => o[i], result));

(and this one

values.push(key.split('.').reduce((o, i) => o[i], result));
)

But I'm not sure how to break it down 🤔

Or just encapsulate it into a try/catch block. Is that compliant to the good-practices of this repo ?

@flovilmart
Copy link
Contributor

Please open a pull request. Using a try catch won’t help as it won’t provide any additional informations

@JacobJT
Copy link

JacobJT commented Sep 18, 2018

Similar to #4740 , which is the same issue but for doesNotMatchKeyInQuery

@JacobJT
Copy link

JacobJT commented Dec 3, 2018

I'll have to read up on how to submit a PR later. I found this SO post helpful.
https://stackoverflow.com/questions/36144406/how-to-break-on-reduce-method

const getDeepestObjectFromKey = (acc, cur idx, src) => {
    if( acc[cur] )
        return acc[cur];
    src.splice(1); // This chops the rest of the array off, so the reduce function will no longer continue
    return undefined;
};
// Optionally add a `.slice(0)` after the `split('.')` to make a copy of the array. Although, here, this is not necessary since we aren't storing the original array, anyway.
const value = key.split('.').reduce(getDeepestObjectFromKey, result);
if( value )
    values.push(value);

This essentially implements break within the reduce() method. reduce() would normally continue iterating over the array, using the third (index) and 4th (array reference) values of the function passed in to reduce(). So, by chopping off everything from the array, it'll fail the check to continue the loop on the next pass, leaving us with whatever we decide to return into the totalValue register.

I moved it out of the values.push() so that we could only call that conditionally on a value actually existing, as at least in my browser 'undefined' actually gets added to the array. That being said, if there was no value before 'undefined' would still get added to the array, and didn't seem to cause any problems. I suppose this could be changed to, which would maintain that "legacy" functionality. But I took it as more of an oversight, since the array would then have as many values as the array of results from the first query, even if only a single object with the relevant field we're looking for existed.

values.push(key.split('.').reduce(getDeepestObjectFromKey, result));

That would replace both of the lines @seza443 pointed out.

Relevant references:
slice
splice
reduce

Also, I have a test case in #4740, regarding this same issue I can move over.
Once I get time to familiarize myself with the PR process, I'll put it up.

@dplewis
Copy link
Member

dplewis commented May 13, 2019

@JacobJT would you be willing to write a failing test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants