繁体   English   中英

在 Mongodb 中使用集合作为循环的最佳方法是什么

[英]What is the best way to use collection as round robin in Mongodb

我有一个名为 items 的集合,其中包含三个文档。

{
  _id: 1,
  item: "Pencil"
}
{
  _id: 1,
  item: "Pen"
}
{
  _id: 1,
  item: "Sharpner"
}

我如何查询以循环方式获取文档? 考虑一下我同时收到多个用户请求。

所以一个人应该得到Pencil其他人会得到Pen然后其他人会得到Sharpner

然后从第一个重新开始。

如果更改模式是一个选择,我也准备好了。

我想我找到了一种无需更改架构即可执行此操作的方法。 它基于skip()limit() 此外,您可以指定保留返回文档的内部排序顺序,但是如指南所述,您不应依赖此顺序,尤其是因为索引被覆盖会导致性能下降:

$natural参数根据项目在数据库中的自然顺序返回项目。 此排序是内部实现功能,您不应依赖其中的任何特定结构。

无论如何,这是查询:

db.getCollection('YourCollection').find().skip(counter).limit(1)

counter存储文档当前索引的位置。

几件事开始..

  1. _id 在整个集合中必须是唯一的,尤其是当集合只是一个复制集时。
  2. 这是一个非常有状态的要求,并且不能很好地与分布式服务集一起工作。

话虽如此,假设您真的只想从数据库中迭代,我会使用游标来完成此操作。 这将进行收集扫描,并且对于记录来说效率非常低。

var myCursor = db.items.find().sort({_id:1});

while (myCursor.hasNext()) {
   printjson(myCursor.next());
}

我的建议是您应该一次从数据库中提取所有结果并在应用程序层中进行迭代。

var myCursor = db.inventory.find().sort({_id:1});
var documentArray = myCursor.toArray();
documentArray.foreach(doSomething)

如果这是关于分发的,您可以考虑通过aggregation/$sample获取随机文档而不是循环法:

db.collection.aggregate([
 {
  "$sample": {
    "size": 1
 }
}
])

操场

或者有通过$rand随机化的选项...

重组数据对象后使用文本 findOneAndUpdate

db.counter.findOneAndUpdate( {}, pipeline)
    {
        "_id" : ObjectId("624317a681e72a1cfd7f2b7e"),
        "values" : [
            "Pencil",
            "Pen",
            "Sharpener"
        ],
        "selected" : "Pencil",
        "counter" : 1
    }

 db.counter.findOneAndUpdate( {}, pipeline)
    {
        "_id" : ObjectId("624317a681e72a1cfd7f2b7e"),
        "values" : [
            "Pencil",
            "Pen",
            "Sharpener"
        ],
        "selected" : "Pen",
        "counter" : 2
    }

数据 object 现在在哪里:

    {
        "_id" : ObjectId("6242fe3bc1551d0f3562bcb2"),
        "values" : [
            "Pencil",
            "Pen",
            "Sharpener"
        ],
        "selected" : "Pencil",
        "counter" : 1
    }

管道是:

[{$project: {
     values: 1,
     selected: {
      $arrayElemAt: [
       '$values',
       '$counter'
      ]
     },
     counter: {
      $mod: [
       {
        $add: [
         '$counter',
         1
        ]
       },
       {
        $size: '$values'
       }
      ]
     }
}}]

这有一些优点:

  • 首先,使用 findOneAndUpdate 意味着将指针移动到列表中的下一项并立即读取 object。
  • 其次,通过使用 {$size: "$values"} 将值添加到列表中不会改变逻辑。
  • 而且,可以使用 object 代替字符串。

问题:对于超过 10 个条目,此方法会很笨拙

很难证明此方法是否像宣传的那样有效,因此有一个附带的 Kotlin 项目。 该项目使用协程,因此它异步调用查找/更新。

短信 GitHub

备选方案(假设 50K 个项目而不是 3 个):设置一个简单的计数器 {counter: 0} 并更新如下:

db.counter.findOneAndUpdate({},
[{$project: {
 counter: {
  $mod: [
   {
    $add: [
     '$counter',
     1
    ]
   },
   50000
  ]
 }
}}])

然后使用简单的 select 查询来查找正确的文档。

我已经更新了 github 以包含此示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM