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.
How to query all childs values from child( firebase)
Seems like you want to find all children where values contains certain String.
equalTo() Return items equal to the specified key or value depending on the order-by method chosen.
DatabaseReference rootReference = FirebaseDatabase.getInstance().getReference();
DatabaseReference resources01Referense = rootReference.child("01").child("tags");
Query query = resources01Referense.orderByValue().equalTo(text+"\uf8ff");
Then query your data
Link to do documentation
It is possible to have a contains search query in firebase?
For small datasets you can use the following code:
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> list = new ArrayList<>();
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String from = ds.child("from").getValue(String.class);
list.add(from);
}
for(String str : list) {
if(str.contains(searchText)) {
Log.d("TAG", "String found!");
}
}
}
@Override
public void onCancelled(DatabaseError error) {
Log.d("TAG", error.getMessage()); //Never ignore potential errors!
}
};
myRef.addListenerForSingleValueEvent(valueEventListener);
This solution is not recommended for large datasets because you'll need to download the entire node in order to make a search. In this case, Algolia or Elasticsearch are recommended.
If you intend to use Cloud Firestore, I recommend you to see my answer from this post. For more information, I also recommend you see this video.
Firebase search by child value
You can use equalTo()
to find any child by value. In your case by name
:
ref.child('users').orderByChild('name').equalTo('John Doe').on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});
The purpose of orderByChild()
is to define the field you want to filter/search for. equalTo()
can get an string, int and boolean value.
Also can be used with auto generated keys (pushKey
) too.
You can find all the documentation here
How to search for a values of Child in firebase Android
In the Web version, they use something called ElasticSearch, you could try to read more here: https://firebase.googleblog.com/2014/01/queries-part-2-advanced-searches-with.html
But for Android, I think there isn't any functionality to perform a search like that. What I would do is to query all the records then filter them myself:
DatabaseReference databaseReference = mDatabase;
mDatabase.addValueEventListener(new ValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot val : dataSnapshot.getChildren()){
//I am not sure what record are you specifically looking for
//This is if you are getting the Key which is the record ID for your Coupon Object
if(val.getKey().contains("Hotel")){
//Do what you want with the record
}
//This is if your are querying for the hotel child
if(val.child("hotel").getValue(String.class).contains("Hotel")){
//Do what you want with the record
}
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {
}
}
Related Topics
Create Singleton Using Gcd'S Dispatch_Once in Objective-C
Delete/Reset All Entries in Core Data
How to Tell If Uiviewcontroller'S View Is Visible
Duplicate Symbols For Architecture X86_64 Under Xcode
Why Isn't Projectname-Prefix.Pch Created Automatically in Xcode 6
How to Change Uipickerview Height
Uicolor Not Working With Rgba Values
Upload Image With Parameters in Swift
How to Programmatically Get the MAC Address of an Iphone
How to Change the Push and Pop Animations in a Navigation Based App
Add Uipickerview & a Button in Action Sheet - How
Starting Iphone App Development in Linux
How to Perform Unwind Segue Programmatically