Get Realtime-Update of an Array of DocumentReferences - android

I'm writing a Game, where the users can join a Lobby to play together. Therefore I update the Lobby and the Users, stored in Firestore. The Lobbys in the Lobby-Collection contain an Id, creator, creationDate and an array of all Members (DocumentReferences to User Objects in a Users Collection). The Users contain an Id, name, mail and an active Lobby. Now, when I update the Entries in the Firestore, in there (Firestore) they seem to be correct.. but when I receive a realtimeUpdate (by adding a SnapshotListener) the array of Members seem to be empty.. but I just inserted the Users to the Array and they ARE saved to Firestore..
Maybe nice to know: I convert the Datasets i get from Firestore to local Java-Objects for better handling with UI things
I also have a local HashMap to more or less cache the Objects so i dont have to always load the from Firestore (I know there is already a cache in the Firestore lib.. but i think i need my own)
At the moment I write the changes directly to Firestore and wait for them to come back via RealtimeUpdate to then update my local Objects. I also tried to update my local Objects and then write them to Firestore.. but then I only append my Users to the Members-Array and the array contains mulitple References of the same user..
/**
* Get a Lobby Object from a DocumentSnapshot from Firestrore
* If the Object already exists it will be loaded from the "CacheMap"
*
* #param documentSnapshot DocumentSnapshot with the data from the Firestore
* #param feedback a method to call when the Lobby was retrieved
*/
public static void getLobbyByDocSnap(DocumentSnapshot documentSnapshot, IFeedback feedback) {
final String METHOD = TAG + " #getLobbyByDocSnap";
String lobby_id = documentSnapshot.getString(FIRESTORE_DOCUMENT_ID);
if (allLoadedLobbys.containsKey(lobby_id)) {
Log.d(METHOD, "found object in map for id: " + lobby_id);
feedback.trigger(allLoadedLobbys.get(lobby_id));
return;
}
Log.d(METHOD, "Could not find in Map.. generate through data");
Lobby lobby = new Lobby();
lobby.setId(lobby_id);
lobby.setPrivateLobby(documentSnapshot.getBoolean(FIRESTORE_DOCUMENT_PRIVATELOBBY));
lobby.setCreationDate(documentSnapshot.getDate(FIRESTORE_DOCUMENT_CREATIONDATE));
allLoadedLobbys.put(lobby.getId(), lobby);
//create all Members of the Lobby as User Objects
final List<User> members = new ArrayList<>();
List<DocumentReference> docmems = (List<DocumentReference>) documentSnapshot.get(FIRESTORE_DOCUMENT_MEMBER);
Log.d(METHOD, "get all members of lobby: " + lobby_id);
for (final DocumentReference docmem : docmems) {
/*docmem.collection(FIRESTORE_DOCUMENT_MEMBER).get()
.addOnSuccessListener(queryDocumentSnapshots -> {
Log.d(METHOD, "Found Members for: "+lobby_id+": "+Arrays.toString(queryDocumentSnapshots.getDocuments().toArray()));
//Convert DocumentReference to User-Object
for (DocumentSnapshot document : queryDocumentSnapshots.getDocuments()) {
Log.d(METHOD, "Get User Object from "+UserManager.class.getCanonicalName());
UserManager.getUserByDocSnap(document, o -> members.add((User) o));
}
});*/
UserManager.getUserByRef(docmem, o -> members.add((User) o));
}
lobby.setMember(members);
Log.d(METHOD, "Start getting the Creator of this Lobby: " + lobby_id);
//create an User-Object for the Creator
UserManager.getUserByRef((DocumentReference) documentSnapshot.get(FIRESTORE_DOCUMENT_CREATOR), o -> {
User creator = (User) o;
lobby.setCreator(creator);
Log.d(METHOD, "Got the Creator, now get the artist for: " + lobby_id);
UserManager.getUserByRef((DocumentReference) documentSnapshot.get(FIRESTORE_DOCUMENT_ARTIST), a -> {
User artist = (User) a;
Log.d(METHOD, "Got the Artist. All Infos collected for: " + lobby_id);
//Create The Lobby-Object
lobby.setArtist(artist);
Log.d(METHOD, "Save the Lobby to the CacheMap: " + lobby.toString());
//add it to the given list and trigger the feedback
feedback.trigger(lobby);
});
});
documentSnapshot.getReference().addSnapshotListener((snapshot, e) -> {
if (e != null) {
Log.w(METHOD+"+new", "Listen failed.", e);
return;
}
if (snapshot != null && snapshot.exists()) {
Log.d(METHOD + "*new", "Current data: " + snapshot.getData());
String update_lobby_id = snapshot.getString(FIRESTORE_DOCUMENT_ID);
Lobby update_lobby = allLoadedLobbys.get(update_lobby_id);
update_lobby.setCreationDate(snapshot.getDate(FIRESTORE_DOCUMENT_CREATIONDATE));
update_lobby.setPrivateLobby(snapshot.getBoolean(FIRESTORE_DOCUMENT_PRIVATELOBBY));
UserManager.getUserByRef(snapshot.getDocumentReference(FIRESTORE_DOCUMENT_ARTIST), o -> update_lobby.setArtist((User) o));
UserManager.getUserByRef(snapshot.getDocumentReference(FIRESTORE_DOCUMENT_CREATOR), o -> update_lobby.setCreator((User) o));
List<User> update_member = update_lobby.getMember();
update_member.clear();
List<DocumentReference> update_docmems = (List<DocumentReference>) documentSnapshot.get(FIRESTORE_DOCUMENT_MEMBER);
//update_lobby.setMember(update_member);
Log.d(METHOD+"*new", "get all updated members of lobby: " + update_lobby_id);
Log.d(METHOD+"*new", "members DocRef List: " + update_docmems);
/*for (final DocumentReference update_docmem : update_docmems) {
Log.d(METHOD+"*new", update_docmem.getId());
UserManager.getUserByRef(update_docmem, o -> {
Log.d(METHOD+"*new",((User) o).toString());
update_lobby.addMember((User) o);
});
}*/
getMemberList(update_docmems, new ArrayList<>(), o -> {
List<User> mems = (List<User>) o;
update_lobby.getMember().clear();
update_lobby.getMember().addAll(mems);
});
} else {
Log.d(METHOD+"*new", "Current data: null");
}
});
}
private static void getMemberList(List<DocumentReference> update_docmems, List<User> member, IFeedback feedback){
final String METHOD = TAG + " #getMemberList";
/*if(null == member){
member = new ArrayList<>();
}*/
if(update_docmems.isEmpty()){
feedback.trigger(member);
return;
}
DocumentReference docref = update_docmems.get(0);
UserManager.getUserByRef(docref, o -> {
member.add((User) o);
Log.d(METHOD, o.toString());
update_docmems.remove(0);
getMemberList(update_docmems, member, feedback);
});
}
The Realtime only provides the "normal" Data but not the array of references. When I initialy load the Data from Firestore i get the actual Data of the Firestore (not empty). But I want to get the whole Document, inluding the "normal" Data (id, creationDate, ...) and the whole array of members.
I already burned 1.5 days to solve this and I cant figure out, whats wrong..

