-1

I need to make a Firestore query in a collection that:

  1. is ordered by field "timestamp"
  2. the field "uid" is equal to any of the elements of a strings array (more than 10)
  3. I need to paginate the query so that I'm able to perform later more queries starting from a precise timestamp

The code should look like this:

Firestore.firestore()
    .where(field: "uid", in: arrayOfUids)
    .order(by: "timestamp", descending: false)
    .limit(to: 5)

or in case of starting from a particular timestamp

Firestore.firestore()
    .where(field: "uid", in: arrayOfUids)
    .startAt("timestmap", lastTimestampFetched)
    .order(by: "timestamp", descending: false)
    .limit(to: 5)

How can I achieve this result? In this topic was suggested to perform multiple calls filtering with up to 10 elements in the array in each call (the maximum number of array elements for Firestore to be able to compare) but I didn't have the constraint of ordering by timestamp.

EDIT: I cannot order the query result client-side, it wouldn't be helpful for my use case. Let's say that we have 30 uids to query for: First, we have to divide these uids into groups of 10.

So we'll have 3 groups of 10 uids each (the max array lenght to for querying with Firestore); for each of this groups we'll do a separate query which looks like this:

Firestore.firestore()
        .where(field: "uid", in: arrayOfTenUids)
        .startAt("timestmap", lastTimestampFetched)
        .order(by: "timestamp", descending: false)
        .limit(to: 5)

For each query, we save the last document timestamp in order to be able to perform successive queries with pagination.

Here is the problem, we are going to have multiple last timestamps, since we are performing multiple queries, which timestamp are we going to save?

1st option: the last timestamp of all last timestamps

2nd option: the first timestamp of all last timestamps

1st case -> in successive queries we are going to fetch some documents that have been previously fetched

2nd case -> in successive queries we are going to miss some documents that have been previously fetched (because the last timestamp of one query could be after the last timestamp of another one)

This topic is really hard to explain but I did my best.

