0
votes

I have a list of users in an array: let userArray = ["userId1", "userId2"]. I'm trying to pull the list of all comments for a given postId and filter the results to only pull those where the userId is in userArray. Below is my code so far but it doesn't seem to be working. Is there a better way to approach the problem?

//pulls all comments given a postId of "postId1"
  let commentsRef = databaseRef.child("comments").queryOrdered(byChild: "postId").queryEqual(toValue: "postId1")

      commentsRef.observe(.value, with: { (comments) in

        var resultArray = [Comment]()
        for comment in comments.children {

           let value = child.value as? NSDictionary

           let userId = value?["userId"] as? String ?? ""

               for contactinfo in userArray {
                   if(contactinfo = userId) {
                     let comment = Comment(snapshot: comment as! DataSnapshot)
                                resultArray.append(comment)
                            }
                        }
                        completion(resultArray)

                    }
                })
    })

Firebase:

Comments
-CommentId1
 -postId: "postId1"
 -userId: "UserId1" //RETRIEVE
-CommentId2
 -postId: "postId2"
 -userId: "UserId50" //DO NOT RETRIEVE
2
"I have a list of users in an array" Firebase also has a list of products.El Tomato
Firebase Database queries can only order/filter on a single property. In many cases it is possible to combine the values you want to filter on into a single (synthetic) property. For an example of this and other approaches, see my answer here: stackoverflow.com/questions/26700924/…Frank van Puffelen
I'm going with @FrankvanPuffelen on this. As an alternative, depending the quantity of data, you could add a .childAdded event to the Comments node - that will iterate over each child node (CommentId1, CommentId2 etc). Compare the userId child value with what's in your array and ignore the ones that don't match and process the ones that do. OR if there are just a couple hundred comments, read them in via observerSingleEvent(.value... and iterate over the child nodes in code; ignore the ones that don't match and process the ones that do. It all depends on how many child nodes and qty of data.Jay
As a side note, your code isn't working because the database node is Comments (capital 'C') and your code is trying to read 'comments' (lower case 'c'). Other than that, it's pretty close to one of my suggestions, although I would suggest creating a class var array to store the comment (self.commentArray) and populating that and upon completing the for loop do something with the array. Also, you are adding an observer query so anytime anything changes that matches your query, the code in the closure will be called - not sure if that was intentional or not.Jay
Thanks Jay. Sorry, I'm relatively new to Firebase so excuse my basic questions. Can you provide an example of what you mean by this "As an alternative, depending the quantity of data, you could add a .childAdded event to the Comments node - that will iterate over each child node (CommentId1, CommentId2 etc). Compare the userId child value with what's in your array and ignore the ones that don't match and process the ones that do." What would a sample code look likeJohn Wong

2 Answers

0
votes

In this condition, you are doing a 'and' query, according to the doc, you should put multiple query together like this.

// Create a reference to the cities collection
let commentsRef = db.collection("comments")

// Create a query against the collection.
commentsRef.whereField("postID", isEqualTo: "post1")
commentsRef.whereField("userID", isEqualTo: ['user1', 'user2'])

Checkout the doc for more detail. https://firebase.google.com/docs/firestore/query-data/queries.

---EDIT---

Emmmmm, I will remain above answer if you change your database selection.

I read this link Query based on multiple where clauses in Firebase .

So I think you should be able to add a custom index on you comments model like this

Comments
  -CommentId1
    -postId: "postId1"
    -userId: "UserId1" //RETRIEVE
    -idx: "postID1+UserID1"

Now you could do the query on this index. And if you want to retrieve multiple users records, you could simply loop the users array.

0
votes

Here are two examples of how to retrieve the posts from a specific user - either option could be expanded to exclude posts from unwanted users by adding additional if statements.

Here's the Firebase structure

comments
-CommentId1
 -postId: "postId1"
 -userId: "UserId1" //RETRIEVE
-CommentId2
 -postId: "postId2"
 -userId: "UserId50" //DO NOT RETRIEVE

and the code tied to two different buttons in the UI

var postsArray = [String]()
let userIdWeWant = "userId1"

func button0() {
    self.postsArray = []
    let commentsRef = self.ref.child("comments")
    commentsRef.observe(.childAdded, with: { snapshot in
        let dict = snapshot.value as! [String: Any]
        let userId = dict["userId"] as! String
        if userId == self.userIdWeWant {
            let post = dict["postId"] as! String
            self.postsArray.append(post)
            print(post)
        }
    })
}

func button1() {
    self.postsArray = []
    let commentsRef = self.ref.child("comments")
    commentsRef.observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let childSnap = child as! DataSnapshot
            let dict = childSnap.value as! [String: Any]
            let userId = dict["userId"] as! String
            if userId == self.userIdWeWant {
                let post = dict["postId"] as! String
                self.postsArray.append(post)
            }
        }
        print(self.postsArray)
    })
}

As it is, we are just capturing posts from userId1 and adding them to an array, but say we wanted to capture posts from any user that loves pizza and has a shoe size of 13. So the structure would be

comments
    -CommentId1
     -postId: "postId1"
     -userId: "UserId1" //RETRIEVE
     -food: "Pizza"
     -shoe: "13"

and a code snippet would be

let shoe = dict["shoe"] as! String
let food = dict["food"] as! String
if shoe == self.shoeWeWant && food == self.foodWeWant {

EDIT

Per a question in a comment, 'get any uid stored in the uid array', it's super simple in Swift 4. Here's an example to see if an object exists in an array

var uidArray = ["uid0", "uid1", "uid2", "uid3"]
if uidArray.contains("uid1") {
    print("it exists! Do something with it!")
} //otherwise ignore it

that technique could be used to see if the read in array with

let userId = dict["userId"] as! String

exists in the array of users you are interested in.