首页 > 解决方案 > 尝试更新嵌套数组时出现 Mongo 错误:未找到标识符的数组过滤器

问题描述

我正在尝试在 Mongo 中更新代表具有以下场景的社区的文档。

给定一个文档 ID 和有关必须放置在每扇门中的标签的信息,我想使用MongoDb C# 驱动程序 v2.10.4mongo:latest更新嵌套列表(几个级别)。我已经阅读了有关数组过滤器的文档,但我无法让它工作。

从头开始创建了一个存储库来重现该问题,并在自述文件中提供了有关如何运行集成测试和使用 docker 运行本地 MongoDB 的说明。

但总而言之,我的方法对标签进行分组,以便我可以在所需的门上批量放置名称,然后迭代这些组并更新 Mongo 上的特定文档,在某些级别的深层嵌套对象中设置所需的值。我想不出更有效的方法。

上述 repo 中的所有代码。

数据库文件:

public class Community
{
    public Guid Id { get; set; }
    public IEnumerable<Block> Blocks { get; set; } = Enumerable.Empty<Block>();
}

public class Block
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<Floor> Floors { get; set; } = Enumerable.Empty<Floor>();
}

public class Floor
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<Door> Doors { get; set; } = Enumerable.Empty<Door>();
}

public class Door
{
    public string Name { get; set; } = string.Empty;
    public IEnumerable<string> LabelNames = Enumerable.Empty<string>();
}

数组过滤器有问题的方法:

public async Task UpdateDoorNames(Guid id, IEnumerable<Label> labels)
{
    var labelsGroupedByHouse =
        labels
            .ToList()
            .GroupBy(x => new { x.BlockId, x.FloorId, x.DoorId })
            .ToList();

    var filter =
        Builders<Community>
            .Filter
            .Where(x => x.Id == id);

    foreach (var house in labelsGroupedByHouse)
    {
        var houseBlockName = house.Key.BlockId;
        var houseFloorName = house.Key.FloorId;
        var houseDoorName = house.Key.DoorId;
        var names = house.Select(x => x.Name).ToList();

        var update =
            Builders<Community>
                .Update
                .Set($"Blocks.$[{houseBlockName}].Floors.$[{houseFloorName}].Doors.$[{houseDoorName}].LabelNames", names);
        await _communities.UpdateOneAsync(filter, update);
    }
}

例外是_

MongoDB.Driver.MongoWriteException with the message "A write operation resulted in an error.
  No array filter found for identifier 'Block 1' in path 'Blocks.$[Block 1].Floors.$[Ground Floor].Doors.$[A].LabelNames'"

这是一个关于嵌套结构在数据库中的外观的更直观的示例。请注意,我要更新的值是LabelNames,它是一个字符串数组。 在此处输入图像描述

假设我无法更改存储库的方法签名,我很感激任何帮助以及关于它是否是正确方法的建议。


解决方案结果:感谢@mickl 的快速回答,它运行良好。在这个repo 的特定历史点上的结果完全符合建议。

标签: c#mongodbmongodb-.net-driver

解决方案


$[{houseBlockName}]需要一个充当占位符的标识符,并在(位置过滤)中定义了相应的arrayfilters过滤。似乎您正在尝试直接传递不正确的过滤器值。

您的 C# 代码可能如下所示:

var houseBlockName = house.Key.BlockId;
var houseFloorName = house.Key.FloorId;
var houseDoorName = house.Key.DoorId;
var names = house.Select(x => x.Name).ToList();

var update = Builders<Community>.Update.Set("Blocks.$[block].Floors.$[floor].Doors.$[door].LabelNames", names);

var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> blockFilter = new BsonDocument("block.Name", new BsonDocument("$eq", houseBlockName));
ArrayFilterDefinition<BsonDocument> floorFilter = new BsonDocument("floor.Name", new BsonDocument("$eq", houseFloorName));
ArrayFilterDefinition<BsonDocument> doorFilter = new BsonDocument("door.Name", new BsonDocument("$eq", houseDoorName));
arrayFilters.Add(blockFilter);
arrayFilters.Add(floorFilter);
arrayFilters.Add(doorFilter);

var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };

var result = _communities.UpdateOne(filter, update, updateOptions);

推荐阅读