Skip to content

Aggregate pipeline with $match on a Pointer reference #4640

Closed
@joshkopecek

Description

@joshkopecek

Issue Description

When using a Pointer for matches on an aggregation query, the system queries against the queried object's class, not against the Pointer's class

Steps to reproduce

Run an aggregate query against an object with a Pointer reference:
Snapp.region is a 'Region' pbje
e.g.

let mostPopularSnapps = new Parse.Query(Parse.Object.extend('Snapp'))
let region = Parse.Object.extend('Region')
region.id = "CLtNoVCMF8"
const pipeline = [
  { match: { region: "CLtNoVCMF8" } },
  { group: { objectId: '$_p_expertIdentifiedPlant', count: { $sum: 1 } } },
  { sort: { count: -1 } }
]
mostPopularSnapps
  .aggregate(pipeline, { useMasterKey: true })
  .then(items => {
    console.log(items)
  })

should match the Mongo pipeline:

[
	{ $match: { _p_region: /CLtNoVCMF8$/ }}, 
	{ $group: { _id: "$_p_expertIdentifiedPlant", count: { $sum: 1 } } }, 
	{ $sort: { count: -1 } }
]

Expected Results

I expect it to return results matching only that region (by id).

Actual Outcome

It returns 0 results, as the match pipeline is malformed.

Environment Setup

  • Server

    • parse-server version (Be specific! Don't say 'latest'.) : [2.7.2]
    • Operating System: [MacOS]
    • Hardware: [MacBook Pro 13 inch mid-2012]
    • Localhost or remote server? (AWS, Heroku, Azure, Digital Ocean, etc): [localhost]
  • Database

    • MongoDB version: [3.6.2]
    • Storage engine: [WiredTiger]
    • Hardware: [Same as above]
    • Localhost or remote server? (AWS, mLab, ObjectRocket, Digital Ocean, etc): [Same as above]

Logs/Trace

None

Potential PR

In MongoStorageAdapter.js:498, the stage.$match function parses the pipeline match like so:

if (stage.$match) {
        for (const field in stage.$match) {
          if (schema.fields[field] && schema.fields[field].type === 'Pointer') {
            const transformMatch = { [`_p_${field}`]: `${className}$${stage.$match[field]}` };
            stage.$match = transformMatch;
          }
          if (field === 'objectId') {
            const transformMatch = Object.assign({}, stage.$match);
            transformMatch._id = stage.$match[field];
            delete transformMatch.objectId;
            stage.$match = transformMatch;
          }
        }
      }

It seems to me that this line:

const transformMatch = { [`_p_${field}`]: `${className}$${stage.$match[field]}` };

should be:

const transformMatch = { [`_p_${field}`]: `${schema.fields[field].targetClass}$${stage.$match[field]}` };

Happy to open a PR if this should be correct behaviour? I'm relatively new to Parse, so want to confirm what the outcome should be here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions