MongoDB: атомно-поп случайный элемент - PullRequest
1 голос
/ 02 сентября 2011

Есть ли способ атомарного извлечения (удаления и извлечения) случайного элемента с помощью MongoDB - как у Redis SPOP ?

Я прочитал учебник RandomAttribute , но теперь мне нужно убедиться, что элемент также удаляется при получении, и это должно быть сделано атомарно.

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

Глядя на документацию $pop, кажется, что она не может принимать аргументы, поэтому она удаляет либо первый, либо последний элемент массива.

Ответы [ 2 ]

3 голосов
/ 21 сентября 2011

Обновления в коллекциях в Mongo являются атомарными, поэтому объединение findAndModify с подходом RandomAttribute поможет:

var rand = Math.rand();
db.collection.findAndModify({query: {random: {$gte: rand}}, remove: true});

Просто убедитесь, что вы также запросили random: {$lt: rand}, когда вышеприведенное возвращает null.

Подсказка: некоторые драйверы имеют findAndRemove, что совпадает с findAndModify с remove: true.

0 голосов
/ 02 сентября 2011

В принципе, в настоящее время нет хорошего способа сделать то, что вы хотите.Большая проблема в том, что не существует атомарного способа удалить элемент массива по его позиции.

См. этот вопрос для получения дополнительной информации по этому вопросу.

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

Шаг первый

Генерироватьслучайное число на стороне клиента.Используйте это, чтобы указать ключ элемента массива, который вы хотите удалить в точечной нотации (т. Е. «My_array.4»), поскольку AFAIK это не может быть сделано динамически в запросе.

Шаг второй

Использование случайного числа для атомарного сброса выбранного элемента массива при извлечении его, как это было до обновления с использованием findAndModify с new установленным в false.Предполагая, что сгенерированное вами случайное число было 4, вы должны сделать что-то вроде следующего:

db.mycollection.findAndModify({
  query: {_id: 1},
  update: {$unset: {"my_array.4" : true }},
  fields: {my_array : {$slice : [4, 1]}, _id : false},
  new: false
});

(Примечание: findAndModify в настоящее время возвращает документ, предварительно обновленный по умолчанию, но я где-то читал, чтоэто может измениться, поэтому рекомендуется всегда указывать, что вы хотите, используя опцию new.)

Это оставит элемент массива, который был выбран как значение null в массиве.

Шаг третий

Удалите нулевой элемент из массива с помощью другого обновления, используя $ pull:

db.mycollection.update({_id: 1}, {$pull: {my_array: null}})

Фу!Похоже, много работы для того, что вы хотите сделать.Я не знаю Redis, но SPOP звучит намного проще.Я надеюсь, что это все равно немного помогает.Это не единственная атомарная операция, но (пока вы можете обрабатывать эти нули), я думаю, что наиболее важными ее частями являются.Вы никогда не попадете в ситуацию, когда два разных потока выталкивают один и тот же элемент практически в одно и то же время, чего, я полагаю, вы пытаетесь избежать.

...