In MongoDB search in an array and sort by number of matches

后端 未结 2 1503
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-12 00:27

The question is the next one:

Get documents with tags in list, ordered by total number of matches

But they say that is possible using Aggregation Framework,

相关标签:
2条回答
  • 2020-12-12 01:02

    Yes, it's possible using Aggregation Framework.

    Assumptions

    • The dataset used here is the same used in Get documents with tags in list, ordered by total number of matches
    • tags attribute is a set (no repeated elements)

    Query

    This approach forces you to unwind the results and reevaluate the match predicate with unwinded results, so its really inefficient.

    db.test_col.aggregate(
        {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
        {$unwind: "$tags"}, 
        {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
        {$group: {
            _id:{"_id":1}, 
            matches:{$sum:1}
        }}, 
        {$sort:{matches:-1}}
    );
    

    Expected Results

    {
        "result" : [
            {
                "_id" : {
                    "_id" : ObjectId("5051f1786a64bd2c54918b26")
                },
                "matches" : 3
            },
            {
                "_id" : {
                    "_id" : ObjectId("5051f1726a64bd2c54918b24")
                },
                "matches" : 2
            },
            {
                "_id" : {
                    "_id" : ObjectId("5051f1756a64bd2c54918b25")
                },
                "matches" : 1
            }
        ],
        "ok" : 1
    }
    
    0 讨论(0)
  • 2020-12-12 01:13

    Using $size and $setIntersection will solve this efficiently, without causing memory multiplication.

    tagList = ['shirt', 'cotton', 'black']
    
    db.test_col.aggregate(
        {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
        {$project: 
            {"title":1,"tags":1}, 
            {$order: 
                {"$size": 
                    {"$setIntersection": [ tagList, "$tags" ]}}, 
        {$sort:{order:-1}}
        );
    
    1. First we match the documents which have at least one element matching.

    2. Then we project Keys/Columns we need along with a new order key/column. Order is generated by taking the count of intersected elements between 'tags in db' and 'tags from query'.

    3. Then we do a simple sort in descending order. This worked for me. Similar Question answered here

    0 讨论(0)
提交回复
热议问题