C# MongoDB 驱动程序:无法在 MongoDB 中找到 运行 AnyIn 过滤器的复杂查询方式

C# MongoDB Driver: Can't find the way to run complex query for AnyIn filter in MongoDB

我有这样一个文档:

{
  "id": "xxxxxxxxxxxx",
  "groupsAuthorized": [
    "USA/California/SF",
    "France/IDF/Paris"
  ]
}

我有一个用户有一个授权组列表,例如:

"groups": [
  "France/IDF",
  "USA/NY/NYC"
]

我想要实现的是检索数据库中用户有权检索的所有文档,基本上我希望能够检查列表 "groupsAuthorized" 如果其中一个组包含我的用户授权

中包含的另一个列表 "groups" 的元素子集

使用以下值:

my document:
{
  "id": "xxxxxxxxxxxx",
  "groupsAuthorized": [
    "USA/California/SF",
    "France/IDF/Paris"
  ]
}

my user permissions:
"groups": [
  "France/IDF",
  "USA/NY/NYC"
]

用户应该能够检索此文档,因为字符串 "France/IDF" 正确包含在字符串 "France/IDF/Paris" 中,但是,如果值是这样的:

my document:
{
  "id": "xxxxxxxxxxxx",
  "groupsAuthorized": [
    "USA/California/SF",
    "France/IDF"
  ]
}

my user permissions:
"groups": [
  "France/IDF/Paris",
  "USA/NY/NYC"
]

它不应该工作,因为我的用户只被授权查看来自 France/IDF/Paris 和 USA/NY/NYC 的文档,我文档的 authorizedGroups 中的字符串 none 包含这些序列

我尝试使用标准的 LINQ 查询来实现这一点,这非常简单:

var userAuthorizedGroups = new List<string> { "France/IDF/Paris", "USA/NY/NYC" };
var results = collection.AsQueryable()
.Where(entity => userAuthorizedGroups
                .Any(userGroup => entity.authorizedGroups
                                  .Any(entityAuthorizedGroup => entityAuthorizedGroup.Contains(userGroup))));

但是我遇到了似乎很多人都遇到的著名的不受支持的过滤器异常,我尝试了在互联网上找到的不同选项,如下所示:

var userAuthorizedGroups = new List<string> { "France/IDF/Paris", "USA/NY/NYC" };
var filter = Builders<PartitionedEntity<Passport>>.Filter.AnyIn(i => i.authorizedGroups, userAuthorizedGroups);
var results = (await collection.FindAsync(filter)).ToList();

return results;

但问题是这只会检查数组的一个元素是否包含在另一个数组中,对于像 "France/IDF" 这样应该正确匹配 "France/IDF/Paris" 的情况,它不会正确工作因为 "France/IDF" 字符串包含在我文档中的 "France/IDF/Paris" 字符串中

我对如何使用 mongodb C# 驱动程序实现这一点有点无能为力,我开始认为我应该将所有文档拉到客户端并手动进行过滤,但那样会相当凌乱

有人对这个问题有想法吗?

i'm starting to think that I should just pull all documents to client and do the filtering manually but that would be quite messy

不要这样做 :)

您可以从 here 开始。它描述了 MongoDB .NET 驱动程序支持的所有 LINQ 运算符。如您所见,那里没有提到 .Contains(),这意味着您不能使用它,并且您会在 运行 时间得到一个错误,但这并不意味着没有办法执行您的操作正在努力实现。

您可以使用的最接近 contains 的运算符是 $indexOfBytes which returns -1 if there's no match and the position of a substring otherwise. Also since you need to match an array against another array you need two pairs of $map and $anyElementTrue 来完成 .NET 的 .Any 所做的事情。

您的查询(MongoDB 客户端)可能如下所示:

db.collection.find({
    $expr: {
        $anyElementTrue: {
            $map: {
                input: "$groupsAuthorized",
                as: "group",
                in: {
                    $anyElementTrue: {
                        $map: { 
                            input: ["France/IDF/Paris", "USA/NY/NYC"],
                            as: "userGroup",
                            in: { $ne: [ -1, { $indexOfBytes: [ "$$userGroup", "$$group" ] } ] }
                        }
                    }
                }
            }
        }
    }
})

Mongo Playground,

您可以 运行 使用 BsonDocument class 来自 .NET 的相同查询,它采用字符串 (JSON) 并转换为查询:

var query = BsonDocument.Parse(@"{
    $expr: {
        $anyElementTrue:
        {
            $map:
            {
                input: '$groupsAuthorized',
                    as: 'group',
                    in: {
                    $anyElementTrue:
                    {
                        $map:
                        {
                            input: ['France/IDF/Paris', 'USA/NY/NYC'],
                                as: 'userGroup',
                                in: { $ne: [-1, { $indexOfBytes: ['$$userGroup', '$$group'] } ] }
                        }
                    }
                }
            }
        }
    }
}");

var result = col.Find(query).ToList();