0

I am not really sure that this is the best way to do this, but when putting just a regular array, I was not returning anything.

I have a firebase collection that looks something like this in Swift:

struct Tournament: Identifiable, Codable, Equatable, Hashable {
    var playersUID: [String] = []  
}

I have an array of UIDs that I want to query against my Player documents

struct Player: Identifiable, Codable, Equatable, Hashable { }

I am trying to pass the playersUID Array into searchPlayers() but I think instead of taking each value, it is trying to give the entire array into the search function:

func searchPlayers() async {
    do {    
        let documents = try await Firestore.firestore().collection("Players")
            .whereField("playersUID", isGreaterThanOrEqualTo: tournament.playersUID). 
            .whereField("playersUID", isLessThanOrEqualTo: "\(tournament.playersUID)\u{f8ff}")
            .getDocuments()
            let player = try documents.documents.compactMap { doc -> Player? in
                try doc.data(as: Player.self)
            }
            await MainActor.run(body: {
               fetchedPlayers.append(contentsOf: player)
            })
    }

that was one way... the other way I tried this was using a for-loop:

do {
            let lastIndex = Int(tournament.playersUID.last!) ?? 0
            for i in 0...lastIndex {
                let documents = try await Firestore.firestore().collection("Players")
                    .whereField("playersUID", isGreaterThanOrEqualTo: tournament.playersUID[i])
                    .whereField("playersUID", isLessThanOrEqualTo: "\(tournament.playersUID[i])\u{f8ff}")
                    .getDocuments()
                
                let player = try documents.documents.compactMap { doc -> Player? in
                    try doc.data(as: Player.self)
                }
                
                await MainActor.run(body: {
                    fetchedPlayers.append(contentsOf: player)
                })
            }
            
            
        } catch {
            print(error.localizedDescription)
        }

I really don't know what I am doing wrong here. Is there a unique function that Firebase gives for querying?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Anish
  • 15
  • 6
  • Since your `Tournament.playersUID` is an array and you pass `.whereField("playersUID", isGreaterThanOrEqualTo: tournament.playersUID)`, you're indeed comparing the full array in your code with the full array in the database. What other operation are you trying to execute here? – Frank van Puffelen Jan 10 '23 at 04:30
  • I am trying to fetch the full document that matches each value inside of `Tournament.playersUID`. Just for conformation, I will not have to use a for-loop right? – Anish Jan 10 '23 at 05:13
  • 1
    If you want to get the document(s) where the `playersUID` contains exactly the same values as `tournament.playersUID`, you can use `.whereField("playersUID", isEqualTo: tournament.playersUID)`. For this to work it is important though that you have the array elements in the exact same order in both arrays. – Frank van Puffelen Jan 10 '23 at 15:00
  • The douments won't have the same elements both ways...is there any other way that I could do this? – Anish Jan 10 '23 at 21:47
  • No. As said, you can only do an equality comparison if the items are in the same order. Failing that the best you can do is query for one of the values (with `array-contains`) and then performing the other checks client-side. There is no `array-contains-all` operator, which what you'd need for this. I linked a question that shows how to implement this use-case based on a map, instead of an array. – Frank van Puffelen Jan 10 '23 at 21:49
  • Ok, maybe I will just try the querying for just one of the variables. Thank you. – Anish Jan 10 '23 at 21:50
  • @Anish Updated the code as per your latest changes. – Rohit Kharche Jan 13 '23 at 14:42

1 Answers1

0

You want to filter collection on the basis of the playerUIDs present in the tournament.playersUID so it is best practice to use in, not-in, and array-contains-any operators to filter the firestore collection for array fields.

for loop is not going to work either, since the i variable is an index to a specific element of the tournament.playersUID array and you're still looking for an exact match.

In my opinion there are 2 ways you can achieve the desired results:

  • One way to achieve this is to use the whereField("playersUID", arrayContains: ) method multiple times, once for each value in the tournament.playersUID array. :
let documents = try await Firestore.firestore().collection("Players")
    .whereField("playersUID", arrayContains: tournament.playersUID[0])
    .whereField("playersUID", arrayContains: tournament.playersUID[1])
    .whereField("playersUID", arrayContains: tournament.playersUID[2])
    .getDocuments()
    ...

This will return all documents in the "Players" collection where the "playersUID" field has a value that is in the tournament.playersUID array which satisfies the above checks.

  • The following approach is also viable if and only if your tournament.playersUID is in the exact same order as in firestore playersUID array field just like @Frank van Puffelen mentioned in the comments:
let documents = try await Firestore.firestore().collection("Players")
    .whereField("playersUID", arrayContainsAny: tournament.playersUID)
    .getDocuments()
    ...

It will filter out all players whose playersUID field doesn't contain any of the UID in the array.

Rohit Kharche
  • 2,541
  • 1
  • 2
  • 13