I am using Firebase as a backend for an app, and I need to create a matchmaking system where users are matched to the current user using a number of criteria.
Since the database can contain a huge amount of users, I would like to avoid retrieving every single user from the database and filtering it manually, to avoid loading times.
The relevant data stored in each user document includes: date of birth
(Timestamp), an array of user interests
(list of strings), an array of already matched user ids
(list of strings), and the user's gender preference
(string F or M).
It should find user data according to the following queries:
- Date of birth must be at most 2 years lower or higher than the current user
- The interests array must contain at least one common interest with the current user
- The gender must match the current user's gender preference
- The user id must not already be in the current user's already matched user ids list
After the list of matching users is retrieved, I would also like it to be sorted by the amount of common interests with the current user, so the best match shows first.
So far the only solution I have is to get 50 random users from the database, check if they match (in code), and if less than 6 matches are found, keep repeating until at least 6 matches are found. In case no matching users are found in the entire database, then simply get 6 random users.
I am aware of the limitations that Firebase has when it comes to querying and filtering data, so I'm asking if something like this is even possible?
CodePudding user response:
Are you using the Firestore as well with your Firebase?
There you could use queries on the firebase directly.
Query query = users.whereEqualTo("user_interest", "sports");
Or use "in"-operator like this:
Query query = users.whereIn("user_interest", Arrays.asList("sports", "books"));
You don't have to fetch all users this way.
Checkout this link for more information: https://firebase.google.com/docs/firestore/query-data/queries?hl=en
CodePudding user response:
This type of relational data doesn't suit a NoSQL document database very well (especially when disqualifying results based on if you've seen them before), but lets ignore that for now.
Combining condition 1, 2 and 3 is possible. But you will need to disqualify results based on condition 4 on the side requesting the data (i.e. on the client/backend - Firestore's indexes can't handle it). This is because conditions 2 and 4 both rely on arrays of data and you are limited to querying against one at a time. As the number of user matches is likely to be low vs the number of users on the platform, condition 4 is better suited to code-based filtering. These limitations are well documented.
Note: Due to the sensitive nature of a user's dating profile, a profile should only contain a user's ID and profile data (no emails or phone numbers). Furthermore, you should consider serving profiles via a regulated backend such as Cloud Functions rather than just making them client-readable directly which could lead to platform abuse.
const profilesColRef = collection('profiles');
const potentialMatchesQuery = query(
profilesColRef,
where('dob', '>=', '1996-01-01'), // use YYYY-MM-DD format (numeric/date values can get messy with timezones if not careful)
where('dob', '<=', '2000-01-01'),
where('gender', '==', 'M'),
where('interests', 'array-contains-any', userInterestArray), // supports up to 10 per query! Split into chunks of 10 and merge results for more interests
limit(50)
);
const querySnapshot = await getDocs(potentialMatchesQuery)
const results = querySnapshot.docs
.filter((doc) => !userPreviousMatches.includes(doc.id))
.map((doc) => ({ id: doc.id, ...doc.data() }) // expand to basic JavaScript object
// results now contains up to 50 matches
// return results to client/use results