Never mind, i got my error.... really stupid one ^^
in the part where i updated my objects, when the firestore changes.. i used the wrong/old DocumentSnapshot. So I used the inital Members-Array not my new updated one :D
should be:
List<DocumentReference> update_docmems = (List<DocumentReference>) snapshot.get(FIRESTORE_DOCUMENT_MEMBER);
instead of:
List<DocumentReference> update_docmems = (List<DocumentReference>) documentSnapshot.get(FIRESTORE_DOCUMENT_MEMBER);
Now i get my updates correctly :D

Related

Insert data to firebase -Android - and check specific properties

I need to insert lesson object to firebase, so I put here the onData change section of code.
First of all I get data snapshot and insert the lessons that I have in firebase, after that I scan the List of Lessons and check:
if the date and time exist in the firebase in any Lesson so I do something else I insert the lesson object to firebase .
The main problem is :
when I insert the details of the lesson and press add, the lesson enter to the firebase twice minimum, and if I try another insertion the program enter to infinite loop .
will be happy for any help !
ArrayList<Lesson> existLesson=new ArrayList<>();
List<String> keys = new ArrayList<>();
int counter=0;
public void getLessons(DataSnapshot dataSnapshot){
//insert the lessons to "existLesson" arrayList
for (DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Lesson lesson = keyNode.getValue(Lesson.class);
existLesson.add(lesson);
Log.i(tag, "data : " + lesson.getSubject());
}//for
}
int flag=1;
#Override
public void addLesson(final String subject, final String topic, final String date, final String time) {
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
When you call you're adding a listener to the data at. This listener will immediately read the data and call your onDataChange, and then continues to listen for updates to the data.
For each update to the data, it calls your onDataChange again. And since you're updating the data inside onDataChange, this ends in an endless loop of setValue->onDataChange->setValue->onDataChange->...
To fix this, you'd typically use addListenerForSingleValueEvent instead, as this only gets the value once and doesn't continue listening for changes.
So something like:
mDatabase.addForListenerValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
})
Note that, since you're updating the data based on its current value, there's a chance that another user may be doing the same operation at almost the same time. If this can lead to conflicting updates in your use-case, consider using a transaction which combines the read and write from your code into a single (repeatable) operation.

