我正在使用 MongoDB 设计一个库存管理系统。我有以下数据库结构:
inventory
└─storage_slots
└─storage_locations
...etc...
每次添加新的Slot
时,都会在storage_locations
集合中添加一棵表示slot在层次结构中位置的树来表示它的位置(根据location,room ,部分,货架)。到目前为止,我已经成功地添加了一个新项目,其中没有任何位置字段已被使用:(该插槽也被添加到 storage_slots
集合中)
{
"_id" : ObjectId("5c57169f0863d665c7f13d27"),
"CreatedUtc" : {
"$date" : 1549211298017
},
"UpdatedUtc" : {
"$date" : 1549211298017
},
"Description" : null,
"Address" : null,
"StorageRooms" : [
{
"_id" : ObjectId("5c57169f0863d665c7f13d28"),
"CreatedUtc" : {
"$date" : 1549211297719
},
"UpdatedUtc" : {
"$date" : 1549211297719
},
"Description" : null,
"StorageSections" : [
{
"_id" : ObjectId("5c57169f0863d665c7f13d29"),
"CreatedUtc" : {
"$date" : 1549211297719
},
"UpdatedUtc" : {
"$date" : 1549211297719
},
"Description" : null,
"StorageShelves" : [
{
"_id" : ObjectId("5c57169f0863d665c7f13d2a"),
"CreatedUtc" : {
"$date" : 1549211297719
},
"UpdatedUtc" : {
"$date" : 1549211297719
},
"Description" : null,
"StorageSlotIds" : [
ObjectId("5c57169f0863d665c7f13d26")
]
}
]
}
]
}
]
}
要清楚,storage_locations
是上面的层次结构,而 storage_slots
只是插槽的集合。
但是,如果字段已经存在于层次结构中,则会运行以下代码:(我从 this 帖子中得到启发)
var filter = Builders<StorageLocation>.Filter.And(
Builders<StorageLocation>.Filter.Where(location => location.Id == id),
Builders<StorageLocation>.Filter.Eq("StorageRooms.Id", roomId),
Builders<StorageLocation>.Filter.Eq("StorageRooms.$.StorageSections.Id", sectionId),
Builders<StorageLocation>.Filter.Eq("StorageRooms.$.StorageSections.$.StorageShelves.Id", shelfId));
var update =
Builders<StorageLocation>.Update.Push("StorageRooms.$.StorageSections.$.StorageShelves.$.StorageSlotIds",
storageSlotIds);
return await UpdateAsync(filter, update, cancellationToken);
此外,如果只定义了其中一些,那么我会混合使用这两种方法,但我决定不在此处显示,因为它们基于相同的原则构建,不会对问题做出贡献。
问题
每当运行上面的代码时。我收到以下错误:
InvalidCastException: Unable to cast object of type 'MongoDB.Bson.ObjectId[]' to type 'MongoDB.Bson.ObjectId'.
MongoDB.Bson.Serialization.Serializers.SerializerBase<TValue>.MongoDB.Bson.Serialization.IBsonSerializer.Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value)
//annoying scrollbar
错误发生在这一行:
return await UpdateAsync(filter, update, cancellationToken);
方法是:
public Task<UpdateResult> UpdateAsync(FilterDefinition<T> filter, UpdateDefinition<T> updateDefinition,
string database, string collection, CancellationToken cancellationToken)
{
return _mongoContext.MongoClient.GetDatabase(database).GetCollection<T>(collection)
.UpdateOneAsync(filter, updateDefinition.Set(o => o.UpdatedUtc, DateTime.UtcNow),
cancellationToken: cancellationToken);
}
额外内容
这里有一些与问题相关的类:
public class StorageLocation : Dbo
{
public string Description { get; set; }
public Address Address { get; set; }
public IEnumerable<StorageRoom> StorageRooms { get; set; }
}
public class StorageRoom : Dbo
{
public string Description { get; set; }
public IEnumerable<StorageSection> StorageSections { get; set; }
}
public class StorageSection : Dbo
{
public string Description { get; set; }
public IEnumerable<StorageShelf> StorageShelves { get; set; }
}
public class StorageShelf : Dbo
{
public string Description { get; set; }
public IEnumerable<ObjectId> StorageSlotIds { get; set; }
}
public class StorageSlot : Dbo
{
public string Description { get; set; }
public ObjectId LocationId { get; set; }
public ObjectId RoomId { get; set; }
public ObjectId SectionId { get; set; }
public ObjectId ShelfId { get; set; }
...etc...
}
最佳答案
您收到此错误是因为 $ positional operator只能使用一次,而在您的情况下有多层嵌套数组。来自文档:
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
要解决这个问题,您可以使用 filtered positional MongoDB 3.6 中引入的运算符。它允许您在更新路径中指定多个占位符,然后您可以使用 arrayFilters
为这些占位符定义条件。
var filter = Builders<StorageLocation>.Filter.And(
Builders<StorageLocation>.Filter.Where(location => location.Id == id),
Builders<StorageLocation>.Filter.Eq("StorageRooms._id", roomId));
var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> sectionFilter = new BsonDocument("section._id", new BsonDocument("$eq", sectionId));
ArrayFilterDefinition<BsonDocument> shelfFilter = new BsonDocument("shelf._id", new BsonDocument("$eq", shelfId));
arrayFilters.Add(sectionFilter);
arrayFilters.Add(shelfFilter);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var update =
Builders<StorageLocation>.Update.Push("StorageRooms.$.StorageSections.$[section].StorageShelves.$[shelf].StorageSlotIds",
storageSlotIds);
await Col.UpdateOneAsync(filter, update, updateOptions);
关于c# - 如何使用 MongoDB 访问深层嵌套数组(ASP.NET Core 2.2),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54505373/