Firebase Query Containing Value

Firebase querying List String contains value

Firebase cannot perform a query across array members, to see if it contains a specific value in any position or to add/remove a record.

Note, that Firebase documentation recommends against using arrays. One of the many reasons Firebase recommends that is that it makes the security rules impossible to write.

As usual with NoSQL databases, I will suggest you an alternative structure like this:

  "SHOPPING_LIST" : {
"kPLZpp809gePpBiBojsngjZdg6A3:228c1e5b-fe11-4f06-ad32-c54734044a5e" : {
"listId" : "kPLZpp809gePpBiBojsngjZdg6A3:228c1e5b-fe11-4f06-ad32-c54734044a5e",
"listName" : "Lista de la compra de Jesús Lunar Pérez",
"owner" : "kPLZpp809gePpBiBojsngjZdg6A3",
"pendingListElements" : 0,
"sharedWithUsers" {
"kPLZpp809gePpBiBojsngjZdg6A3": true
}
}

So instead of using arrays, you can us a map, in which the key is the uid and the value is the boolean true.

You can also take a look, here, for more informations.

Firebase query if child of child contains a value

Your current data structure is great to look up the participants of a specific chat. It is however not a very good structure for looking up the inverse: the chats that a user participates in.

A few problems here:

  • you're storing a set as an array
  • you can only index on fixed paths

Set vs array

A chat can have multiple participants, so you modelled this as an array. But this actually is not the ideal data structure. Likely each participant can only be in the chat once. But by using an array, I could have:

participants: ["puf", "puf"]

That is clearly not what you have in mind, but the data structure allows it. You can try to secure this in code and security rules, but it would be easier if you start with a data structure that implicitly matches your model better.

My rule of thumb: if you find yourself writing array.contains(), you should be using a set.

A set is a structure where each child can be present at most once, so it naturally protects against duplicates. In Firebase you'd model a set as:

participants: {
"puf": true
}

The true here is really just a dummy value: the important thing is that we've moved the name to the key. Now if I'd try to join this chat again, it would be a noop:

participants: {
"puf": true
}

And when you'd join:

participants: {
"john": true,
"puf": true
}

This is the most direct representation of your requirement: a collection that can only contain each participant once.

You can only index known properties

With the above structure, you could query for chats that you are in with:

ref.child("chats").orderByChild("participants/john").equalTo(true)

The problem is that this requires you to define an index on `participants/john":

{
"rules": {
"chats": {
"$chatid": {
"participants": {
".indexOn": ["john", "puf"]
}
}
}
}
}

This will work and perform great. But now each time someone new joins the chat app, you'll need to add another index. That's clearly not a scaleable model. We'll need to change our data structure to allow the query you want.

Invert the index - pull categories up, flattening the tree

Second rule of thumb: model your data to reflect what you show in your app.

Since you are looking to show a list of chat rooms for a user, store the chat rooms for each user:

userChatrooms: {
john: {
chatRoom1: true,
chatRoom2: true
},
puf: {
chatRoom1: true,
chatRoom3: true
}
}

Now you can simply determine your list of chat rooms with:

ref.child("userChatrooms").child("john")

And then loop over the keys to get each room.

You'll like have two relevant lists in your app:

  • the list of chat rooms for a specific user
  • the list of participants in a specific chat room

In that case you'll also have both lists in the database.

chatroomUsers
chatroom1
user1: true
user2: true
chatroom2
user1: true
user3: true
userChatrooms
user1:
chatroom1: true
chatroom2: true
user2:
chatroom1: true
user2:
chatroom2: true

I've pulled both lists to the top-level of the tree, since Firebase recommends against nesting data.

Having both lists is completely normal in NoSQL solutions. In the example above we'd refer to userChatrooms as the inverted index of chatroomsUsers.

Cloud Firestore

This is one of the cases where Cloud Firestore has better support for this type of query. Its array-contains operator allows filter documents that have a certain value in an array, while arrayRemove allows you to treat an array as a set. For more on this, see Better Arrays in Cloud Firestore.

Flutter - Firebase - Query by list item value

I'm pretty sure firebase and Dart use the dot notation so you'd need this:

final Query nextSlot = FirebaseFirestore.instance
.collection('locations')
.where('availability.31', isGreaterThan: 0);

How to query documents where contains an array and the value of the array is [val1, val2] Firestore

When using the array-contains-any operator, you can check the values of your array against the value of a property of type String and not an array. There is currently no way you can use array-contains-any operator on an array. There are two options, one would be to create two separate fields and create two separate queries or, been only a document, you can get the entire document and filter the data on the client.

Edit:

What @FrankvanPuffelen has commented is correct, I made some research and I found that we can check against any type and even complex types, not just against strings, as mentioned before. The key to solving this issue is to match the entire object, meaning all properties of that object and not just a partial match, for example, one of three properties.

What you are trying to achieve is not working with your current database structure because your slug property exists in an object that is nested within the actual object that exists in your array. A possible solution might also be to duplicate some data and add only the desired values into an array and use the array-contains-any operator on this new creatded array.



Related Topics



Leave a reply



Submit