Order internal data sent to Firestore

Hi i am working with firebase. When sending a record I get messy data.
Is there a method to order them to my liking? Since I would like to have it in the order of the form.
I leave a fragment of my code, thank you very much for reading.
firebaseAuth.createUserWithEmailAndPassword(correo, password).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
USERUID = firebaseAuth.getCurrentUser().getUid();
DocumentReference documentReference = firebaseFirestore.collection("Clientes").document(USERUID);
Map<String, Object> user = new HashMap<>();
user.put("Nombre y Apellido", nombre);
user.put("Correo", correo);
user.put("Domicilio", domicilio);
user.put("Localidad", localidad);
user.put("Fecha" , FieldValue.serverTimestamp());
documentReference.set(user).addOnSuccessListener(aVoid -> Log.d(TAG, "Cuenta creada, UID de usuario : " + USERUID))
.addOnFailureListener(e -> Log.d(TAG, "Creación de cuenta fallida: " + e.toString()));
startActivity(new Intent(getApplicationContext(), VerificacionDeDatos.class));
finish();
} else {
Light.make(snackbar, "Error al registrarse: " + task.getException().getLocalizedMessage(), R.drawable.ic_error_outline_black_24dp, android.R.color.transparent, R.color.error, Snackbar.LENGTH_SHORT).show();
}
});
Firestore document fields don't have a determined order. What you see in the screenshot is just how the Firebase console chooses to order them. You'll notice they're in alphabetical order. There's no way to change this, but you can do whatever you want with the order in your own app.

How to get random and unique document from Google Cloud Firestore? [duplicate]

