Firebase database - Query by list of ids - android

I have recently started switching my app from Parse to Firebase. Everything is going great so far, but I have not been able to find a Firebase equivalent method to Parse's whereContainedIn(String key, Collection values).
This was a very useful method that allowed me to pass in an array of ids and it would return all of the rows that matched that id. So far with Firebase, I have it returning all all of the rows in the database then looping through them to see if that row's id is contained in my array of ids. The other way is to query for each id individually, which doesn't work well with Firebase's asynchronous behavior. This is what I am doing now for the first approach:
List<String> userIds = new ArrayList<>();
userIds.add("2");
userIds.add("32");
userIds.add("12545");
userIds.add("187");
DatabaseReference firebaseRef = FirebaseDatabase.getInstance().getReference();
Query queryRef = firebaseRef.child("videos");
queryRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Video> usersVideos = new ArrayList<Video>();
for (DataSnapshot videoSnapshot : dataSnapshot.getChildren()) {
Video video = videoSnapshot.getValue(Video.class);
if (userIds.contains(video.userId)) {
usersVideos.add(video);
}
}
// usersVideos is now populated with the videos for the users
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "CANCELLED");
}
});
Neither of these methods seem reasonable since my table will contain hundreds of thousands of records.
Any help would be greatly appreciated!

Make another reference that can be looked up by userId and have it return all videoIds that this user has. From there you can query the videos. That means each time you save you will need to save in two spots. Sort of a pain, but it is what Firebase recommends.
So your reference will look more like:
videos -
| - <videoId> -
| - <video stuffs>
| - userId
videosByUser -
| - <userId> -
| 0 - <videoIdA>
| 1 - <videoIdB>
| - <userId2> -
| 0 - <videoIdA>

can't you just perhaps do a for loop over your ids and inside the for loop write a valueventlistener that will be called for each iterated item`?

Related

Android Studio: Query documents containing array node using a substring

I am trying to search Documents in a Collection which contain a certain key.
Here is how I structured my Firestore database:
-- FirestoreRoot
|-- Products (Collection)
|-- Departments (Document)
|-- Food (Colletion)
|-- {Id} (Document)
-- description : "this is my very first description"
-- keywords :
-- 0 : this
-- 1 : is
-- 2 : my
-- 3 : very
-- 4 : first
-- 5 : description
In the example below, I was able to search by using a substring for the first word in the description. To this date, this method does not work for querying the following words. In the example below, typing the letters "thi" is enough to return documents.
CollectionReference colecRef = FirebaseFirestore.getInstance()
.collection("Products")
.document("Departments")
.collection("Food");
Query query = colecRef;
query.whereGreaterThanOrEqualTo("description", searchField.getText().toString().toLowerCase())
.whereLessThan("description", searchField.getText().toString().toLowerCase()+'\uf8ff')
.get().addOnSuccessListener(SearchActivity.this, new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
Log.i(TAG, "onSuccess: "+documentSnapshot);
}
}
});
In my case, I need to search by using 1 or multiple keys, for example: first or first description. And by using the whole word or only a substring, for example: first or descr
I've tried to use whereArrayContains(), but I can't use it typing multiple keys or substring.
query.whereArrayContains("keywords",searchField.getText().toString().toLowerCase())
.get().addOnSuccessListener(SearchActivity.this, new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
Log.i(TAG, "onSuccess: "+documentSnapshot);
}
}
})
I appreciate any help.
Firestore currently only offers two ways to query the contents of an array:
Array membership with whereArrayContains(). This will tell you if an element exists in an array. It must match exactly - no substrings.
array-contains-any memberhip with whereArrayContainsAny(). This will tell you if any of the given strings exist in the array. It must match exactly - no substrings.
As you can see, searching substrings in arrays isn't going to work at all. You might want to consider using another database in tandem with Firestore in order to satisfy these specific queries, as Firestore is not very well suited for them.

Accessing data from firebase realtime database inside recyclerview

