You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 03:10:58