I have 1000 documents in a single collection in Cloud Firestore, is it possible to fetch random documents?
Say for example: Students is a collection in Firestore and I have 1000 students in that collection, my requirement is to pick 10 students randomnly on each call.
As per Alex's answer I got hint to get duplicate records from Firebase Firestore Database (Specially for small amount of data)
I got some problems in his question as follow:
It gives all the records same as randomNumber is not updated.
It may have duplicate records in final list even we update randomNumber everytime.
It may have duplicate records which we are already displaying.
I have updated answer as follow:
FirebaseFirestore database = FirebaseFirestore.getInstance();
CollectionReference collection = database.collection(VIDEO_PATH);
collection.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<VideoModel> videoModelList = new ArrayList<>();
for (DocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
VideoModel student = document.toObject(VideoModel.class);
videoModelList.add(student);
}
/* Get Size of Total Items */
int size = videoModelList.size();
/* Random Array List */
ArrayList<VideoModel> randomVideoModels = new ArrayList<>();
/* for-loop: It will loop all the data if you want
* RANDOM + UNIQUE data.
* */
for (int i = 0; i < size; i++) {
// Getting random number (inside loop just because every time we'll generate new number)
int randomNumber = new Random().nextInt(size);
VideoModel model = videoModelList.get(randomNumber);
// Check with current items whether its same or not
// It will helpful when you want to show related items excepting current item
if (!model.getTitle().equals(mTitle)) {
// Check whether current list is contains same item.
// May random number get similar again then its happens
if (!randomVideoModels.contains(model))
randomVideoModels.add(model);
// How many random items you want
// I want 6 items so It will break loop if size will be 6.
if (randomVideoModels.size() == 6) break;
}
}
// Bind adapter
if (randomVideoModels.size() > 0) {
adapter = new RelatedVideoAdapter(VideoPlayerActivity.this, randomVideoModels, VideoPlayerActivity.this);
binding.recyclerView.setAdapter(adapter);
}
} else {
Log.d("TAG", "Error getting documents: ", task.getException());
}
}
});
Hope this logic helps to all who has small amount of data and I don't think It will create any problem for 1000 to 5000 data.
Thank you.
Yes it is and to achieve this, please use the following code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference studentsCollectionReference = rootRef.collection("students");
studentsCollectionReference.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<Student> studentList = new ArrayList<>();
for (DocumentSnapshot document : task.getResult()) {
Student student = document.toObject(Student.class);
studentList.add(student);
}
int studentListSize = studentList.size();
List<Students> randomStudentList = new ArrayList<>();
for(int i = 0; i < studentListSize; i++) {
Student randomStudent = studentList.get(new Random().nextInt(studentListSize));
if(!randomStudentList.contains(randomStudent)) {
randomStudentList.add(randomStudent);
if(randomStudentList.size() == 10) {
break;
}
}
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
This is called the classic solution and you can use it for collections that contain only a few records but if you are afraid of getting huge number of reads then, I'll recommend you this second approach. This also involves a little change in your database by adding a new document that can hold an array with all student ids. So to get those random 10 students, you'll need to make only a get() call, which implies only a single read operation. Once you get that array, you can use the same algorithm and get those 10 random ids. Once you have those random ids, you can get the corresponding documents and add them to a list. In this way you perform only 10 more reads to get the actual random students. In total, there are only 11 document reads.
This practice is called denormalization (duplicating data) and is a common practice when it comes to Firebase. If you're new to NoSQL database, so for a better understanding, I recommend you see this video, Denormalization is normal with the Firebase Database. It's for Firebase realtime database but same principles apply to Cloud Firestore.
But rememebr, in the way you are adding the random products in this new created node, in the same way you need to remove them when there are not needed anymore.
To add a student id to an array simply use:
FieldValue.arrayUnion("yourArrayProperty")
And to remove a student id, please use:
FieldValue.arrayRemove("yourArrayProperty")
To get all 10 random students at once, you can use List<Task<DocumentSnapshot>> and then call Tasks.whenAllSuccess(tasks), as explained in my answer from this post:
Android Firestore convert array of document references to List<Pojo>
I faced a similar problem (I only needed to get one random document every 24 hours or when users refresh the page manually but you can apply this solution on your case as well) and what worked for me was the following:
Technique
Read a small list of documents for the first time, let's say from 1 to 10 documents (10 to 30 or 50 in your case).
Select random document(s) based on a randomly generated number(s) within the range of the list of documents.
Save the last id of the document you selected locally on the client device (maybe in shared preferences like I did).
if you want a new random document(s), you will use the saved document id to start the process again (steps 1 to 3) after the saved document id which will exclude all documents that appeared before.
Repeat the process until there are no more documents after the saved document id then start over again from the beginning assuming this is the first time you run this algorithm (by setting the saved document id to null and start the process again (steps 1 to 4).
Technique Pros and Cons
Pros:
You can determine the jump size each time you get a new random document(s).
No need to modify the original model class of your object.
No need to modify the database that you already have or designed.
No need to add a document in the collection and handle adding random id for each document when adding a new document to the collection like solution mentioned here.
No need to load a big list of documents to just get one document or small-sized list of documents,
Works well if you are using the auto-generated id by firestore (because the documents inside the collection are already slightly randomized)
Works well if you want one random document or a small-sized random list of documents.
Works on all platforms (including iOS, Android, Web).
Cons
Handle saving the id of the document to use in the next request of getting random document(s) (which is better than handling a new field in each document or handling adding ids for each document in the collection to a new document in the main collection)
May get some documents more than one time if the list is not large enough (in my case it wasn't a problem) and I didn't find any solution that is avoiding this case completely.
Implementation (kotlin on android):
var documentId = //get document id from shared preference (will be null if not set before)
getRandomDocument(documentId)
fun getRandomDocument(documentId: String?) {
if (documentId == null) {
val query = FirebaseFirestore.getInstance()
.collection(COLLECTION_NAME)
.limit(getLimitSize())
loadDataWithQuery(query)
} else {
val docRef = FirebaseFirestore.getInstance()
.collection(COLLECTION_NAME).document(documentId)
docRef.get().addOnSuccessListener { documentSnapshot ->
val query = FirebaseFirestore.getInstance()
.collection(COLLECTION_NAME)
.startAfter(documentSnapshot)
.limit(getLimitSize())
loadDataWithQuery(query)
}.addOnFailureListener { e ->
// handle on failure
}
}
}
fun loadDataWithQuery(query: Query) {
query.get().addOnSuccessListener { queryDocumentSnapshots ->
val documents = queryDocumentSnapshots.documents
if (documents.isNotEmpty() && documents[documents.size - 1].exists()) {
//select one document from the loaded list (I selected the last document in the list)
val snapshot = documents[documents.size - 1]
var documentId = snapshot.id
//SAVE the document id in shared preferences here
//handle the random document here
} else {
//handle in case you reach to the end of the list of documents
//so we start over again as this is the first time we get a random document
//by calling getRandomDocument() with a null as a documentId
getRandomDocument(null)
}
}
}
fun getLimitSize(): Long {
val random = Random()
val listLimit = 10
return (random.nextInt(listLimit) + 1).toLong()
}
Based on #ajzbc answer I wrote this for Unity3D and its working for me.
FirebaseFirestore db;
void Start()
{
db = FirebaseFirestore.DefaultInstance;
}
public void GetRandomDocument()
{
Query query1 = db.Collection("Sports").WhereGreaterThanOrEqualTo(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);
Query query2 = db.Collection("Sports").WhereLessThan(FieldPath.DocumentId, db.Collection("Sports").Document().Id).Limit(1);
query1.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask1) =>
{
if(querySnapshotTask1.Result.Count > 0)
{
foreach (DocumentSnapshot documentSnapshot in querySnapshotTask1.Result.Documents)
{
Debug.Log("Random ID: "+documentSnapshot.Id);
}
} else
{
query2.GetSnapshotAsync().ContinueWithOnMainThread((querySnapshotTask2) =>
{
foreach (DocumentSnapshot documentSnapshot in querySnapshotTask2.Result.Documents)
{
Debug.Log("Random ID: " + documentSnapshot.Id);
}
});
}
});
}
A second approach as described by Alex Mamo would look similar to this:
Get the array list with the stored document ids
Get a number of strings (I stored the doc ids as string) from that list
In the code below you get 3 random and unique strings from the array and store it in a list, from where you can access the strings and make a query. I am using this code in a fragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_category_selection, container, false);
btnNavFragCat1 = view.findViewById(R.id.btn_category_1);
btnNavFragCat1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
questionKeyRef.document(tvCat1).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
List<String> questions = (List<String>) document.get("questions"); // This gets the array list from Firestore
List<String> randomList = getRandomElement(questions, 0);
removeDuplicates(randomList);
...
}
}
});
}
});
...
return view;
}
private List<String> getRandomElement(List<String> list, int totalItems) {
int PICK_RANDOM_STRING = 3;
Random rand = new Random();
List<String> newList = new ArrayList<>();
int count = 0;
while (count < PICK_RANDOM_STRING) {
int randomIndex = rand.nextInt(list.size());
String currentValue = list.get(randomIndex);
if (!newList.contains(currentValue)) {
newList.add(currentValue);
count++;
}
}
return newList;
}
private void removeDuplicates(List<String> list) {
try {
Log.e("One", list.get(0));
Log.e("Two", list.get(1));
Log.e("Three", list.get(2));
query1 = list.get(0); // In this vars are the strings stored with them you can then make a normal query in Firestore to get the actual document
query2 = list.get(1);
query3 = list.get(2);
} catch (Exception e) {
e.printStackTrace();
}
}
Here is the array that I get from Firestore:

