#mongodb
#mongodb
Вопрос:
Я пытаюсь подсчитать использование слов с помощью MongoDB. Моя коллекция в настоящее время выглядит так:
{'_id':###, 'username':'Foo', words:[{'word':'foo', 'count':1}, {'word':'bar', 'count':1}]}
Когда создается новое сообщение, я извлекаю все новые слова в массив, но я пытаюсь выяснить, как выполнить upsert в массив words и увеличить количество, если слово уже существует.
Например, в приведенном выше примере, если пользователь «Foo» опубликовал «lorem ipsum foo», я бы добавил «lorem» и «ipsum» в массив слов пользователей, но увеличил количество для «foo».
Возможно ли это в одном запросе? В настоящее время я использую addToSet:
'$addToSet':{'words':{'$each':word_array}}
Но, похоже, это не дает никакого способа увеличить количество слов.
Был бы очень признателен за помощь 🙂
Ответ №1:
Если вы хотите переключиться со списка на хэш (объект), вы можете сделать это атомарно.
Из документов: « $inc
… увеличивает поле на числовое значение, если поле присутствует в объекте, в противном случае присваивает полю числовое значение. »
{ $inc : { field : value } }
Итак, если бы вы могли реорганизовать свой контейнер и объект:
words: [
{
'word': 'foo',
'count': 1
},
...
]
Для:
words: {
'foo': 1,
'other_word: 2,
...
}
вы могли бы использовать операцию update
с:
{ $inc: { 'words.foo': 1 } }
который будет создан { 'foo': 1 }
, если ‘foo’ не существует, иначе увеличит foo .
Например.:
$ db.bar.insert({ id: 1, words: {} });
$ db.bar.find({ id: 1 })
[
{ ..., "words" : { }, "id" : 1 }
]
$ db.bar.update({ id: 1 }, { $inc: { 'words.foo': 1 } });
$ db.bar.find({ id: 1 })
[
{ ..., "id" : 1, "words" : { "foo" : 1 } }
]
$ db.bar.update({ id: 1 }, { $inc: { 'words.foo': 1 } });
$ db.bar.find({ id: 1 })
[
{ ..., "id" : 1, "words" : { "foo" : 2 } }
]
Комментарии:
1. Это работает только для каждого слова и делает невозможным выполнение каких-либо запросов к самим словам. Я немного смущен тем, насколько это лучше, чем выделенные кортежи счетчиков для каждого слова. Это также связано с проблемами, о которых я упоминал в своем ответе.
2. Пользователь может ввести весь массив слов (измененный на
word: 1
форму dict), а ключи word либо добавляются как «1», либо увеличиваются. Итак, это именно то, что было запрошено в вопросе: «Я извлекаю все новые слова в массив, но я пытаюсь выяснить, как выполнить upsert в массив words и увеличить количество, если слово уже существует». — и все это за одну атомарную операцию (update), и это несколько ближек исходной структуре вопроса (правильной или неправильной).3. Достаточно справедливо. Я все еще думаю, что это неоптимальное решение проблемы.
Ответ №2:
К сожалению, это невозможно сделать за одно обновление вашей схемы. Ваша схема немного сомнительна и, вероятно, должна быть преобразована в выделенную коллекцию со счетчиками слов, например :
db.users {_id:###, username:'Foo'}
db.words.counters {_id:###, word:'Word', userId: ###, count: 1}
Это позволит избежать многих проблем, таких как :
- Ограничение максимального размера документа
- Принудительное перемещение mongo по вашим документам по мере увеличения их размера
Оба сценария требуют двух обновлений, чтобы делать то, что вы хотите, что приводит к проблемам атомарности. Обновление для каждого слова путем перебора word_array лучше и безопаснее (и возможно с обоими решениями).