MongoDB.live, free & fully virtual. June 9th - 10th. Register Now MongoDB.live, free & fully virtual. June 9th - 10th. Register Now

How to $match documents with an array with a subset of given values

Hi, I’ve done some research on the problem I’m working on, specifically reading over the docs for $all, $in, $elemMatch etc. but I still don’t understand how to write a query to do the following:

Data

User 1 has groups: [A, B, C]
User 2 has groups: [C, D, E]

Ticket 1 groups required: [A, B] <— matches User 1. Does not match User 2.
Ticket 2 groups required: [B, C] <— matches User 1. Does not match User 2.
Ticket 3 groups required: [C, D] <— does not match User 1, because it contains one required group (D) not in the user groups. Matches User 2.

Explanation

The “groups” on the ticket are required for a user to take a ticket. If there are any groups in the group array on the ticket not in the user’s group list, the user can’t take the ticket. In other words, the required groups on a ticket have to be equal to a user’s group list, or a subset of a user’s group list for the ticket to be matched/returned.

If it matters/makes a difference, this is being added as a $match stage of an existing aggregation query.

Is this possible? Can you please offer some pointers for how to go about this?

Thanks for any help you can offer :slight_smile:

I have a potential solution, which feels like a hack but I’m pretty sure will get me the results that I want:

  • get an array of all of the groups a user is NOT a member of
  • use $nin to match only required group arrays that don’t contain any of the groups the user is not a member of

Thoughts?

I tried this from Mongo Shell, and it looks like this is what you are looking for:

user1 = [ "A", "B", "C" ]
user2 = [ "C", "D", "E" ]

I created a collection with 3 documents:
{ _id: 1, groups: [ “A”, “B” ] }
{ _id: 2, groups: [ “B”, “C” ] }
{ _id: 3, groups: [ “C”, “D” ] }

The query:

db.collection.find( { $expr: { $setIsSubset: [ "$groups", user1 ] } } )

returns:
{ _id: 1, groups: [ “A”, “B” ] }
{ _id: 2, groups: [ “B”, “C” ] }

and,
db.collection.find( { $expr: { $setIsSubset: [ "$groups", user2 ] } } )
[ edit: Corrected the user1 to user2 ]

returns:
{ _id: 3, groups: [ "C", "D" ] }

2 Likes

Thanks, this in combination with $setEquals looks like just what I need!

$setIsSubset includes equality condition.

1 Like

Ah, the nested example there in the docs confused me.