Я изо всех сил пытался найти решение следующей проблемы и, похоже, получал противоречивые советы от разных постов mongodb.Я пытаюсь выяснить, как правильно представить «массив» подобъектов таким образом, чтобы:
- они могли быть выгружены (т.е. обновлены или новый элемент создан, если необходимо, в одной операции)
- идентификаторы объектов доступны в виде значений, которые можно искать, а не только ключей (которые вы не можете реально найти в монго).
У меня есть структура, которую я могупредставить как массив (repr A):
{
_id: 1,
subdocs: [
{ sd_id: 1, title: t1 },
{ sd_id: 2, title: t2 },
...
]
}
или как вложенный документ (repr B)
{
_id: 1,
subdocs: {
1: { title: t1 },
2: { title: t2 },
...
}
}
Я хотел бы иметь возможность обновить ИЛИ вставить (т.е. upsert)новые поддокеты без необходимости использования дополнительной логики в приложении.
В отчете B это просто, так как я могу просто использовать set
$set: {subdocs.3.title: t3}
в обновлении с upsert: true
.
В отчете A возможно обновление существующей записи с использованием 'arrayFilter' с чем-то вроде:
update({_id: 1}, {$set: {subdocs.$[i].title: t3}}, {arrayFilter: [{i.sd_id: 3}], upsert: true})
Проблема заключается в том, что, хотя вышеприведенное обновит существующий подобъект, он не создастновый подобъект (то есть с _id: 3), если он не существует (это не upsert).Документы утверждают, что $ [] поддерживает upsert, но это не работает для меня.
Хотя repr B разрешает обновление / upserts, нет способа искать идентификаторы вложенных документов, потому что они теперь являются ключамиа не ценности.
Единственное решение вышеизложенного состоит в использовании денормализованного представления, например, с идентификатором, сохраняемым как ключ и значение:
subdocs: {
1: { sd_id: 1, title: t1 },
2: { sd_id: 2, title: t2 },
...
}
Но это кажется ненадежным (поскольку значения могутиз синхронизации).
Так что мой вопрос, есть ли способ обойти это?Возможно, мне не хватает способа сделать upsert в случае A?
ОБНОВЛЕНИЕ: я нашел обходной путь, который позволяет мне эффективно использовать repr A, хотя я не уверен, что он оптимален.Он включает в себя две записи, а не одну:
update({_id: 1, "subdocs.sd_id": {$ne: 3}}, {$push: {subdocs: {sd_id: 3}}})
update({_id: 1}, {$set: {subdocs.$[i].title: t3}}, {arrayFilter: [{i.sd_id: 3}]})
Первая строка в приведенном выше примере гарантирует, что мы когда-либо вставим только один поддок с sd_id 3 (и действует только в том случае, если идентификатор не существует), покавторая строка обновляет запись (которая теперь обязательно должна существовать).Я, вероятно, могу поместить их в упорядоченный пакет, чтобы все это заработало.