如何使用MongoDB(ASP.NET Core 2.2)访问深度嵌套的数组 [英] How to access deeply nested array with MongoDB (ASP.NET Core 2.2)

查看:110
本文介绍了如何使用MongoDB(ASP.NET Core 2.2)访问深度嵌套的数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用MongoDB设计库存管理系统.我具有以下数据库结构:

I'm designing an inventory management system with MongoDB. I have the following database structure:

inventory
└─storage_slots
└─storage_locations
...etc...

每次添加新的Slot时,会将表示插槽在层次结构中的位置的树添加到storage_locations集合中,以表示其位置(根据位置,房间,区域,架子).到目前为止,我已经成功地成功添加了一个新项目,其中没有使用任何位置字段:(该插槽也已添加到storage_slots集合中)

Every time a new Slot is added, a tree representing the slot's location in the hierarchy is added to the storage_locations collection to represent its location (according to location, room, section, shelf). So far I have managed to successfully add a new item where none of the location fields are already used: (The slot is also added to the storage_slots collection)

{
"_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只是插槽的集合.

To be clear, storage_locations is the above hierarchy while storage_slots is just a collection of slots.

但是,如果字段已经存在于层次结构中,则运行以下代码:(我从发布)

However, if the fields are already present in the hierarchy, the following code is run: (I took insipration from this post)

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

此外,如果仅定义了其中一些,那么我将两者混合使用,由于它们基于相同的原理并且不会对问题有所帮助,因此我决定在此处不显示它们.

Also, if only some of them are defined then I do a mix of both that I decided not to show here because they are built on the same principles and would not contribute to the question.

只要运行上面直接的代码.我收到以下错误:

Whenever the code directly above is run. I get the following error:

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

其他东西

以下是与此问题相关的一些类:

Extra stuff

Here are some more relevant classes for the question:

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...
}

推荐答案

由于

You're getting this error because $ positional operator can be used only once while in your case there are multiple levels of nested arrays. From the docs:

位置$运算符不能用于遍历一个以上数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换是单个值

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

要解决此问题,您可以使用已过滤位置 MongoDB 3.6中引入的运算符.它允许您在更新路径中指定多个占位符,然后可以使用arrayFilters定义这些占位符的条件.

To fix that you can use filtered positional operator which was introduced in MongoDB 3.6. It allows you to specify multiple placeholders in your update path and then you can use arrayFilters to define conditions for those placeholders.

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

这篇关于如何使用MongoDB(ASP.NET Core 2.2)访问深度嵌套的数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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