Mongodb, что является лучшим способом использовать коллекцию как круговой

У меня есть коллекция названных предметов с тремя документами.

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

Как я могу запросить, чтобы получить документ как круговой Учтите, что я получил несколько запросов пользователей одновременно.

поэтому один должен получить "Карандаш", другой - "Перо", а другой - "Шарпнер".

затем начните снова с первого.

Если изменение схемы - выбор, я также готов к этому.

4 ответа

Я думаю, что нашел способ сделать это без изменения схемы. Он основан на skip() а также limit(), Более того, вы можете указать, чтобы сохранить внутренний порядок сортировки для возвращенных документов, но, как говорится в руководстве, вам не следует полагаться на это, особенно потому, что вы теряете производительность, так как индексирование переопределено:

$natural Параметр возвращает элементы в соответствии с их естественным порядком в базе данных. Этот порядок является внутренней функцией реализации, и вы не должны полагаться на какую-либо конкретную структуру внутри него.

Во всяком случае, это запрос:

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

куда counter хранит текущий индекс для ваших документов.

Если речь идет о распространении, вы можете рассмотреть возможность получения случайных документов вместо циклического перебора через aggregation/$sample :

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

детская площадка

Или есть варианты рандомизации через $rand ...

Немного вещей, чтобы начать..

  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)

Используйте текст 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
    }

где сейчас находится объект данных:

          {
        "_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 означает, что перемещение указателя на следующий элемент в списке и чтение объекта происходит одновременно.
  • Во-вторых, с помощью {$size: "$values"} добавление значения в список не меняет логику.
  • И вместо строки можно было бы использовать объект.

Проблемы: этот метод будет громоздким, если количество записей превышает 10.

Трудно доказать, что этот метод работает так, как рекламируется, поэтому существует сопутствующий проект Kotlin. В проекте используются сопрограммы, поэтому он вызывает поиск/обновление асинхронно.

текст GitHub

Альтернативный вариант (при условии, что элементов 50 000, а не 3): настройте простой счетчик {counter: 0} и обновите его следующим образом:

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

Затем используйте простой запрос выбора, чтобы найти нужный документ.

Я обновил github, чтобы включить этот пример.

Другие вопросы по тегам