Dimitri Badicean
Dimitri Badicean

Reputation: 87

How can I sort array by a field inside a MongoDB document

I have document called question

var QuestionSchema = new Schema({
    title: {
        type: String,
        default: '',
        trim: true
    },
    body: {
        type: String,
        default: '',
        trim: true
    },
    user: {
        type: Schema.ObjectId,
        ref: 'User'
    },
    category: [],
    comments: [{
        body: {
            type: String,
            default: ''
        },
        root: {
            type: String,
            default: ''
        },
        user: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        },
        createdAt: {
            type: Date,
            default: Date.now
        }
    }],
    tags: {
        type: [],
        get: getTags,
        set: setTags
    },
    image: {
        cdnUri: String,
        files: []
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
});

As a result, I need to sort comments by root field, like this example

I tried to sort the array of comments manually at backend and tried to use aggregation, but I was not able to sort this. Help please.

Upvotes: 2

Views: 188

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50416

Presuming that Question is a model object in your code and that of course you want to sort your "comments by "date" from createdAt then using .aggregate() you would use this:

Question.aggregate([
    // Ideally match the document you want
    { "$match": { "_id": docId } },

    // Unwind the array contents
    { "$unwind": "comments" },

    // Then sort on the array contents per document
    { "$sort": { "_id": 1, "comments.createdAt": 1 } },

    // Then group back the structure
    { "$group": {
        "_id": "$_id",
        "title": { "$first": "$title" },
        "body": { "$first": "$body" },
        "user": { "$first": "$user" },
        "comments": { "$push": "$comments" },
        "tags": { "$first": "$tags" },
        "image": { "$first": "$image" },
        "createdAt": { "$first": "$createdAt" }
    }}
],
function(err,results) {
    // do something with sorted results
});

But that is really overkill since you are not "aggregating" between documents. Just use the JavaScript methods instead. Such as .sort():

Quesion.findOneById(docId,function(err,doc) {
    if (err) throw (err);
    var mydoc = doc.toObject();
    mydoc.questions = mydoc.questions.sort(function(a,b) {
        return a.createdAt > b.createdAt;
    });
   console.log( JSON.stringify( doc, undefined, 2 ) ); // Intented nicely
});

So whilst MongoDB does have the "tools" to do this on the server, it makes the most sense to do this in client code when you retrieve the data unless you actually need to "aggregate" accross documents.

But both example usages have been given now.

Upvotes: 3

Related Questions