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 ...
Немного вещей, чтобы начать..
- _id должен быть уникальным в коллекции, особенно если коллекция представляет собой только набор репликации.
- Это очень требовательное состояние, и оно не будет хорошо работать, например, с распределенным набором сервисов.
С учетом сказанного, если вы действительно просто хотите выполнить итерацию из базы данных, я бы использовал для этого курсоры. Это приведет к сканированию коллекции и очень неэффективно для записи.
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. В проекте используются сопрограммы, поэтому он вызывает поиск/обновление асинхронно.
Альтернативный вариант (при условии, что элементов 50 000, а не 3): настройте простой счетчик {counter: 0} и обновите его следующим образом:
db.counter.findOneAndUpdate({},
[{$project: {
counter: {
$mod: [
{
$add: [
'$counter',
1
]
},
50000
]
}
}}])
Затем используйте простой запрос выбора, чтобы найти нужный документ.
Я обновил github, чтобы включить этот пример.