【问题标题】:Sort documents by a present field and a calculated value按当前字段和计算值对文档进行排序
【发布时间】:2017-06-04 11:20:18
【问题描述】:

如何在页面顶部显示最佳评论和最差评论。

我认为用户的“有用”和“无用”投票应该会对结果产生影响。

我有评论,如果人们点击有用和无用按钮,他们的 ID 会被添加到适当的数组中(有用或无用)。

您可以通过“总分”来判断正分或负分。即 1 到 5。所以 1 是最差的,5 是最好的。

我猜如果有人给出了总分 5 分但只有一个有用的评论,但有人给出了总分 4 分并且 100 人点击“有用”的评论应该显示为 100 人的最佳正面?

我只想在页面顶部显示 2 条评论最好和最差的评论,如果与总分有联系,决定因素应该是有用性。因此,如果有 2 条总分相同的评论,其中一条有 5 条有用和 10 条无用,那将是 -5 有用,而在另一条评论中,有人有 5 条有用和 4 条无用,那将是 1 条有用,这样就会显示打破平局。

我希望通过一个 mongoose 查询而不是聚合来做到这一点,但我认为答案将是聚合。

我想可能会有一个截断,比如分数大于 3 是正面评价,低于 3 是负面评价。

我用猫鼬。 提前感谢您的帮助。

一些样本数据。

{
    "_id" : ObjectId("5929f89a54aa92274c4e4677"),
    "compId" : ObjectId("58d94c441eb9e52454932db6"),
    "anonId" : ObjectId("5929f88154aa92274c4e4675"),
    "overall" : 3,
    "titleReview" : "53",
    "reviewText" : "53",
    "companyName" : "store1",
    "replies" : [],
    "version" : 2,
    "notUseful" : [ObjectId("58d94c441eb9e52454932db6")],
    "useful" : [],
    "dateCreated" : ISODate("2017-05-27T22:07:22.207Z"),
    "images" : [],
    "__v" : 0
}


{
    "_id" : ObjectId("5929f8dfa1435135fc5e904b"),
    "compId" : ObjectId("58d94c441eb9e52454932db6"),
    "anonId" : ObjectId("5929f8bab0bc8834f41e9cf8"),
    "overall" : 3,
    "titleReview" : "54",
    "reviewText" : "54",
    "companyName" : "store1",
    "replies" : [],
    "version" : 1,
    "notUseful" : [ObjectId("5929f83bf371672714bb8d44"), ObjectId("5929f853f371672714bb8d46")],
    "useful" : [],
    "dateCreated" : ISODate("2017-05-27T22:08:31.516Z"),
    "images" : [],

    "__v" : 0
}


{
    "_id" : ObjectId("5929f956a692e82398aaa2f2"),
    "compId" : ObjectId("58d94c441eb9e52454932db6"),
    "anonId" : ObjectId("5929f93da692e82398aaa2f0"),
    "overall" : 3,
    "titleReview" : "56",
    "reviewText" : "56",
    "companyName" : "store1",
    "replies" : [],
    "version" : 1,
    "notUseful" : [],
    "useful" : [],
    "dateCreated" : ISODate("2017-05-27T22:10:30.608Z"),
    "images" : [],
    "__v" : 0
}

【问题讨论】:

  • 真的不清楚你在这里问什么。您是否只是要求对文档进行排序,以便“整体”值相同,然后另一个计算(第二个排序参数)将确定哪个在顺序中排在第一位?那基本上是useful - nonUseful = secondSort吗?因此,在这两个文档中,第一个文档将是第一个文档,因为它的“无用”条目比另一个文档少。这是正确的解释吗?
  • 大部分情况下是的。所以我应该在同一个查询中按总分进行查找和排序。结果:如果我按升序排序,开头的 obj 将是最低的总分。然后我需要从.find 的返回中执行我的第二个算法。我想这听起来很容易。我只有一个对象,我可以得到最低的对象,然后通过 useful - nonUseful 得到最有用的对象,哪个更高的将是最差的评论,但最有用的。
  • 我将阅读您的答案,但我只想说我难以解释的部分。棘手的部分是我在问题中提到的问题,如果总分为 4 和 5,但总分为 4 的人有更多有用的选票,那么将显示 4 的评论而不是5.只是想说。谢谢
  • 如前所述,您的问题确实可能比现在更清楚。我想我的回答基本上涵盖了选项,本质上是您要么使用聚合来计算一个或多个值以对文档中不存在的值进行排序,要么将该逻辑放入“投票”的每次更新中以保持文档中存在的值。 “分数”的实际算法真的取决于你。我正在介绍你用来到达那里的方法。

标签: node.js mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

如果我正确阅读了您的问题,那么在对文档的 "overall" 分数进行排序时,您似乎也希望计算出 "useful""nonUseful" 投票的差异。

这里更好的选择是将计算包含在您存储的文档中,但总体而言,我们将涵盖这两个选项。

聚合

在不更改架构和其他逻辑的情况下,确实需要聚合来执行该计算。最好将其表示为:

Model.aggregate([
  { "$addFields": {
     "netUseful": {
       "$subtract": [
         { "$size": "$useful" },
         { "$size": "$notUseful" }
       ]
     }
  }},
  { "$sort": { "overall": 1, "netUseful": -1 } }
],function(err, result) {

})

所以你基本上得到了两个数组之间的差异,更多的"useful" 响应对提升排名有积极的影响,而更多的"notUseful" 将减少这种影响。根据您可用的 MongoDB 版本,您可以使用 $addFields 仅包含附加字段或 $project 与您需要返回的所有字段。

$sort 然后根据您的规则对"overall" 分数的组合执行升序,"netUseful" 的新字段按降序排列“正面”到“负面”。

重新建模

完全放弃聚合,您可以从普通查询中获得更快的结果。但这当然意味着在向数组添加成员时在文档中保留该“分数”。

在基本选项中,您使用 $inc 更新运算符和 $push 来更改分数。

因此,对于 "useful" 条目,您可以执行以下操作:

Model.update(
  { "_id": docId, "useful": { "$ne": userId } },
  { 
    "$push": { "useful": userId },
    "$inc": { "netUseful": 1 }
  },
  function(err, status) {

  }
)

对于"notUseful",您可以通过将负值“递减”到$inc 来做相反的事情:

Model.update(
  { "_id": docId, "nonUseful": { "$ne": userId } },
  { 
    "$push": { "nonUseful": userId },
    "$inc": { "netUseful": -1 }
  },
  function(err, status) {

  }
)

要涵盖所有情况,包括投票从"useFul"“更改”为"nonUseful",然后您将扩展逻辑并使用$pull 实施适当的反向操作。但这应该给出一般的想法。

注意我们不使用$addToSet 操作的原因是因为我们想确保在“递增”时用户 id 不存在于数组中或“递减”。因此,$ne 运算符用于测试值不存在。如果是这样,那么我们不会尝试修改数组影响"netUseful" 值。这同样适用于从这些投票中“删除”用户的相反情况。

由于每次更新都会维护计算,因此您只需使用标准 .sort() 执行查询

Model.find().sort({ "overall": 1, "netUseful": -1 }).exec(function(err,results) {

})

因此,通过将“成本”转移到“投票”的维护中,您可以消除稍后运行聚合的开销。对于我来说,这是一个常规操作,并且“排序”不依赖于其他强制计算为动态的运行时参数,那么您可以使用存储的结果。

【讨论】:

    猜你喜欢
    • 2017-05-10
    • 2016-01-11
    • 2017-12-16
    • 2018-09-22
    • 2020-09-08
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    • 2015-03-24
    相关资源
    最近更新 更多