My recyclerview gets data from json api and populate list of articles. I have now implemented a commenting system with the help of firebase realtime database. I want to show number of comments below each article image in recyclerview. I tried several methods to implement that but all of them are not very effective.
At first I implemented database query based on article unique id for each view but since recyclerview has over 100 articles so it was making over 100 instance calls to database and was causing huge bandwidth problem.
Then I made one query to get all comments count from database and saved them locally in SQLite database and inside recyclerview i query SQLite databse to get comments counts but inserting 100 rows with article id and comments count in SQLite is slow.
What do you guys recommend best method for such task where I will spend least amount of bandwidth and get comment counts also?
My db structure is like this.
get comments method
public void getComments() {
keyrf = FirebaseDatabase.getInstance().getReference().child("Keys");
keyrf.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
HashMap map = new HashMap();
for( DataSnapshot child : dataSnapshot.getChildren() ) {
String childKey = child.getKey();
String c = child.child("c").getValue().toString();
map.put(childKey, c);
addComments(MainActivity.this, childKey, c);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
insert comments method
public static void addComments(Context context, String childKey, String c) {
try {
SQLiteDatabase myDB = context.openOrCreateDatabase("MyDb", Context.MODE_PRIVATE, null);
myDB.execSQL("CREATE TABLE IF NOT EXISTS comments (articleId INTEGER not null unique, comment INTEGER not null)");
String sql = "REPLACE INTO comments (articleId, comment) VALUES (?, ?)";
SQLiteStatement statement = myDB.compileStatement(sql);
statement.bindString(1, childKey);
statement.bindString(2, c);
statement.execute();
} catch (Exception e) {
}
}
What do you guys recommend best method for such task where I will spend least amount of bandwidth and get comment counts also?
The workaround here is to keep a count property somewhere in the database and update that whenever you add/delete child nodes.
So you can consider using a new section that might look similar to this:
Fireabase-root
|
--- numberOfComments
|
--- commentId: 12
|
--- commentId: 10
|
--- commentId: 20
So everytime you add or delete a post, increase / decrease that count by one. And because the number of comments might be updated in an multi user environment, I recommend you to use FirebaseTransactions as explained my answer from this post.

Android Firestore, Query Array

I want to query the highlighted values from the above database
My code is incomplete I can't figure how to complete the query.
I want to know if 123,456,789 exists anywhere in the entire collection,
like for a normal query .whereEqualTo("address","P18/A CIT Road Ananda Palit"); would give me the third document,here I want to query card
CollectionReference ref = db.collection("company");
Query query = ref.whereEqualTo("card[]","???");
To solve this, please use the following code:
ref.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (DocumentSnapshot document : task.getResult()) {
List<String> list = (List<String>) document.get("card");
for (String item : list) {
Log.d("TAG", item);
}
}
}
});
The output will be:
123
456
789
//and so on
Remember, document.get("card") does not return an array, it returns an ArraList.
If you want use a Query using whereEqualTo() method then I suggest you change your database a little bit. Your database structure shoould look like this:
Firestore-root
|
--- company
|
--- companyDocumentId
|
--- address: "P18/A CIT Road Palit"
|
--- card
|
--- 123: true
|
--- 456: true
|
--- 789: true
In this case the your query should look like this:
Query = ref.whereEqualTo("card.123", true);
Edit:
According to your commend the query should be:
Query = ref.whereEqualTo("card.E20040806709002620760CE82", true);
Edit 13 Aug 2018:
According to the updated documentation regarding array membership, now it is possible to filter data based on array values using whereArrayContains() method. A simple example would be:
CollectionReference citiesRef = db.collection("cities");
citiesRef.whereArrayContains("regions", "west_coast");
This query returns every city document where the regions field is an array that contains west_coast. If the array has multiple instances of the value you query on, the document is included in the results only once.

How to query highest values if children have dynamic keys in Firebase? [duplicate]

My Firebase Database is like this
When the coding below was run:
String loc=(snapshot.child("loc").getvalue()).tostring();
The output I get has different sequence with the database:
Why is that so?
Firebase data is stored as JSON and is inherently unordered.
If you want to access the data in a specific order, you should specify an orderBy clause for your query and access the data using the snapshot's getChildren() method.
Say you want to log the items by ascending key:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getRef();
Query locations = rootRef.orderByKey();
locations.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot locSnapshot: snapshot.getChildren()) {
System.out.println(locSnapshot.getKey() + ": " + locSnapshot.getValue(String.class));
}
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
This sample comes (modified) from the Firebase documentation on reading lists of data.
Frank beat me to my edit, check out his correct solution using orderBy....
You need to use forEach rather than the child method (or child.foreach)
Here is a snippet from the doc:
Because of the way JavaScript Objects work, the ordering of data in
the JavaScript Object returned by val() is not guaranteed to match the
ordering on the server nor the ordering of child_added events. That is
where forEach() comes in handy. It guarantees the children of a
DataSnapshot will be iterated in their query-order.
Source: https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach

Read data from different directories using Firebase for Android

/users:
- user1
- name: user1name
- /contacts
- user2
- user3
.....
- user2
- name: user2name
....
This is my current firebase database. When user1 is logged in I check which contacts he has and get their UIDs. Now that I have their UIDs I'm trying to read the names of his contacts. How can I get the names for each of his contacts? Do I have to read the entire /users directory or can I use a query that retrieves only the data that I want. (In this case: the name of user2 and user3)
You'd do a nested read. So retrieve /users/$uid/contacts, then loop over the keys in there and for each key load /users/$key.
Supposing that you already have the user selected, you can go about it in two ways:
1) Use .child(String user); in your database reference.
2) You may have declared a URL for your database. Add + "/" + String user; to your URL.
You can further use these ways to parse through contacts of that user.
Finally, to retrieve contacts, you have to loop through.
Here is an example code:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot msgSnapshot : snapshot.getChildren()) {
mDataset.add(msgSnapshot.getKey());
mAdapter.notifyDataSetChanged();
}
}
This snippet fetches data and adds it to your adapter (which can be used to populate list view, grid view etc)

Categories

Resources