我们正在使用带有子文档“设置”的MongoDB文档。该子文档的结构未知。不同的设备会将不同的设置推送到此子文档中。 MongoDB文档可以包含多层嵌套的子文档。
我们想使用包含已更新值的JSON / BSON更新MongoDB中的文档。我们希望执行递归合并,但是如果我更新子文档的一个元素,则所有其他元素都将被删除。
我们无法使用MongoDB API 3.4解决问题。我不确定在3.6版中引入的mergeObject是否会有所帮助。不幸的是,Microsoft Azure的CosmosDB中尚不提供3.6版。
MongoDB中的文档:
{
"_id" : "1234",
"settings" : {
"settingA" : {
"SettingA1" : {
"SettingA11" : 11,
"SettingA12" : 22
},
"SettingA2" : 33
},
"settingB" : {
"SettingB1" : {
"SettingB11" : 44,
"SettingB12" : 55
}
}
}
}
除了顶层以外,结构和所有名称都是未知的,并且因文档而异。由于必须保存结构,因此平面阵列是不够的。
文档应使用设备发送的JSON字符串进行更新。 JSON字符串应与数据库中的设置合并。重复的条目应被覆盖。
要合并到MongoDB文档中的BSONDocument(或JSON)示例:
{
"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});
}
}
我宁愿使用MongoDB API来实现它,而不是通过软件来实现它。有没有办法使用MongoDB API合并递归文档(聚合,查找,更新)?