Listen to new document additions Cloud Firestore

I am making an app that will listen to new document addition to Firestore collection. I have tried look at Firestore documentation, but doesn't work for my specific problem.
Here is my code listen to document updata
FirebaseFirestore.getInstance().collection("users/" + companyID + "/trips").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot querySnapshot, #Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
if (!querySnapshot.isEmpty()){
for (QueryDocumentSnapshot qds : querySnapshot){
tripList.add(qds.getId());
}
showTripList();//update trip list view
}
}
});
Show the list to the ListView
public void showTripList() {
ListView tripListView = findViewById(R.id.tripList);
if (tripList.size() != 0) {
ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, tripList);
tripListView.setAdapter(arrayAdapter);//show trip list on screen
} else {
TextView noTripTxt = findViewById(R.id.noTripTxt);
noTripTxt.setVisibility(View.VISIBLE);//show "no trip has been made yet" text to user
}
}
The logcat shows NullPointerException at this two line
FirebaseFirestore.getInstance().collection("users/" + companyID + "/trips").addSnapshotListener(new EventListener<QuerySnapshot>()
tripList.add(qds.getId());
Here the code that is work for me
FirebaseFirestore.getInstance().collection("users/" + companyID + "/trips")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value, #Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
tripList.clear();//clear the ArrayList, because this method get all the document id instead of
//the one that just created
for (QueryDocumentSnapshot doc : value) {
if (doc.getId() != null){
tripList.add(doc.getId());//store the document IDs to ArrayList
Log.d(TAG, "Retrieved data: " + doc.getId());
}
}
showTripList();//show trip name to listView
}
});
The code you shared seems to try and listen to the trips subcollection of a specific companyId. But the way you build the path to that collection seems suspicious:
collection("users" + companyID + "trips")
The total path should be users/${companyID}/trips, so it's more likely that the code should be:
collection("users/" + companyID + "/trips")
Without the / your code is listening to a top-level collection users${companyID}trips, which doesn't exist. So that would explain why you're not getting any results.
Note that you can easily prevent such string concatenation problems, by using the more explicit API variant:
collection("users").doc(companyID).collection("trips")
While that last variant is a bit longer, it removes the chance of making simple string concatenation mistakes.
With the collection path fixed, your code will be listening for the data in a subcollection. So it will match multiple documents, and get a QuerySnapshot as shown in the documentation on listening to multiple documents in a collection.

