如何在c#中重命名mongodb中嵌套数组的文档字段

ippsafx7  于 2023-03-07  发布在  Go
关注(0)|答案(2)|浏览(148)

给定MongoDB中包含以下文档的集合:

{
  id: guid,
  items: [
    {
      fieldA : "value"
      ...
    },
    {
      fieldA : "value"
      ...
    },
  ]
},
{  ...
}

我必须找到一种有效的方法来使用C#将 * fieldA * 重命名为 * fieldB *。在我看来,MongoDB没有提供可用于数组的重命名操作符。
我尝试使用该路径向数组中添加新字段,但不知道如何将该字段设置为旧字段的值。
任何帮助都是感激的。谢谢

rqenqsqc

rqenqsqc1#

目前MongoDB驱动程序2.10.4中还没有类型安全的方法来实现这一点,但是,您可以使用$map构建一个聚合管道,并将其作为更新管道执行。

var db = client.GetDatabase("test");
var collection = db.GetCollection<MyClass>("data");

var filter = Builders<MyClass>.Filter
    .Empty;

var update = Builders<MyClass>.Update.Pipeline(
    new PipelineStagePipelineDefinition<MyClass, MyClass>(
        new PipelineStageDefinition<MyClass, MyClass>[]
        {
            @"{ ""$addFields"": {
                  items: {
                    $map: {
                      input: ""$items"",
                      as: ""item"",
                      in: {
                        fieldB: ""$$item.fieldA""
                      }
                    }
                  }
                }
              }"}));

await collection.UpdateManyAsync(filter, update)
    .ConfigureAwait(false);

这是通过运行管道并用管道的输出更新每个文档来实现的,上面是一个简单的管道,它用新的items字段替换items字段,新的items字段具有从旧数组Map的新数组。

f4t66c6m

f4t66c6m2#

除了Kevin Smith的答案之外,这里还有一个helper函数,它也Map所有现有字段,并且只对数组中存在旧字段名的记录执行更改。
使用这个函数时要记住的几件事,这只是单层深度,它不能处理某些字段被部分转换的情况,尽管后者一开始就不应该是一个东西。
这使用了C#11中新的原始字符串特性,使工作变得更容易。

/// <summary>
/// NOTE: YOU CAN LOSE DATA IF TArray PUBLIC PROPERTIES DO NOT INCLUDE ALL FIELDS IN THE MongoDB ARRAY <para />
/// Helper to rename a field for all values in an array such as <br />
/// { arrayItems: [ { "fieldNameA": "fieldValue" }, .... ] } <br />
/// TO <br />
/// { arrayItems: [ { "fieldNameB": "fieldValue" }, ... ] }
/// <remarks>
/// <typeparamref name="TArray"/> type is required to allow mapping all other fields.
/// </remarks>
/// </summary>
public async Task RenameFieldInArray<TCollection, TArray>(IMongoCollection<TCollection> collection,
    string arrayFieldName, 
    string oldFieldName, 
    string newFieldName)
{
    var filter = Builders<TCollection>.Filter.Exists($"{arrayFieldName}.{oldFieldName}");

    // map all unspecified fields in the existing array
    var arrayPropertyNames = typeof(TArray).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(x => !string.Equals(x.Name, oldFieldName, StringComparison.OrdinalIgnoreCase) && 
                        !string.Equals(x.Name, newFieldName, StringComparison.OrdinalIgnoreCase))
            .Select(x => $"{x.Name}: \"$$arrayItem.{x.Name}\"");
    var otherFieldMapping = string.Join("," + Environment.NewLine, arrayPropertyNames);

    var update = Builders<TCollection>.Update.Pipeline(
        new PipelineStagePipelineDefinition<TCollection, TCollection>(
            new PipelineStageDefinition<TCollection, TCollection>[]
            {
                $$"""
                 { "$addFields": {
                     {{arrayFieldName}}: {
                       $map: {
                         input: "${{arrayFieldName}}",
                         as: "arrayItem",
                         in: {
                           {{newFieldName}}: "$$arrayItem.{{oldFieldName}}",
                           {{otherFieldMapping}}
                         }
                       }
                     }
                   }
                 }
                 """,
            })
    );
    
    await collection.UpdateManyAsync(filter, update).ConfigureAwait(false);
}

// Assuming 'Items' is the type mapping for the array of items in 'MyClass'
await RenameFieldInArray<MyClass, Items>(MyClassCollection, arrayFieldName: "items", oldFieldName: "fieldA", newFieldName: "fieldB");

相关问题