Shri
Shri

Reputation: 752

Mongo DB reverse an array field before slicing

I have this mongo query to get the list of messages in a thread.

await db
      .collection("threads")
      .find({
        _id: ObjectId(req.query.thread_id),
      })
      .project({
        messages: {
          $slice: [parseInt(req.query.offset), parseInt(req.query.limit)],
        },
        _id: 1,
        date_created: 1,
      })
      .toArray();

The limit and offset is set by the client to support unlimited loading. The problem is the query gets result in the order they were added whereas it should paginate with last added messages first. How do I go about this? Is it possible that I can reverse an array before fetching or is it too much load?

This is my thread schema:

{
   "_id":{
      "$oid":"5f7b32c014014100176c2a55"
   },
   "thread_name":"personal",
   "messages":[
      {
         "_id":{
            "$oid":"5f7b32d414014100176c2a56"
         },
         "sender":{
            "$oid":"5f7b329114014100176c2a52"
         },
         "type":"text",
         "date_created":{
            "$date":"2020-10-05T14:51:00.716Z"
         },
         "content":"hi"
      },
      {
         "_id":{
            "$oid":"5f7b32df14014100176c2a57"
         },
         "sender":{
            "$oid":"5f7b329114014100176c2a52"
         },
         "type":"text",
         "date_created":{
            "$date":"2020-10-05T14:51:11.302Z"
         },
         "content":"hello"
      },
   ],
   "date_created":{
      "$date":"2020-10-05T14:50:40.271Z"
   },
}

Upvotes: 1

Views: 688

Answers (2)

Tom Slabbaert
Tom Slabbaert

Reputation: 22276

"Is it possible that I can reverse an array before fetching?" You can't reverse what you don't have. I've seen the other answer with $reverseArray but why spend resources reversing an array when you can just $slice the last elements?

If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if is specified

db.collection("threads").aggregate([
  {
    $match: {
      _id: ObjectId(req.query.thread_id),
    },
  },
  {
    $project: {
      _id: 1,
      date_created: 1,
      reverseMessages: {
        $slice: [
          "$messages",
          parseInt(req.query.limit) * -1
        ]
      }
    }
  }
]);

Upvotes: 1

Nenad Jovicic
Nenad Jovicic

Reputation: 185

You can use $reverseArray option in aggregation

With aggregate, you can use first $match (it will replace your find part), and then, with $project you can add new property like reversedMessages, where you will use $reverseArray and $slice combined

so, query should be something like this

db.collection("threads").aggregate([
  {
    $match: {
      _id: ObjectId(req.query.thread_id),
    },
  },
  {
    $project: {
      _id: 1,
      date_created: 1,
      reverseMessages: {
        $slice: [
          { $reverseArray: "$messages" },
          0,
          parseInt(req.query.limit), // I guess you want to return latest n messages, where n is a limit from query
        ],
      },
    },
  },
]);

Upvotes: 1

Related Questions