StackGU
  • 868
  • 9
  • 22
  • The question and the expected result is a bit unclear. The code in the question will return all documents that have a uid field value that matches one of the arrayOfUid's. Is that code not working? Or do you want to query for more than 10 values in the array? – Jay Oct 22 '21 at 16:55
  • exactly the second option, I want to query for more than 10 values in the array – StackGU Oct 22 '21 at 16:57
  • The answer by @alexmamo in the link you provided is spot on. The ordering of said data will have no impact. Actually, thinking through that, you'll need to perform multiple queries and combine them in code anyway so that's the time to apply a sort - once you have all of the data. Is there something more? – Jay Oct 23 '21 at 15:00
  • I wanted to sort the documents using timestamp because, since the documents in the collection are a lot, I needed to paginate the query, and that's the reason why I wanted to order the data so that I can get some of them and not all the collection, but still have it ordered. – StackGU Oct 23 '21 at 20:16
  • The answer in the [topic](https://stackoverflow.com/questions/69597834/firestore-query-passing-in-an-array-of-string-firestore-swift) suggested to combine the result of the queries in the client. Have you tried to sort it there? – Javier A Oct 25 '21 at 15:05
  • @JavierA I can't sort them client side because I need to paginate the query, I will explain it more in the EDIT I'm going to make in the original question – StackGU Oct 25 '21 at 17:05
  • Also, I don't understand why people continue to vote for Closing the Topic saying it's not clear instead of trying to help people out. Oftentimes people on StackOverflow are just sick. Continuously trying to prove people wrong without helping them out smh... – StackGU Oct 25 '21 at 17:32
  • While I understand your frustration, the last comment is not necessary nor productive. I would suggest deleting it. – Jay Oct 25 '21 at 18:35
  • The bottom line is you can't do what you're attempting to do in the way you're attempting to do it. Firestore doesn't have a join or a way to get an unlimited number of matches from a given list. So, you need to re-think your structure and develop a way to organically group the things you need in timestamp ordered. If we had a clear understanding of your Firestore structure (in brief) and *why* you're trying to match more than 10 uid's we could probably come up with another solution to get you the uid's in timestamp order for pagination. – Jay Oct 25 '21 at 18:39
  • Thanks for the feedback @Jay . I have a collection of "Posts" that people share, and then I have another collection of "Matches" where all uids have a subcollection of other uids with which they have matched. I retrieve all the matches uids (String) that the current uid has (oftentimes more than 10) and I save them in array. Then I want to query in the collection of "Posts" where the field "uid" is equal to any of this uids in the array. I want the results ordered by timestamp so that I can paginate them without repeating the same document over and over. – StackGU Oct 25 '21 at 19:04
  • In your answer to my comment you state you are going to paginate the query. What query is that? I understand you will execute 3 queries. Also, showing for example the most recent timestamp of each query does not mean you are showing the 3 most recent timestamps. – Javier A Oct 26 '21 at 07:50
  • @JavierA read my edit to the Original Question and you will understand. You are exactly on the right point, I need to paginate all three queries in this case. – StackGU Oct 26 '21 at 10:22
  • You want to get the most recent entries for each field or the most recent entries for every field in the arrays you use? I was talking about that in my previous comment. – Javier A Oct 26 '21 at 14:55
  • Why don't you just add this users uid as a field in Posts (or maybe to an array in posts so you can store multiple uids). Then you can query Posts for any that match uid and get the results you want. – Jay Oct 26 '21 at 17:17
  • @Jay that's a cool idea but requires a lot of work, hypotetically serverside using cloud functions. Whenever a user matches with another user, I need to add the matched uid to all the posts documents that the user has created till that moment... it seems like a huge work, if I understand correctly. – StackGU Oct 26 '21 at 22:52

1 Answers1

2

Based on the comments let me try to create the current Firestore structure and re-state the objective, then suggest a solution. I am ignoring the timestamp for the moment:

Posts (collection)
   post_0
      uid: uid_4
   post_1:
      uid: uid_1
   post_2:
      uid: uid_3
   post_3:
      uid: uid_2

and then

Matches (collection)
   uid_0
      uid_1
      uid_2
      uid_3
   uid_5
      uid_4

The objective is for the current user, say uid_0, to read their node within the Matches collection which would be this

   uid_0
      uid_1
      uid_2
      uid_3

and then get the posts from the Post nodes where the uid field matches the uid's in that list. The result should be post_1, post_2 and post_3.

The issue is the users node, uid_0, could have dozens of child uid's and since Firestore can only match up to 10 at a time using the 'in' function

.where(field: "uid", in: arrayOfUids)

it would take multiple reads - and then there's the issue of the timestamp and sorting along with paginating.

My suggestion is to simplify; if the above stated goal is correct, instead of storing those different uid's with each post, just store the uid of the current user, like this

Posts (collection)
   post_0
      uid: uid_2
   post_1:
      uid: uid_0
   post_2:
      uid: uid_0
   post_3:
      uid: uid_0

Then query the Posts node for uid = uid_0. The result will be exactly the same and much simplier to implement and maintain and then the timestamp/pagination issue is no longer and issue.

Obviously this is a simple example; I would imagine you want to store multiple uid's within each post, so whichever user is logged in can retrieve their selection of posts. Do to that, use array contains to retrieve the posts that match for this user.

Posts (collection)
   post_0
      uid_array: 
         uid_2
         uid_9
         uid_21
Jay
  • 34,438
  • 18
  • 52
  • 81
  • You completely understood what I meant, thanks so much for the answer. Basically, whenever a new match is created I need to add a new uid in "uid_array" to each Post corresponding to the matched uids . Obviously, I will do it serverside, do you think it's the right approach? – StackGU Oct 27 '21 at 21:27
  • 1
    @StackGU Sounds like you're on track. – Jay Oct 28 '21 at 16:54