将未知结构的嵌套BSON合并到mongodb文档中

时间:2019-04-12 15:25:43

标签: c# mongodb bson azure-cosmosdb-mongoapi

我们正在使用带有子文档“设置”的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合并递归文档(聚合,查找,更新)?

0 个答案:

没有答案