按数组大小对结果进行排序

Posted

技术标签:

【中文标题】按数组大小对结果进行排序【英文标题】:Sort results by array size 【发布时间】:2016-01-06 21:38:16 【问题描述】:

我有一个名为 article 的集合,我需要按照它包含的数组的大小对返回的对象进行排序。做这个的最好方式是什么? 我想到的一种方法是检索整个对象列表并在 JS 中手动对其进行排序。但是我一次返回 10 篇文章,所以每次调用这个 API 时,它都必须做不必要的数组排序工作。

Article.find()
     .limit(10)
     .skip(req.params.page*10)
     //Something like using $project to make a new variable called votecount that counts objects in a given array.
     .sort(votecount:-1)
     .exec(function(err,arts)
      articleObj.articles = arts;
        if (arts.length<10)
          articleObj.reachedEnd = true;
        
        res.json(articleObj);
     );

我需要计算票数。这是一个示例对象:


"_id" : ObjectId("55f50cfddcf1ad6931fb8dd4"),
"timestamp" : "2015-09-13T00:58:57-5:00",
"url" : "http://www.nytimes.com/2015/09/13/sports/floyd-mayweather-finishes-bout-and-maybe-his-career-with-lopsided-win-over-andre-berto.html",
"abstract" : "Mayweather’s victory by unanimous decision gave him a record of 49-0, the same as the legendary heavyweight Rocky Marciano.",
"title" : "Mayweather Wins Easily in What He Calls Last Bout",
"section" : "Sports",
"comments" : [ ],
"votes" : 
    "up" : [
        ObjectId("55e5e16934d355d61c471e48")
    ],
    "down" : [ ]
,
"image" : 
    "caption" : "Floyd Mayweather Jr. after learning he defeated Andre Berto in a unanimous decision.",
    "url" : "http://static01.nyt.com/images/2015/09/14/sports/13fight/13fight-mediumThreeByTwo210.jpg"
,
"__v" : 0

【问题讨论】:

【参考方案1】:

您需要使用.aggregate() 方法,因为所有“排序”参数都必须是文档中存在的字段,这样您就可以将数组的$size“投影”到文档中进行排序:

Article.aggregate(
    [
         "$project": 
            "timestamp": 1,
            "url": 1,
            "abstract": 1,
            "title": 1,
            "section": 1,
            "comments": 1,
            "votes": 1,
            "image": 1,
            "voteCount":  
                "$subtract": [
                     "$size": "$votes.up" ,
                     "$size": "$votes.down" 
                ]
            
        ,
         "$sort":  "voteCount": -1  ,
         "$skip": req.params.page*10 ,
         "$limit": 10 ,
    ],
    function(err,results) 
        // results here
    
);

当然,这是有代价的,因为您基本上需要在每次迭代中计算大小。因此,更好的做法是在每次更新操作时保持文档中的“计数”,并且更好地使用Bulk Operations 以适应所有情况:

var bulk = Atricle.collection.intializeOrderedBulkOp();

// Swap out a downvote where present
bulk.find( 
    "_id": id, 
    "votes.up":  "$ne": userId ,
    "votes.down": userId
).updateOne(
    "$push":  "votes.up": userId ,
    "$pull":  "votes.down": userId 
    "$inc":  "voteCount": 2 
);

// Add an upvote where not present
bulk.find( 
    "_id": id, 
    "votes.up":  "$ne": userId ,
    "votes.down":  "$ne": userId 
).updateOne(
    "$push":  "votes.up": userId ,
    "$inc":  "voteCount": 1 
);

bulk.execute(function(err,response) 
    // maybe do something here
);

当然,“downvote”与此过程相反。

这里的要点是,在处理每个投票时,“计数”或“分数”会同时保持更新。这允许进行正常的查询和排序,而无需在每次访问数据时都进行计算,因为它已经完成了。

后一种情况是处理此问题的最有效方式。

【讨论】:

我实际上并没有将两者联系起来。他们分别显示赞成票和反对票的数量。你能为此简化代码吗? @AyushGupta 在聚合情况下,只需删除 $subtract 并返回要排序的数组的 $size。当然,真正的重点是存储计数,因此最好在更新每个包含计数的数组时存储字段,具体取决于是否删除或添加了项目。这个原则应该很容易遵循。

以上是关于按数组大小对结果进行排序的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB,按两个数组之间匹配元素的数量对结果进行排序

python中进行字符串排序

如何按位置对矩形数组进行排序?

如何使用 SQL Order By 语句对结果进行排序,不区分大小写?

需要帮助在 perl 中对数组进行排序

如何按值对数组进行排序(排序)? *有一个转折*