【问题标题】:mongodb sort/query on calculated value aggregation-framework?mongodb排序/查询计算值聚合框架?
【发布时间】:2015-11-19 12:50:33
【问题描述】:

我有一个与其他集合有关系的集合,我想为用户选择一个最相关的记录选择如下:

具有 this.some_collection_id == 'someid' && this.another_collection_id == 'another_id' 的第一条记录 然后记录有 this.some_collection_id == 'someid' 然后记录有 this.another_collection_id == 'another_id' 然后是其他记录

示例:假设主要集合是故事,每个故事都有一个“兴趣组”和一个“位置”。所有用户都有一个“主要兴趣组”和一个“位置”。我想查询特定用户的前 100 个最有趣的故事,具有相同兴趣组和位置的故事应权重 5,相同兴趣组仅 3,相同位置仅 2,其余 1。

我想在查询期间奖励这些积分,以便我可以根据这些积分对故事集进行排序并返回前 100 名。

所以我可以执行 4 个单独的查询并在客户端合并结果(不是那么漂亮),或者我可以尝试以正确的顺序获取集合。

到目前为止,我想出了以下几点:

map = %Q{
  function() {
    var score = 1;
    if (this.some_id == "#{some_id}") {
      score = score + 3
    }
    if (this.another_id == "#{another_id}") {
      score = score + 2
    }

    emit(this._id, { _id: this._id, score: score });
  }
}

reduce = %Q{
  function(key, values) {
    return values;
  }
}

MyCollection.map_reduce(map, reduce).out(inline: true)

这将正确返回带有分数的整个记录​​集合,但是我无法对分数进行排序,所以我必须在客户端进行(也很难看,因为我只需要前 100 条左右的记录)

有没有办法以不同的方式做到这一点?我还检查了聚合框架,但我不知道这将如何解决这个特定的查询。

【问题讨论】:

  • 这个问题有点笼统,这是没有人接触它的最大原因。您至少可以编辑以显示一些示例文档(仅几个)以及应该与所需结果匹配的内容。这将极大地帮助理解。
  • 谢谢,让我尝试添加一些相关数据
  • 请做。我想我明白你的意思,但我想确定一下。
  • 实际上是希望获得一些具有值的真实数据,而不是您刚刚添加的数据。但以下至少与您提供的代码类似。

标签: ruby mongodb mapreduce mongodb-query aggregation-framework


【解决方案1】:

这里提到的问题并没有真正解释这里的逻辑,但如果我确实理解了你的一般意思,那么这应该是上面的类似物,还有你想要的额外增强:

MyCollection.collection.aggregate([
    { "$project" => {
        "score" => {
            "$add" => [
                { "$cond" => [
                    { "$or" => [
                        { "$eq" => [ "$some_id" => some_id_var1 ] },
                        { "$eq" => [ "$some_id" => some_id_var2 ] },
                        { "$eq" => [ "$some_id" => some_id_var3 ] },
                        { "$eq" => [ "$some_id" => some_id_var4 ] }
                    ]},
                    3,
                    0
                ]},
                { "$cond" => [
                    { "$or" => [
                        { "$eq" => [ "$another_id" => another_id_var1 ] },
                        { "$eq" => [ "$another_id" => another_id_var2 ] },
                        { "$eq" => [ "$another_id" => another_id_var3 ] },
                        { "$eq" => [ "$another_id" => another_id_var4 ] }
                    ]},
                    2,
                    0
                ]},
                1
            ]
        }
    }},
    { "$sort" => { "score" => -1 } },
    { "$limit" => 100 }
])

本质上这是完全相同的,因为“some_id”和“another_id”字段都与一些变量输入进行比较,以查看它们是否匹配并返回分数。我在这里所做的补充是您提到“4 个查询”,所以这听起来像是变量更改。在这两种情况下,这都应该与 $or 条件相适应。如果您真的只在每种情况下比较一个值,那么只需删除包装 $or

$cond 运算符本身就是“if/then/else”三元运算。所以第一个参数是“if”,下一个参数分别是 true/false 返回的值。在这种情况下,您的归属分数或0

然后用add 将所有内容包装起来,为每个文档生成“总分”,使用与您使用的逻辑相同的逻辑。

还要注意$project 要求您“显式”地在结果中输出您想要的所有字段。你的 mapReduce 除了_idscore 什么都不做,所以我在这里做同样的事情。但是您可以根据需要添加其他字段。 _id 当然总是隐含的,除非另有说明,例如"_id" => 0

剩下的就是“分数”值上的$sort,以及排序后总结果的$limit。这两件事都是 mapReduce 做不到的。

这基本上是根据匹配的属性添加权重,然后将结果“排序”和“限制”到最高分。另请注意,作为“数据结构”,您要使用的聚合管道中的变量只是本机代码,不需要您使用 mapReduce 进行的“字符串化”处理。

【讨论】:

  • 重读这个问题我必须说我过去表达得更好:) 感谢您阅读它并解释如何使用这样的聚合框架。我已经能够得到一个概念验证查询工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
  • 2023-01-31
  • 1970-01-01
  • 2021-03-09
  • 2013-12-25
  • 2013-01-12
相关资源
最近更新 更多