如何使用Dictionary<string, object>批量修补RavenDB文档?
问题描述
我需要用接收的Dictionary<string, object>批量修补通过ID标识的文档,字典的键是文档属性路径(支持子路径)。
示例文档结构:
public class MyDoc { public string StringProp {get; set;} public bool? BoolProp {get; set;} public int? IntProp {get; set;} public SubDocument SubProp {get; set;} } public class SubDocument { public string StringProp {get; set;} }
示例更新字典:
new Dictionary<string, object> { {"StringProp", "hello" }, {"BoolProp", true }, {"SubProp.StringProp", "subprop value" }, }
我知道可以先拉取文档修改后再保存,但想实现无需提取文档的直接修改方式,目前想到用PatchByQueryOperation的方式(示例代码如下),另外想解决两个场景:
- 文档中不存在
SubProp时,如何修改其子属性 - 如何直接赋值整个
SubProp而非仅修改子属性
var operation = store .Operations .Send(new PatchByQueryOperation(new IndexQuery { QueryParameters = new Parameters { {"ids", new[] {"orders/1-A", "companies/1-A"}} }, Query = @"from @all_docs as d where id() in ($ids) update { d.StringProp = 'hello'; d.BoolProp = true; d.SubProp.StringProp = 'subprop value'; }" }));
解决方案
一、动态生成Patch查询语句
硬编码更新语句不够灵活,可通过遍历字典动态构建update块,适配任意属性路径:
var targetIds = new[] {"orders/1-A", "companies/1-A"}; var updateDict = new Dictionary<string, object> { {"StringProp", "hello" }, {"BoolProp", true }, {"SubProp.StringProp", "subprop value" }, }; var assignmentLines = new List<string>(); foreach (var (path, value) in updateDict) { // 处理不同类型的值转义 var formattedValue = value switch { string str => $"'{str.Replace("'", "''")}'", // 转义单引号避免语法错误 bool boolVal => boolVal.ToString().ToLower(), int intVal => intVal.ToString(), _ => System.Text.Json.JsonSerializer.Serialize(value) // 复杂类型序列化为JSON }; assignmentLines.Add($"d.{path} = {formattedValue};"); } var updateBlock = string.Join("\n ", assignmentLines); var patchOperation = store.Operations.Send(new PatchByQueryOperation(new IndexQuery { QueryParameters = new Parameters { {"ids", targetIds} }, Query = $@"from @all_docs as d where id() in ($ids) update {{ {updateBlock} }}" }));
二、处理子对象不存在的场景
如果文档中SubProp为null,直接修改SubProp.StringProp会报错,有两种处理方式:
方式1:自动初始化嵌套对象
遍历属性路径,为每层不存在的对象创建空实例:
foreach (var (path, value) in updateDict) { var pathSegments = path.Split('.'); var currentPath = "d"; // 初始化路径中除最后一段外的所有父对象 for (int i = 0; i < pathSegments.Length - 1; i++) { var segment = pathSegments[i]; currentPath += $".{segment}"; assignmentLines.Add($"{currentPath} = {currentPath} ?? new {{}};"); } // 添加最终的赋值语句 var formattedValue = /* 同之前的类型处理逻辑 */; assignmentLines.Add($"{currentPath}.{pathSegments.Last()} = {formattedValue};"); }
方式2:使用RavenDB内置的patch函数
RavenDB的Patch语法支持直接设置嵌套属性,自动创建不存在的父对象,写法更简洁:
var patchOperation = store.Operations.Send(new PatchByQueryOperation(new IndexQuery { QueryParameters = new Parameters { {"ids", targetIds} }, Query = @"from @all_docs as d where id() in ($ids) update { patch(d, function(p) { p.set(""SubProp.StringProp"", ""subprop value""); }) }" }));
三、直接赋值整个子对象
如果要替换整个SubProp,只需将字典的键设为SubProp,值传入完整的子对象即可:
示例字典
var updateDict = new Dictionary<string, object> { {"SubProp", new SubDocument { StringProp = "new sub value" } }, };
动态生成后对应的更新语句会自动处理为:
d.SubProp = {"StringProp": "new sub value"};
或者用patch函数写法:
Query = @"from @all_docs as d where id() in ($ids) update { patch(d, function(p) { p.set(""SubProp"", { StringProp: ""new sub value"" }); }) }"
内容的提问来源于stack exchange,提问作者Stephan Steiner
相关产品推荐
相关产品推荐

