Is there a better way to slice an array field after an index in a mongodb document?

Viewed 29

I have an order field in my document that contains reference IDs to documents in another collection.

product: {
  _id: // product id,
  orders: [refId_1415, refId_5414, refId_0210, refId_4320, refId_1543, refId_4326, refId_7845]
}

Since, they are not in ascending/descending order, I can't really run $gt or $lt to retrieve the values. So, I have to get all the refIds after a certain refId given by the user. Suppose, refId_5414 is given by the user. So, starting at index 2 (refId_0210) run a $lookup to return an array with the values populated like so:

order_history: [{
  id: refId_0210,
  created: // Date
}, 
{
  id: refId_4320,
  created: // Date
},
  ...
{ 
  id: refId_7845,
  created: // Date
}];

This is the query that I have come up with and it works, however there's a lot of stages to this and I was wondering if there is a better way to do this:

  1. Find the product with the id.
{
  $match: { _id: product._id }, 
},
  1. Find the index of the id preceding the required starting index since that's what we'll receive.
{
  $project: {
    index: {
      $indexOfArray: ["$orders", "refId_5414"],
    },
    orders: 1,
  },
},
  1. Increase the index from the previous stage by 1 to get the required starting index.
{
  $project: {
    index: { $add: ["$index", 1] },
    orders: 1,
  },
},
  1. Slice the array from the starting index to the given index.
{
  $project: {
    orders: { $slice: ["$orders", "$index", to] },
    _id: 0,
  },
},
  1. Repopulate the array
{
  $lookup: {
    from: "orders",
    let: { orders: "$orders" },
    pipeline: [{ $match: { $expr: { $in: ["$_id", "$$orders"] } } }],
    as: "order_history",
  },
},

This is how it looks (Notice the 3 project stages):

const res = await this.collection.aggregate([
  {
    $match: { _id: product._id },
  },
  {
    $project: {
      ...
    },
  },
  {
    $project: {
      ...
    },
  },
  {
    $project: {
      ...
    },
  },
  {
    $lookup: {
    ...
    },
  },
])

Any advice to make it better would be greatly appreciated. Thank you.

1 Answers

Your query looks good, you can reduce multiple $project stages and use simple lookup:

  { $match: { _id: 1 } },
  {
    $project: {
      _id: 0,
      orders: {
        $slice: [
          "$orders",
          {
            $add: [
              { $indexOfArray: ["$orders", "refId_5414"] },
              1
            ]
          },
          to
        ]
      }
    }
  },
  {
    $lookup: {
      from: "orders",
      localField: "orders",
      foreignField: "id",
      as: "orders"
    }
  },
  { $project: { orders: "$orders.id" } }

Playground

Related