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

限于喜欢 提交于 2021-02-10 06:42:06

问题


I have a document like this:

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

And I have an user that has a list of authorized groups, like for example the following:

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

What I'm trying to achieve is to retrieve all documents in the database that the user is authorized to retrieve, essentially I want to be able to check in the list "groupsAuthorized" if one of the group contains a subset of an element of the other list "groups" contained in my user authorizations

using the following values:

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

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

the user should be able to retrieve this document as the string "France/IDF" is correctly contained in the string "France/IDF/Paris", however, if the values would've been like this:

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

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

it should not work, because my user is only authorized to view documents from France/IDF/Paris and USA/NY/NYC and none of the string inside of the authorizedGroups of my document contains those sequences

I've tried to use a standard LINQ query to achieve this which is fairly simple:

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

But i'm getting the famous unsupported filter exception that it seems lot of people is having, i've tried different options found on the internet like the following:

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;

But the problem is this will only check if one of the element of the array is contained inside the other array, It will not correctly work for case like "France/IDF" that should correctly match "France/IDF/Paris" because "France/IDF" string is contained inside the "France/IDF/Paris" string inside of my document

I'm getting a bit clueless on how to achieve this using the mongodb C# driver, 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

Has anyone an Idea on this subject ?


回答1:


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

don't do it :)

One place you can start with is here. It describes all the LINQ operators that are supported by the MongoDB .NET driver. As you can see .Contains() isn't mentioned there which means you can't use it and you'll get an arror in the runtime but it does not mean that there's no way to do what you're trying to achieve.

The operator closest to contains you can use is $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 to do exactly what .NET's .Any does.

Your query (MongoDB client) can look like this:

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,

You can run the same query from .NET using BsonDocument class which takes a string (JSON) and converts into a query:

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


来源:https://stackoverflow.com/questions/61921425/c-sharp-mongodb-driver-cant-find-the-way-to-run-complex-query-for-anyin-filter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!