3
votes

I'm trying to find a way to properly desing my Data Model with Firestore. I'm looking for something similar to what Tinder does, showing you people that you have'nt swiped yet, based on your location.

So I ended up with something like :

  • A User1 has an array of "met people"
  • A "Haven't yet met user"/ User2 his also a User with the same document model
  • They all belong in the same "Users" collection
  • I want to query all the users that this User1 haven't swiped yet

I know that you can't do something like "array_not_contains" or "!=" because all fields that you query need to be indexed.

So I wonder, is this possible to model data to make it work, or the only solution is to drop Firebase because this kind of query is not possible at all?

One alternative can be to store in a collection all the relationships (with theirs status) between all users. But that also means that whenever a user signup, I have to create as many documents as I have users that's really ugly and make a enormous numbers of documents.

EDIT:

Thanks again for your answer and sorry for my late answer.

There is no need to create a new database call since you already got all the users from that area in the first place.

Not If have a large response set, I will limit to a number. (5 in the example below). And even If I don't limit the number, in the next db call, how I can know that new peoples has been added and how to retrieve only those.
I will not remove them from Users Collection has they can be show to others users.

P.S: I forget User4 in Users Collection pictures.

Initial db state

For User 1, get 5 first matchs, remove existing ones, show User5.
For User2, get 5 first matchs, remove existing ones, show User4, User5.
After users choices, Users are added to their list. Users Collection stay the same.

step 2

For User 1, get 5 first matchs, remove existing ones, nothing to show, even if I have a User 6, 7.
To fix that I launch a second query get the new ones but, more the user use the app more query I may need to do to try to display to him existing user in his area.

Maybe I've misunderstood what you named "initial list", for me it is the list object retrieve from my db containing all users (with limit).

EDIT 2:

You can check the answers of Alex Mamo to know how to query documents that are not exist in an array possible.

Let's me explain my use case and why I think, that won't work.

I want to be able to search all users next to me, for trying to do that in Firebase, I store Geopoint. Geopoint can't be really use for now out of the box with Firebase, so I user Geofirestore in a Cloud Function.
I store and update user Geopoints based on theirs locations, so this means user location change by time. I limit the numbers of Users return by this function.

initial search

In my initial state I retrieve users next to me (User1), I get 3 an 4.
Let's say that I store last checked userId to use it later as a cursor for my query (User 4).

Now my geopoint change, and the users in this area changes too. I request next bunch of users next to me, and I use my previous userId/document to "startAfter" (more on this
here), see the image below, that's won't work.

second search

If I use the cursor (User4), I'll take 5, but not 2, because in the return list, if I order by Id, 2 will be before 4.
Worse, like below, if the return list may not even have user 4 in it, the cursor will be pointless.

thirdsearch

My example is a bit simplified and does not take in account what is described in the first answer and my first edit (limited subset of users, data design).

1

1 Answers

4
votes

A possible database structure for your app might be:

Firestore-root
    |
    --- users (collection)
         |
         --- uid (document)
              |
              --- acceptedUsers: ["uidOne", "uidTwo"]
              |
              --- declinedUsers: ["uidThree", "uidFour"]
              |
              --- //Other user properties

The mechanism is simple. When you first want to show a user profile to the current (authenticated) user, you have to create a query that will return all users (in user area). According to the user decision, you need to add the corresponding uid in either the acceptedUsers array or in declinedUsers array. Once you want to show another users, use the same query but this time, you need to make an extra operation. Once the query returns the users within user location, add all those users to a list. Compare the list that is coming from the database with your exting arrays and remove all the users from both arrays. In this way you'll have a list that contains only users that the actual user didn't see. This extra step is needed to make sure the id of the user does not exist in one of those arrays. In the end, simply choose a random user from the list and show the details to the user. That's it!

One alternative can be to store in a collection all the relationships (with theirs status) between all users. But that also means that whenever a user signup, I have to create as many documents as I have users.....that's really ugly and make a enormous numbers of documents.

This is not an option. This means that you need to write each time a user joins your app an enormous amount of data, which will be very costly. Since everything in Firestore is about the number of read and writes, I think you should think again about this approach. Please see Firestore usage and limits.

Edit:

Let's consider the initial list of users that has 10 records. With other words, all the users within that area are 10. You say that 7 users are already seen, that makes the list contain only the 3 remaining users.

So I display the 3, (or I do another request to get some more) and he check the 3.

Yes, you should display those 3 users and then remove them one by one from the initial list. There is no need to create a new database call since you already got all the users from that area in the first place. Once the list remains empty, you should display a message to the user that in that particular area are no more users to swipe.

When will create another database call?

Only when needed. Which means that you create another call once new users enter that area. Let's say 3 new users are new, you get a list now of 3 user and use the same algorithm.

More my user use the app more it’s difficult to show people that he haven’t seen, because his list become bigger.

If you think that the arrays will grow more than a document can hold, then you should consider storing the users in a collection and not in an array. So in this case, the problem is that the documents have limits. So there are some limits when it comes to how much data you can put into a document. According to the official documentation regarding usage and limits:

Maximum size for a document: 1 MiB (1,048,576 bytes)

As you can see, you are limited to 1 MiB total of data in a single document. When we are talking about storing text (uids), you can store pretty much but as your array getts bigger, be careful about this limitation.

But if you'll stay within this limits, which I personally think you'll do, you have nothing to worry about.

Edit2:

Not If have a large response set, I will limit to a number. (5 in the example below). And even If I don't limit the number, in the next db call, how I can know that new peoples has been added and how to retrieve only those. I will not remove them from Users Collection has they can be show to others users.

If you have large amount of data (many users in a single area), yes it's good idea to limit the results, but a much better idea would be to load the data in smaller chunks. In short, get 5 users, remove one by one till the list has zero users, load other 5 users and so on. This can be made using my answer from the following post:

The initial list, is the list that you are getting when you first query the database. In this case, the initial list will contain 5 users.