【问题标题】:C# MongoDB Driver: Can't find the way to run complex query for AnyIn filter in MongoDBC# MongoDB 驱动程序:找不到在 MongoDB 中为 AnyIn 过滤器运行复杂查询的方法
【发布时间】:2020-09-07 07:27:55
【问题描述】:

我有这样的文件:

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

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

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

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

使用以下值:

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

它应该不起作用,因为我的用户仅被授权查看来自法国/IDF/Paris 和 USA/NY/NYC 的文档,并且我的文档的 authorizedGroups 中的字符串都不包含这些序列

我尝试使用标准 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/Paris”的“France/IDF”这样的情况,它将无法正常工作因为“France/IDF”字符串包含在我文档中的“France/IDF/Paris”字符串中

我对如何使用 mongodb C# 驱动程序实现这一点有点一头雾水,我开始认为我应该将所有文档拉到客户端并手动进行过滤,但这会很混乱

有人对这个主题有什么想法吗?

【问题讨论】:

  • 检查 this 以获取使用 mongo 运行复杂查询的替代方法

标签: c# .net mongodb mongodb-query mongodb-.net-driver


【解决方案1】:

我开始认为我应该将所有文档拉到客户端并手动进行过滤,但这会很混乱

不要这样做:)

您可以从here 开始。它描述了 MongoDB .NET 驱动程序支持的所有 LINQ 运算符。如您所见,.Contains() 没有在此处提及,这意味着您无法使用它,并且您会在运行时遇到错误,但这并不意味着您无法实现您想要实现的目标。

您可以使用的最接近包含的运算符是$indexOfBytes,如果没有匹配则返回-1,否则返回子字符串的位置。此外,由于您需要将一个数组与另一个数组进行匹配,因此您需要两对 $map$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 类从 .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();

【讨论】:

  • 您的解决方案非常好,我能够对其进行调整以完全满足我的需求,您知道如何将它与以前的 IQueryable 结合起来吗?我正在使用一个框架,该框架使用 IQueryable 接口公开一个 API 以过滤/排序/分页,但我不知道如何结合您向我提出的解决方案(使用 collection.Find 和我的请求的 bson 文档)和我以前的使用 IQueryable 的行为
  • 抱歉,我没有按回车键,评论消失了,你真的在​​ 50 秒内回复了,哈哈,我已经编辑了我的评论
  • @Rakiah 不幸的是,您不能这样做。当您尝试使用 LINQ 和 IQueryable 时,您必须在 MongoDB 的聚合框架运算符和 LINQ 运算符之间具有 1:1 的覆盖率。在这种情况下,.Contains() 是缺失的一块拼图。所以你必须使用.Find()。如果您担心任何重构都会破坏查询,则一个提示是在 C# 中使用 nameof 运算符
  • 啊,可悲的是,我不认为我会知道如何转换为 LINQ 方式,因为我正在使用一个框架并且无法控制它我不能同时删除 IQueryable 系统.如果将这个:mongoplayground.net/p/ihi19w_uU2l 翻译成 LINQ 方式甚至可行,您会有任何提示吗? OR 部分会很简单,但如果不使用 contains/startsWith/Any 等,我似乎无法翻译表达式本身
  • @Rakiah 不,目前这是不可能的,因为$indexOfBytes 在 LINQ 中没有对应项
猜你喜欢
  • 1970-01-01
  • 2018-02-04
  • 2012-03-04
  • 2019-03-01
  • 2017-02-26
  • 2015-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多