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?

7
  • 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? Commented Jan 10, 2023 at 4: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? Commented Jan 10, 2023 at 5: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. Commented Jan 10, 2023 at 15:00
  • The douments won't have the same elements both ways...is there any other way that I could do this? Commented Jan 10, 2023 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. Commented Jan 10, 2023 at 21:49

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I was not able to get the documents. This might be because the 2 different arrays will not be in the same order.
@Anish Updated the code as per your latest changes.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.