Howto: Resolving parse pointer in aggregations

I use an aggregation to resolve parse pointer referencing the user class. Just wanted to share the solution, since I could not find a solution to this in the docs.

Setup: Product class referencing a Parse.User

// Product uses a pointer to User
// user must be a Parse.User
await new Product().set("description", "foo 9000").set("createdBy", user).save();

Resolve pointer in aggregation
Goal is to create list of all products with the Users referenced by field “createdBy” resolved.

// pipeline to list all products with their users
const pipeline = [
  {
    // A parse pointer looks like this in MongoDB:
    // _p_createdBy:"_User$iGy2IHfsRf3"
    // extract the user objectId from the pointer, store it as a new field
    // I rely on the fact, that the prefix "_User$" is constant
    addFields: {
      userId: { $substrBytes: ["$_p_createdBy", 6, 100] },
    },
  },
  {
    lookup: {
      from: "_User", // name of the Parse.User class within MongoDB
      localField: "userId", // the user id we just extracted from the pointer
      foreignField: "_id", // the user id in the _User collection
      as: "UserLookup", // the array to store the lookup
    },
  },
  {
    addFields: {
      user: { $arrayElemAt: ["$UserLookup", 0] }, // pick the first element from the lookup
    },
  },
];

const query = new Parse.Query("Product");
return await query.aggregate(pipeline);

Alternatives

  • do not use aggregations for basic listing, just load the Product class use a standard Parse.Query and use “.include” to load the User at the same time.
3 Likes

That would be great to be documented. Would you be willed to open a PR to the docs repository? GitHub - parse-community/docs: Parse Platform docs

I was just wondering how to do this and took a chance searching here. Thanks for sharing your findings!

1 Like

Thank you, thank you, thank you! You’re a life saver! :blue_heart:

1 Like

Another solution:

userId: { $substrCP: ["$_p_createdBy", 6, { $subtract: [ { $strLenCP: "$_p_createdBy" }, 6 ] }] },

It uses UTF-8 code points instead of a raw byte count and reads exactly to the end of the string. It shouldn’t matter much for Parse pointers, as we can assume they will always consist only of ASCII characters, but it doesn’t limit to the arbitrary 100 characters.