Я бы хотел присвоить значение полю "_id" вновь вставленного документа. Поле ID должно начинаться с 0 и не может пропускать число. Вставка может произойти только в том случае, если нет документа, содержащего дублированный контент.
В настоящее время это то, что у меня есть.
db.system.js.save({
_id: "nextId",
value: function (x) {
return db.counters.findAndModify({
query:{_id:x},
update:{$inc:{value:1}},
new:true
}).value;
}
})
db.loadServerScripts() // make the nextId available directly or via $where clause
db.counters.insert({_id: "_id", value: 0})
db.test.update(
{"value": "abc"},
{$setOnInsert: {"_id": nextId("_id"), "value": "abc"}},
{upsert: true}
)
Все работает хорошо, если нет документа, содержащего дублированное поле «значение». При наличии дублированного поля «значение» функция JavaScript nextId («_ id») по-прежнему вызывается, даже если она находится внутри тега $ setOnInsert. Следовательно, наблюдаемое неправильное поведение здесь будет означать, что некоторые номера _id будут использованы / потрачены впустую, но не могут быть назначены действительным новым документам. Правильное / предполагаемое поведение было бы в том случае, когда nextId () вызывается только тогда, когда фактически вставлен новый документ.
Вопрос: Есть ли обходной путь для достижения правильного / отступающего поведения в MongoDB?
Версия MongoDB: v3.4.10 (локальная установка) и v3.4.15 (хостинг mlab MongoDB)
Примечание: я отказался от этой проблемы и решил ослабить мое требование "не могу пропустить число". Измененное требование состоит в том, чтобы использовать как можно меньше идентификаторов в глобальном масштабе, при этом гарантируя, что используемые идентификаторы имеют как можно более низкие значения. После этого изменения требований к расслаблению я разработал лучшее и масштабируемое решение.
1) для каждого клиента зарезервируйте скромное количество идентификаторов. Результат будет:
db.counters.findAndModify({
query: {_id:"_id"},
update: {$inc: {"_id_beg": 10, "_id_end": 10}},
new: true
})
2) для каждой вставки клиента используйте следующее
db.test.findAndModify({
query: {"value": <input value>},
update: {$setOnInsert: {"_id": <use lowest reserved id>, "value": <input value>"}
upsert: true,
new: true
})
3) для каждого ответа на вставку клиента возвращаемое значение из findAndModify содержит nextId. Если nextId совпадает с самым низким зарезервированным идентификатором, это означает, что он используется. Если он отличается, это означает, что другой клиент вставил то же значение, мы можем использовать зарезервированный идентификатор для последующей вставки
Существуют некоторые оптимизации для каждого конкретного случая, такие как уникальный индекс _id, неиспользуемые значения от сбойных клиентов и т. Д., Чтобы обеспечить восстановление после сбоя