swiftparse-platform

Parse: How to query for a relation that contains exactly two objects?


Given two tables:

Users
Groups

One or more users can be part of a group. Each group contains a relations column with all it's users.

How can I query for a group that has an exact list of users as members in it's relation field?

I tried

query.whereKey(users, containsAllObjectsInArray: [userA, userB])

but that gives me an error

Unsupported query operator on relation field: users (Code: 102, Version: 1.8.5)

On the other hand if I use

query.whereKey(users, containedIn: [userA, userB])

I get also groups where more then the required users are members. I would then have loop through all the groups found in the first query and place another query for each group to check if the group's only members are userA + userB. That sounds too complicated.

What's the most efficient query to do this?


Solution

  • Obviously what you want to achieve does not have a straightforward solution because Parse is designed for scalability and speed, therefore complicated pattern matching is not directly supported. The error you are getting when using containsAllObjectsInArray constraint happens because that constraint works on fields with Array type but cannot be used on Relation fields. To get the results you want, you need to get a bit creative. So I thought of a solution which might work using inner queries.

    First, add an extra column of type Number to your Group class (lets call it 'counter') This field needs to keep the number of users who are part of a group. So every time you add a user to a group, you also increase its value by one and when you remove a user from that group, you decrease it. In your case, you can use this counter value to quickly find out which one of the groups has exactly two members.

    Now you can write a query which returns the groups for which this counter is exactly two and the user Relation contains at least 'user1'. This query is going to be your inner query. Your main query now simply needs to go through what this inner query returns and selects only the records where the user relation also contains 'user2'. Given that 'having exactly two users' condition is satisfied in your inner query, you simply end up with all the group records which have exactly two users ('user1' and 'user2') in their user Relation.

    let innerQuery = PFQuery(className: "Groups")
    innerQuery.whereKey("counter", equalTo: 2)  // exactly two users
    innerQuery.whereKey("users", equalTo: user1) // at least user1 is there
    let query = PFQuery(className: "Groups")
    query.whereKey("objectId", matchesQuery: innerQuery) // exactly two users including user1
    query.whereKey("users", equalTo: user2) // select groups which also have user2
    query.findObjectsInBackgroundWithBlock {
        (comments: [PFObject]?, error: NSError?) -> Void in
        // Groups with exactly two users, user1 and user2
    }