尝试更新嵌套数组时出现 Mongo 错误:未找到标识符的数组过滤器 [英] Mongo error when trying to update nested arrays: No array filter found for identifier

查看:29
本文介绍了尝试更新嵌套数组时出现 Mongo 错误:未找到标识符的数组过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  • 一个社区有一组块
  • 一个街区有一系列楼层
  • 一个楼层有一组门
  • 一扇门有一组标签名称

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

我创建了一个

如果我无法更改存储库的方法签名,我非常感谢您对这项工作的帮助以及关于这是否是正确方法的建议.


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

解决方案

$[{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.Update.Set(Blocks.$[block].Floors.$[floor].Doors.$[door].LabelNames", names);var arrayFilters = new List();ArrayFilterDefinitionblockFilter = new BsonDocument("block.Name", new BsonDocument("$eq", houseBlockName));ArrayFilterDefinitionfloorFilter = new BsonDocument("floor.Name", new BsonDocument("$eq", houseFloorName));ArrayFilterDefinitiondoorFilter = new BsonDocument("door.Name", new BsonDocument("$eq", houseDoorName));arrayFilters.Add(blockFilter);arrayFilters.Add(floorFilter);arrayFilters.Add(doorFilter);var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };var 结果 = _communities.UpdateOne(filter, update, updateOptions);

I am trying to update a document in Mongo that represents a community with the following scenario.

  • A community has a collection of blocks
  • A block has a collection of floors
  • A floor has a collection of doors
  • A door has a collection of label names

Given a document Id and information about the labels that must be placed into each door, I want to use the MongoDb C# driver v2.10.4 and mongo:latest to update nested lists (several levels). I've reading the documentation, about array filters, but I can't have it working.

I've created a repository from scratch to reproduce the problem, with instructions on the Readme on how to run the integration test and a local MongoDB with docker.

But as a summary, my method groupds the labels so that I can bulk place names on the desired door and then it iterates over these groups and updates on Mongo the specific document setting the desired value inside some levels deep nested object. I couldn't think of a more efficient way.

All the code in the above repo.

The DB document:

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>();
}

The problematic method with array filters:

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);
    }
}

The exception is

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'"

Here's a more visual sample on how the nested structure looks like in the database. Notice the value I want to update is the LabelNames, which is an array of string.

I appreciate any help to have this working and suggestions on whether it's the right approach assuming that I cannot change the repository's method signature.


SOLUTION RESULT: Thanks for the quick answer @mickl, it works perfectly. Result at this repo's specific point of history exactly as suggested.

解决方案

The $[{houseBlockName}] expects an identifier which acts as a placeholder and has a corresponding filter defined within arrayfilters (positional filtered). It seems like you're trying to pass the filter value directly which is incorrect.

Your C# code can look like this:

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);

这篇关于尝试更新嵌套数组时出现 Mongo 错误:未找到标识符的数组过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