Слияние вложенного BSON неизвестной структуры в документ mongodb - PullRequest
0 голосов
/ 12 апреля 2019

Мы используем документы 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 (агрегация, поиск, обновление)?

...