Unable to use the result of a ParseQuery

I'm using Parse and I'm doing a query to fetch a table .
As you can see in the code below, the list LOCALparseQuestionList is populated correctly during the for loop inside the findInBackground. Once it's done, the LOCALparseQuestionList is empty (the log prints 0 size and I see the same when using the debugger).
How should I fetch correctly the data and populate my LOCALparseQuestionList?
public List<QuestionStruct> getParseAllQuestions() {
final List<QuestionStruct> LOCALparseQuestionList = new ArrayList<QuestionStruct>();
// Select All Query
ParseQuery<ParseObject> questionQuery = ParseQuery.getQuery("triviaQuestions");
questionQuery.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> allQuestions, ParseException e) {
if (e == null) {
parseQuestionList = allQuestions;
Log.d(TAG, "Retrieved " + allQuestions.size() + " All questions");
for (ParseObject qu : allQuestions) {
QuestionStruct currentQuestion = new QuestionStruct();
currentQuestion.setID(qu.getInt("id"));
currentQuestion.setQuestion(qu.getString("question"));
currentQuestion.setCorrectAnswer(qu.getString("correct"));
currentQuestion.setPossibleAnswer(qu.getString("wrong_1"));
currentQuestion.setPossibleAnswer(qu.getString("wrong_2"));
currentQuestion.setPossibleAnswer(qu.getString("wrong_3"));
currentQuestion.setPossibleAnswer(qu.getString("correct"));
LOCALparseQuestionList.add(currentQuestion);
Log.d(TAG, "Retrieved " + LOCALparseQuestionList.size() + " LOCALparseQuestionList ");
}
} else {
Log.d(TAG, "Error: " + e.getMessage());
}
}
});
Log.d(TAG, "questionList size: " + LOCALparseQuestionList.size());
return LOCALparseQuestionList;
}
Its a the number one misunderstanding about asynchronous functions: the code underneath the find function does not run after the find function. It runs before it.
The last log statement in the function logs, and the return statement returns an empty list, because that list is populated later, after the find is done and the results are returned. Anything you do that depend on LOCALparseQuestionList being populated must be done within the find's callback.

Categories

Resources