簡體   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