I'm trying to migrate an app I had built which used Parse as its cloud backend. In my Parse backend database, I had a table which stored data as shown below:
Device ID | Contacts
xxxxx001 | "(800)-888-8888"
xxxxx002 | "(800)-888-8858"
xxxxx003 | "(800)-888-8868"
Over here, device ID is the android device ID and the Contacts are an ArrayList of strings which was generated through logic on the device. Basically, the user would select a contact (multiple in future iterations, hence it being an ArrayList, for testing I'm just keeping one item in the list) and that contact is saved for that DeviceID in the backend database. If the same DeviceID changes the contact, the contacts ArrayList in the database corresponding to its DeviceID would be replaced with the new ArrayList.
I'm trying to get something similar set up on Firebase, however right now it seems I only have a global variable on my databse which seems to get updated each time I press my button.
Here is my code for the button:
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference mContactsRef = mRootRef.child("contacts");
#Override
protected void onStart() {
super.onStart();
mButtonContactSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mContactsRef.setValue(contacts); //contacts is an arraylist with 1 item
}
});
}
Here is how it looks in Firebase after clicking the button 3 times:
Instead of updating the value, it seems to add another row(?) of to store the current phone number selected. How can I go about setting up a DeviceID->(Objects to be stored per device)
sort of setup?
You're looking for push(), which generates a unique ID for new items.
From the Firebase documentation on reading and writing lists of data:
// Create a new post reference with an auto-generated id
var newPostRef = postListRef.push();
newPostRef.set({
// ...
});
The new items will have complex-looking keys of the form -KTTHEScy82fpfNSCoYN. Read this article on why those are preferred over array indices and (if you're interested) this article that explains the format of these keys.
Consider a different data model
In general though you might want to consider a different data model. What you're storing is a collection of phone numbers. At first sight, storing those in an array-like list seems fine.
But typically you'll want these behaviors for this contact list:
each phone number can only be present once
you need to find whether a given phone number is already in the list
With your current structure you can only see if a number is already in the list by scanning all items in the list. Whenever that is the case, it is a good moment to consider using a set data structure instead.
In Firebase you'd model a set of phone numbers like this:
"contacts": {
"(800)-888-8858": true
"(800)-888-8868": true
"(800)-888-8888": true
}
While this structure initially looks less efficient, it is actually stored more efficiently than the array list in Firebase. And looking up whether an item exists is now a simple existence check instead of having to scan the array. And with this structure it is impossible to store the same number twice.
Related
So suppose I am building an app that lets users manage trips. When a trip is created , any number of users can be added in it. I want this data to be stored in a single place and then for each user be able to retrieve the trips that that person is included in. My data looks like this:
-trips
-<trip-id>
-title
-budget
-etc
-people
-<person-id>
-name
-uid
-<person-id>
-name
-uid
-<trip-id>
-
-
-
This trip will contain all the trips created by all the users. To show any person their trips, I want to retrieve only the lists that person exists in.
This is what I've tried to do including other similar approaches.
rootReference.child("trips").orderByChild("uid").equalTo(FirebaseAuth.instance.currentUser.uid).addValueEventListener(object:ValueEventListener){
override fun onDataChange(snapshot: DataSnapshot) {
//this should only return the trips that current user exists in.
}
}
I have checked the documentation for searching and filtering on firebase but there is nothing that show filtering based nested keys. One particular example is this. I understand it perfectly. If for example I try to filter my trips based on the main attributes like title, budget, it works, but not when I use an attribute of a nested child.
What other approach can I use to filter based to nested keys or should I structure the data differently? Any help is greatly appreciated.
Firebase Realtime Database queries operate on a flat list of child nodes directly under the path that you query.
So the value you order/filter on has to be at a fixex path under each immediate child node. Since that isn't the case for your uid, you can't query across all trips for the UID of all users of those trips.
You can query across one trip for a UID of a user (and then get back that user), or you can query across all trips for properties of the trip itself, such as its title or budget.
If you want to query across all users on all trips, consider keeping an additional list where you have the UID of the user as the key, and then all their trips under there:
"user_trips": {
"uid1": {
"tripid1": true,
"tripid2": true
},
"uid2": {
"tripid2": true,
"tripid3": true
}
}
Also see:
Firebase Query Double Nested
Firebase query if child of child contains a value
Many to Many relationship in Firebase
Goal: show the list of groups based upon the recent message and when a new message comes, update the chat group list.
Firebase Structure:
Left one structure represents user model: Basically under student key, there is a list of the student then its details along with group ids in which he/she participated.
Right one structure represents the group detail model: Under chat_group key, there is a list of groups with details and also recent message timing.
Solution Tried:
Fetch the group id from the student using the onChildAdded method of firebase DB one by one then fetch its group details using the addListenerForSingleValueEvent method, then store in the list and every time sort it using sort method of array list and then call notifyDataSetChanged method of recyclerview adapter.
Problem with this approach: Too time-consuming and as the number of groups increases processing time also increases.
I think the best solution is to add a new key to the group details model for the students signed up , like that
SignedStudents : "student1 , student2 , student3"
Then you will read data from chat_group directly orderedByChild("recent_message_timeStamp") , then you have to filter it locally to get the groups only related to the specific student to add it to your list like that
String students = model.getStudents();
if (students.contains("student1")) addGroupToYourList();
I have a users collection with uId, name, photo
I have a visits collection with uId, userId, location
I have a recyclerview in which I want to show the location with the user name and photo
Can I use the reference field type? If so, how will Firestore know to link visits.userId == users.uId ?
Maybe I first need to query all the visits and then query the relevant user but 2 things:
It means querying a lot of times.
I didn't understand how to collect the joined collection into the adapter, which is based on one query?
Please advice
Thanks
current code
visitsList = db.collection("visitsList");
Query query = visitsList.whereEqualTo("userId",prefs.getString("id","")).orderBy("visitDate", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<AVisit> options = new FirestoreRecyclerOptions.Builder<AVisit>().setQuery(query, AVisit.class).build();
adapter = new VisitsListAdapter(options, VisitsListActivity.this);
RecyclerView rv = findViewById(R.id.rvVisitsList);
rv.setHasFixedSize(true);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(adapter);
The code is a simple query from the collection, not sure how to get the name and photo from the userId field in that collection.
Can I use the reference field type?
Yes, you can use a reference field.
If so, how will Firestore know to link visits.userId == users.uId ?
Firestore results always comes from a single collection (at the moment). It does not automatically join the document from the users collection when you're reading from the visits collection. You will have to do this yourself.
That indeed means you'll be executing multiple reads, but it's often not nearly as slow as you may think. See Google Firestore - how to get document by multiple ids in one round trip?
Update: To show data from the user profile in a list of visits, there are two main options:
load the additional user document in populateView or with a custom parseSnapshot implementation.
duplicate the relevant user data in the visits collection (which is quite normal in NoSQL databases). Also see Alex' answer here: indexed query with FirestoreRecyclerAdapter.
I develop an android application for students attendance according to their seat numbers. If the student entered his seat number, it will be stored into real time database in fire-base.
Now, what I need to do is to retrieve the empty seat numbers to a list view according to students' information. for example: database has 50 seat numbers entered, but seat number (25) is not in the database then it will be retrieved to the list view.
In general how can I do that with fire-base real time database?
This is how database looks like for one student
You will need to mark seat empty in your db to later only fetch the empty seats. In current situation there is no way to put a query which can retrieve data only for absent students.
Your screenshot is limited so I suggest this structure.
{lectureId}/
seatNumber: {
isEmpty: boolean,
.... your other data
}
Then you will use orderByChild("isEmpty").equalTo(false);
And for querying a set of data from realtime database, have a look at the docs:
https://firebase.google.com/docs/database/web/lists-of-data
you can use a boolean if student is present (present = true).
put this variabble in te object
then you can run a loop on datasnashot for
i < noOfSeats
and show in te ListView with a condition
if (!present){
showInList();
}
Firebase by default orders data from the earliest and I need it to be ordered from the latest.
I am using timestamp to do so and doesn't seem to be working.
private void filldata() {
mDatabase.child("Data").orderByChild("timestamp").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String s) {
System.out.println("snapshot:" + snapshot.toString());
}
}
You no need to use orderByChild() here in your case because firebase itself generate unique key which is based on a timestamp, so you can simply use orderByKey() in your query and you will get your data in latest order.
The unique key is based on a timestamp, so list items will
automatically be ordered chronologically. Because Firebase generates a
unique key for each blog post, no write conflicts will occur if
multiple users add a post at the same time.
You can find more here
I'll suggest to use
mDatabase.child("Data").orderByKey().limitToLast(no_of_items_you_want)
This will give you list of latest data
Also to get value from snapshot use
snapshot.getValue(String.class);
Since orderByChild() only sort data in ascending order, you should store an extra data item in your child node whith the value timestamp*(-1) and then sort (order) on this data item.
Your code is correct. The commonly suggested way to order will be by using a negative timestamp.
However I have noticed previously that firebase does order your results by timestamp, as you currently wish for it to do. When the device receives the results it reorders the results by arrival (suspicion).
To test this, try limit your results by using the .limitToLast(n) function, you will realize that while firebase will return the last 10 (in order of timestamp) results to you, these results will not be ordered by timestamp.
Therefore, the best solution will be to store the firebase results in a list and reorder the list using a sorting tool like a comparator