【问题标题】:mongo sort after limit after sort - Not working排序后限制后的mongo排序 - 不工作
【发布时间】:2016-09-02 20:15:56
【问题描述】:

我有一个集合,我使用 $query 从中获取特定类型的用户
然后我需要根据 user_id 升序对它们进行排序并将它们限制为 2000

从这些我需要最大的 user_id,所以我按降序对它们进行排序并限制为 1。

但是第二次排序忘记了 2000 的限制,并从 find() 对整个光标进行排序。

有什么解决方法吗?

$cursor = $collection   ->find($query)                // too many entries
            ->sort(array('user_id'=>1))   // go ascending
            ->limit(2000)                // got our limited qouta
            ->sort(array('user_id'=>-1)) // go descending to get max
            ->limit(1);          // the one with max(user_id)

【问题讨论】:

  • 第三行有一个分号。这是复制/粘贴错误还是您的原始代码中也存在?
  • 哦对,复制粘贴错误,感谢指出,已更正

标签: php mongodb


【解决方案1】:

您不能先进行排序,然后再进行限制,然后再进行排序。 Cursor 对象就是这样,它不会运行查询,直到您通过getNext() 迭代到第一个结果,这可以手动运行或在foreach 循环中运行,不仅如此,sort 只是对象的一个​​属性因此,进行两种排序只会覆盖该属性。

实现您的目标的最佳方式是:

$doc = $collection->find($query)->sort(array('user_id' => 1))
       ->skip(1999)->limit(1)->getNext();

这将始终选择组中最高的user_id(出现在此排序的末尾),这将给出与进​​行两次排序相同的结果。

【讨论】:

  • 这个看起来是正确的。我尝试了 collection->find->sort->skip(1999)->limit->getNext 以及 collection->find->sort->skip(1999)->getNext。两者都给出相同(但不正确)的答案。当我弄清楚时会回复。
  • 我们可以做 $collection->find()->sort()->skip(1999)->getNext();之间的限制(1)绝对可以跳过
  • @davneet 是的,我没有想到,getNext() 当然只是获取下一个文档,所以限制在那里是惰性的。
【解决方案2】:

使用skip()怎么样:

$cursor = $collection   ->find($query)  
        ->sort(array('user_id'=>1))   
        ->limit(2000)                
        ->skip(1999);

【讨论】:

  • +1 limit(1) 在这里是否也能正常工作,因为只需要 1999 年跳过的文档之后的第一个文档吗?
  • 上面的代码仍然在光标中给了我 2000 条记录...这意味着我必须遍历所有这些记录才能到达最后一条记录
  • UPS,对不起。 find(...)->sort(...)->skip(1999)->limit(1) 可能是你需要的。
【解决方案3】:

sort-limit-sort-limit 方法背后的原因是什么?

你就不能这样做

$cursor = $collection ->find($query)
            ->sort(array('user_id'=>-1))
            ->limit(1);

编辑:此外,只有最后一个应用于光标的sort 有效。 (这一行出现在 pymongo docs here 中,这是有道理的。)

【讨论】:

  • 第一个排序限制的要点是从所有记录中取出前 2000 条记录。第二个排序限制只是从这 2000 个中获取最大值。Ur 代码将从 find() 的所有记录中给我最大值
  • 好的,现在我明白了。您正在尝试以 ASC 顺序获取与查询匹配的第 2000 个文档。您应该知道,当您在 cursor 上应用 sort 时。在您实际迭代该光标之前,不会检索(因此排序)任何文档。您可以使用skip,但这会变得很慢。最好的方法是在这种情况下使用基于范围的查询。
【解决方案4】:

我在 最新版本中使用以下代码:

$doc = $collection->find($query)->sort(array('user_id' => 1))
    ->skip(1999)->limit(1)->getNext();

当我在sort() 之后使用->skip(1999)->limit(1) 时它停止工作

光标$doc 确实给了我值。我试着把它分解成这样:

$doc = $collection->find($query)->sort(array('user_id' => 1))
$doc = $doc->skip(1999)->limit(1);

这行得通。也许你应该在新版本中尝试一下。

【讨论】:

    【解决方案5】:

    Answer by Sammaye 是正确的。

    您仍然可以实现您想要的方式。您可以使用聚合框架,因为它在一个阶段之后执行另一个阶段,依此类推。

    $doc = $collection->aggregate(array(
        array(
          '$match' => $query,
        ),
        array(
          '$sort' => array(
              'user_id'=> 1
          ),
        ),
        array(
          '$limit' => 2000
        ),
        array(
          '$sort' => array(
              'user_id'=> -1
          ),
        ),
        array(
          '$limit' => 1
        ),
    ));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-25
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      • 2015-08-20
      • 1970-01-01
      • 2017-02-19
      • 1970-01-01
      相关资源
      最近更新 更多