-1

Comments are saved in an array of objects. How can I correctly output them in reverse order (comments from newest to oldest)?

My db:

{"_id":{"$oid":"5e3032f14b82d14604e7cfb7"},
"videoId":"zX6bZbsZ5sU",
"message":[
    {"_id":{"$oid":"5e3032f14b82d14604e7cfb8"},
    "user":{"$oid":"5e2571ba388ea01bcc26bc96"},"text":"1"
    },
     {"_id":{"$oid":"5e3032f14b82d14604e7cfb9"},
    "user":{"$oid":"5e2571ba388ea01bcc26bc96"},"text":"2"
    },
    ....
    ]

My sheme Mongoose:

const schema = new Schema({
videoId: { type: String, isRequired: true },
message: [
{
  user: { type: Schema.Types.ObjectId, ref: 'User' },
  text: { type: String }
},

]
});

My code:

const userComments = await Comment.find(
  { videoId: req.query.videoId },
  { message: { $slice: [skip * SIZE_COMMENT, SIZE_COMMENT] } }
)
  .sort({ message: -1 })
  .populate('message.user', ['avatar', 'firstName']);

but sort not working;

thanks in advance!

Andrey
  • 13
  • 4
  • Do you just wanted to reverse an array or slice it as well ? – whoami - fakeFaceTrueSoul Jan 28 '20 at 20:07
  • slice the array for output in parts and reverse – Andrey Jan 29 '20 at 06:06
  • Instead check my answer it will reverse the whole array and after that you can apply slice if needed and send limited objects in an array.. – whoami - fakeFaceTrueSoul Jan 29 '20 at 06:08
  • Thank you very much for your answer! Please explain why sort or sort({$natural:-1}) does not work?I'm new to mongodb. And if there is a large array of comments, $reverseArray will not be a slow option? Can I use userComments.aggregate([ { $project: { message: { $reverseArray: "$message" } } } ]);after using part of my code – Andrey Jan 29 '20 at 07:07
  • `$sort` or `.sort()` doesn't work on field type array !! So in order sort elements in an array you need to do `$unwind`->sort->`$group`. So if you just wanted to reverse the order of elements in an array then why are you thinking about sorting ? Slowness of a query depends on multiple factors, Since you already have a filter `{ videoId: req.query.videoId }` then probably documents will be very less & count of secondly elements in an array - How many do you've(max) ? Just try using `$explain` to check your query performance, but if data set is slow please go ahead with `$reverseArray` – whoami - fakeFaceTrueSoul Jan 29 '20 at 15:42
  • 1
    @whoami,thanks for your answer. The solution worked. But another task arrived, to display data by date.if you can, advise how to do it :https://stackoverflow.com/questions/60169726/how-to-display-message-by-date-added-from-new-to-old – Andrey Feb 13 '20 at 12:02

2 Answers2

1

You can simply use $reverseArray to reverse content of an array.

db.collection.aggregate([
   {
     $addFields:
      {
         message: { $reverseArray: "$message" }
      }
   }
])

Collection Data :

/* 1 */
{
    "_id" : ObjectId("5e3032f14b82d14604e7cfb7"),
    "videoId" : "zX6bZbsZ5sU",
    "message" : [ 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfb8"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc96"),
            "text" : "1"
        }, 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfb9"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc96"),
            "text" : "2"
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("5e309318d02e05b694b0b25f"),
    "videoId" : "zX6bZbsZ5sUNEWWWW",
    "message" : [ 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfc9"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc87"),
            "text" : "Old"
        }, 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfd0"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc87"),
            "text" : "New"
        }
    ]
}

Result :

/* 1 */
{
    "_id" : ObjectId("5e3032f14b82d14604e7cfb7"),
    "videoId" : "zX6bZbsZ5sU",
    "message" : [ 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfb9"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc96"),
            "text" : "2"
        }, 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfb8"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc96"),
            "text" : "1"
        }
    ]
}

/* 2 */
{
    "_id" : ObjectId("5e309318d02e05b694b0b25f"),
    "videoId" : "zX6bZbsZ5sUNEWWWW",
    "message" : [ 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfd0"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc87"),
            "text" : "New"
        }, 
        {
            "_id" : ObjectId("5e3032f14b82d14604e7cfc9"),
            "user" : ObjectId("5e2571ba388ea01bcc26bc87"),
            "text" : "Old"
        }
    ]
}

Your Query : You can use native MongoDB's $lookup instead of .populate() , So try below :

Comments.aggregate([
    {
        $addFields:
        {
            message: { $reverseArray: "$message" }
        }
    }, {
        $lookup: {
            from: "User",
            let: { ids: "$message.user" },
            pipeline: [
                {
                    $match: { $expr: { $in: ["$_id", "$$ids"] } }
                },
                { $project: { avatar: 1, firstName: 1, _id: 0 } }
            ],
            as: "userData"
        }
    }
])
whoami - fakeFaceTrueSoul
  • 17,086
  • 6
  • 32
  • 46
0

You're going to want to use one of two things:

  1. The MongoDB aggregation framework
  2. Sorting within the application

If you choose to use the MongoDB aggregation framework, you'll likely want to use the $unwind operation to expand the array into separate documents, then sorting those documents with the $sort operation.

You can see more on how to do that in this ticket:

how to sort array inside collection record in mongoDB

You could also do this within your application by first executing a query, and then sorting each of the arrays in the result set.

Best,

Nic Raboy
  • 3,143
  • 24
  • 26