Mongo 尝试更新嵌套数组时出错:找不到标识符的数组过滤器
Mongo error when trying to update nested arrays: No array filter found for identifier
我正在尝试更新 Mongo 中代表具有以下场景的社区的文档。
- 社区有积木集合
- 一个街区有一组楼层
- 一个楼层有一组门
- 一扇门有一组标签名称
给定文档 ID 和有关必须放入每扇门的标签的信息,我想使用 MongoDb C# 驱动程序 v2.10.4 和 mongo:latest
更新嵌套列表(多个级别)。
我已经阅读了有关数组过滤器的文档,但我无法使用它。
我创建了一个 repository from scratch to reproduce the problem,在自述文件中有关于如何 运行 集成测试的说明和一个本地 MongoDB docker.
但总而言之,我的方法对标签进行了分组,以便我可以将名称批量放置在所需的门上,然后遍历这些组并更新 Mongo 特定文档,在某些文档中设置所需的值层次深层嵌套对象。我想不出更有效的方法。
以上代码库中的所有代码。
数据库文件:
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's specific point of history 的结果与建议完全一致。
$[{houseBlockName}]
需要一个用作占位符的标识符,并在 arrayfilters
(positional filtered) 中定义了相应的过滤器。看起来你试图直接传递过滤器值,这是不正确的。
您的 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);
{
var filterCompany = Builders<CompanyInfo>.Filter.Eq(x => x.Id, Timekeepping.CompanyID);
var update = Builders<CompanyInfo>.Update.Set("LstPersonnel.$[i].Timekeeping.$[j].CheckOutDate", DateTime.UtcNow);
var arrayFilters = new List<ArrayFilterDefinition>
{
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("i.MacAddress",new BsonDocument("$eq", Timekeepping.MacAddress) )),
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("j.Id", new BsonDocument("$eq", timeKeeping.Id)))
};
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters};
var updateResult = await _companys.UpdateOneAsync(filterCompany, update, updateOptions);
return updateResult.ModifiedCount != 0;
}
我正在尝试更新 Mongo 中代表具有以下场景的社区的文档。
- 社区有积木集合
- 一个街区有一组楼层
- 一个楼层有一组门
- 一扇门有一组标签名称
给定文档 ID 和有关必须放入每扇门的标签的信息,我想使用 MongoDb C# 驱动程序 v2.10.4 和 mongo:latest
更新嵌套列表(多个级别)。
我已经阅读了有关数组过滤器的文档,但我无法使用它。
我创建了一个 repository from scratch to reproduce the problem,在自述文件中有关于如何 运行 集成测试的说明和一个本地 MongoDB docker.
但总而言之,我的方法对标签进行了分组,以便我可以将名称批量放置在所需的门上,然后遍历这些组并更新 Mongo 特定文档,在某些文档中设置所需的值层次深层嵌套对象。我想不出更有效的方法。
以上代码库中的所有代码。
数据库文件:
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's specific point of history 的结果与建议完全一致。
$[{houseBlockName}]
需要一个用作占位符的标识符,并在 arrayfilters
(positional filtered) 中定义了相应的过滤器。看起来你试图直接传递过滤器值,这是不正确的。
您的 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);
{
var filterCompany = Builders<CompanyInfo>.Filter.Eq(x => x.Id, Timekeepping.CompanyID);
var update = Builders<CompanyInfo>.Update.Set("LstPersonnel.$[i].Timekeeping.$[j].CheckOutDate", DateTime.UtcNow);
var arrayFilters = new List<ArrayFilterDefinition>
{
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("i.MacAddress",new BsonDocument("$eq", Timekeepping.MacAddress) )),
new BsonDocumentArrayFilterDefinition<BsonDocument>(new BsonDocument("j.Id", new BsonDocument("$eq", timeKeeping.Id)))
};
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters};
var updateResult = await _companys.UpdateOneAsync(filterCompany, update, updateOptions);
return updateResult.ModifiedCount != 0;
}