Мы используем документы MongoDB с вложенным документом «настройки».Структура этого поддокумента неизвестна.Разные устройства помещают в этот поддок разные настройки.Документы MongoDB могут содержать несколько уровней вложенных вложенных документов.
Мы хотели бы обновить документ в MongoDB с помощью JSON / BSON, содержащего обновленные значения.Мы хотели бы выполнить рекурсивное слияние, но если я обновлю один элемент вложенного документа, все остальные элементы будут удалены.
Мы не смогли решить проблему с помощью MongoDB API 3.4.Я не уверен, поможет ли mergeObject, представленный в версии 3.6.К сожалению, версия 3.6 еще не доступна в CosmosDB Microsoft Azure.
Документ в MongoDB:
{
"_id" : "1234",
"settings" : {
"settingA" : {
"SettingA1" : {
"SettingA11" : 11,
"SettingA12" : 22
},
"SettingA2" : 33
},
"settingB" : {
"SettingB1" : {
"SettingB11" : 44,
"SettingB12" : 55
}
}
}
}
Помимо верхнего уровня, структура и все имена не известны и отличаются отдокумент к документу.Поскольку структура должна быть сохранена, плоского массива будет недостаточно.
Документ должен обновляться с использованием JSON-строки, отправляемой устройствами.JSON-строка должна быть объединена с настройками в базе данных.Дубликаты записей должны быть перезаписаны.
Пример BSONDocument (или JSON) для объединения в MongoDB-документ:
{
"settings" : {
"settingA" : {
"SettingA1" : {
"SettingA11" : 66,
"SettingA13" : 77
}
}
}
}
Мне не удалось интегрировать Update-JSON в Mongo-DB.Используя оператор $ set, весь поддокумент заменяется.
Нежелательный результат:
{
"_id" : "1234",
"settings" : {
"settingA" : {
"SettingA1" : {
"SettingA11" : 66,
"SettingA13" : 77
}
},
"settingB" : {
"SettingB1" : {
"SettingB11" : 44,
"SettingB12" : 55
}
}
}
}
Желаемый результат:
{
"_id" : "1234",
"settings" : {
"settingA" : {
"SettingA1" : {
"SettingA11" : 66,
"SettingA12" : 22,
"SettingA13" : 77
},
"SettingA2" : 33
},
"settingB" : {
"SettingB1" : {
"SettingB11" : 44,
"SettingB12" : 55
}
}
}
}
Обходной путь - прочитать документ и выполнить рекурсивное объединение в программном обеспечении.Я смог сделать это на Python и в C #:
private static void BsonDocumentMerge(ref BsonDocument doc, BsonDocument merge_doc)
{
foreach (BsonElement mergeElement in mergeDoc)
{
if (doc.Contains(mergeElement.Name) && mergeElement.Value.IsBsonDocument)
{
BsonDocument subdocument = doc.GetElement(mergeElement.Name).Value.ToBsonDocument();
BsonDocument mergeSubdocument = mergeElement.Value.ToBsonDocument();
DictMerge(ref subdocument, mergeSubdocument);
}
else
{
doc.Set(mergeElement.Name, mergeElement.Value);
}
}
}
// JSON to be merged with DB document
// BsonDocument newSettings = {…} // Document to be merged into MongoDB document
//
// Read current settings from DB
var pipeline = new BsonDocument[] {
new BsonDocument{ { "$match", new BsonDocument("_id", myID) }},
new BsonDocument{ { "$project", new BsonDocument("settings", 1)} }
};
var asyncCursor = collection.Aggregate<BsonDocument>(pipeline);
var results = await asyncCursor.ToListAsync();
if (results.Count > 0)
{
BsonElement settingsInDbAsElement;
if (results[0].TryGetElement("settings", out settingsInDbAsElement))
{
// Prepare settings from Database
BsonDocument settingsInDB = settingsInDbAsElement.Value.ToBsonDocument();
// Merge recursivelly
BsonDocumentMerge(ref settingsInDB, newSettings);
updatedSettings = settingsInDB;
// Set new settings subdocument to MongoDB-document
var filter = Builders<BsonDocument>.Filter.Eq("_id", myID);
var updateDefinition = Builders<BsonDocument>.Update
.Set("settings", updatedSettings)
.Set("last_update", now)
.Min("first", now);
var result = collection.UpdateOne(filter, updateDefinition, new UpdateOptions { IsUpsert = true});
}
}
Я бы предпочел добиться этого с помощью API MongoDB вместо того, чтобы делать это в программном обеспечении.Можно ли объединить рекурсивные документы с помощью API MongoDB (агрегация, поиск, обновление)?