Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB Aggregate: Sorting by existing values first

My user has this field :

interestedIn: [{
        type: String,
        enum: [
            'art',
            'sport',
            'news',
            'calture',
            ...
        ],
    }],

and my video has this field:

categories: [{
            type: String,
            enum: [
                'art',
                'sport',
                'news',
                'calture',
                ...
            ],
        }],

So I need a Video query that has the following conditions:

  • Query to all videos and sort by values in req.user.interestedIn first.
  • Rest of the videos that are not match to req.user.interestedIn get in the last.

I got this far with said query:

Video.aggregate([

        { '$match': {}},
        { '$unwind': '$categories' },
        { '$match': {categories: {$in: req.user.interestedIn}}},
        { '$group': {
            '_id': '$categories',
            'categories': { '$push': '$categories' }
        }},
        { '$sort': { 'categories': 1 } }
    ])

This is the result:

   "videos": [
        {
            "_id": "art",
            "categories": [
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art"
            ]
        },
        {
            "_id": "news",
            "categories": [
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
   "videos": [
        {
            "_id": "art",
            "categories": [
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art",
                "art"
            ]
        },
        {
            "_id": "news",
            "categories": [
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news",
                "news"
            ]
        }
    ]
}
like image 738
Babak Abadkheir Avatar asked Oct 16 '18 11:10

Babak Abadkheir


People also ask

How do I sort data in MongoDB aggregate?

In MongoDB, the $sort stage is used to sort all the documents in the aggregation pipeline and pass a sorted order to the next stage of the pipeline. Lets take a closer look at the above syntax: The $sort stage accepts a document that defines the field or fields that will be used for sorting.

How do I sort an array in MongoDB aggregation?

To sort the whole array by value, or to sort by array elements that are not documents, identify the input array and specify 1 for an ascending sort or -1 for descending sort in the sortBy parameter.

What does $first in MongoDB?

This means $first returns the first order type for the documents between the beginning of the partition and the current document.


1 Answers

You can use $setIntersection to extract the matching elements followed by $size to count the matches.

$sort descending documents on number of matches.

Something like

Video.aggregate([ 
  {"$addFields":{ "numofmatches":{"$size":{"$setIntersection":["$categories", req.user.interestedIn]}}}}, 
  {"$sort":{"numofmatches":-1}} 
])
like image 54
s7vr Avatar answered Oct 21 '22 21:10

s7vr