简体   繁体   中英

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

I have a collection named items with three documents.

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

How could I query to get the document as round-robin? Consider I got multiple user requests at the same time.

so one should get Pencil other will get Pen and then other will get Sharpner .

then start again from the first one.

If changing schema is a choice I am also ready for that.

I think I found a way to do this without changing the schema. It is based on skip() and limit() . Moreover you can specify to keep the internal sorting order for returned documents but as the guide says you should not rely on this, especially because you are losing performance since the indexing is overridden:

The $natural parameter returns items according to their natural order within the database. This ordering is an internal implementation feature, and you should not rely on any particular structure within it.

Anyway, this is the query:

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

Where counter stores the current index for your documents.

Few things to start..

  1. _id has to be unique across a collection especially when the collection is only a replication set.
  2. This is a very stateful requirement and would not work well with a distributed set of services for example.

With that said, assuming you really just want to iterate from the database i would use cursors to accomplish this. This will do a collection scan and is very inefficient for the record.

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

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

My suggestion is that you should pull all results from the database at once and do your iteration in the application tier.

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

If this is about distribution you may consider fetching random documents instead of round-robin via aggregation/$sample :

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

playground

Or there is options to randomize via $rand ...

Use text findOneAndUpdate after restructuring the data objects

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
    }

where the data object is now:

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

and the pipeline is:

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

This has some merits:

  • Firstly, using findOneAndUpdate means that moving the pointer to the next item in the list and reading the object happen at once.
  • Secondly,by using the {$size: "$values"} adding a value into the list doesn't change the logic.
  • And, instead of a string an object could be used instead.

Problems: This method would be unwieldy with more than 10's of entries

It is hard to prove that this method works as advertised so there is an accompanying Kotlin project. The project uses coroutines so it is calling a find/update asynchronously.

text GitHub

The alternative (assuming 50K items and not 3): Set-up a simple counter {counter: 0} and update as follows:

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

Then use a simple select query to find the right document.

I've updated the github to include this example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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