I'm trying to run a query in Firebase to get the value of an specific field in the USERS collection and I don't understand why .documents it's an Unresolved Reference. Any ideas?
fun getUserSpecialty() {
val user = FirebaseAuth.getInstance().currentUser!!.uid
val specRef = usersCollectionRef.document(user)
specRef.get().addOnSuccessListener { snapshot ->
for (document in snapshot.documents) { //.documents it's an Unresolved Reference
val data = document.data
val userSpecialtyCode = data!![SPECIALTY_CODE] as String
val loggedUserSpecialty = UserSpecialty(userSpecialtyCode)
userSpecClass.add(loggedUserSpecialty)
this.userSpecTxt?.text = userSpecialtyCode
}
}
}
It's because snapshot is a DocumentSnapshot, and as you can see from the linked API documentation, it doesn't have a method called getDocuments() on it.
When you call get() on a DocumentReference as you are now, you get a single document as a DocumentSnapshot. You do not get a QuerySnapshot like you do with queries that could return multiple documents. You are probably confusing the two.
Related
I alredy searched here in the forum but i didn't find nothing like that. I want to get an Object from Firebase Firestore, but I can't manipulate the object that I am receiving. How can I separate the imageLink from the imageTitle?
The database structure is:
The code I am using is:
firebaseFirestore.collection("image").get()
.addOnSuccessListener { documentSnapshot ->
val imageData = documentSnapshot.documents[0].data?.getValue("imageData")
}
But, when I do that, I receive something like that:
How can I get the imageLink from the imageTitle separately?
You can try casting imageData as a Map as shown below:
db.collection("images").limit(1).get().addOnSuccessListener { querySnapshot ->
val imageData =
querySnapshot.documents[0].data!!.getValue("imageData")?.let { it as Map<*, *> }
val imageLink = imageData?.get("imageLink")
val imageTitle = imageData?.get("imageTitle")
Log.d("MainActivity", "imageLink: $imageLink")
Log.d("MainActivity", "imageTitle: $imageTitle")
}
Checkout the documentation for let for more information.
You are calling get() on a CollectionReference that will fetch all documents from the collection (if you add any). If you only want to fetch 1st document from the collection, you can add limit(1) that'll save any additional charges.
I am writing code to get the newsfeed from the database and to show this feed in UI. This is my Firestore database structure:
users->uniqueUserId |-->UsersProfileInfo--->Profile(document)
|-->FeedNewsFeed |--->unique documents for each newsfeed
|--->unique documents for each newsfeed
|--->unique documents for each newsfeed
to get each news feed from every user in my user collection I have to write nested for loops which takes some extra time and getting data from Firestore also takes some times, so is there any nice way to optimize this problem.
Function for getting newsfeed
suspend fun getAllNewsFeeds(): ArrayList<NewsFeedClass> {
list.clear()
val querySnapshot = collectionRef.get().await()
//Traversing through each document in collection
for (document in querySnapshot) {
val currDocRef = document.reference
//Getting user name
val userName =
currDocRef
.collection(Constants.UserProfileInfo)
.document(Constants.Profile)
.get()
.await()
.toObject(FeedUserName::class.java)?.userName
// adding username in newsFeedClass for displaying on newsfeed
val newsFeedClass = NewsFeedClass()
if (userName != null) {
newsFeedClass.username = userName
}
//getting QuerySnapshot from FeedNewsFeed collection
val newsFeedQuerySnapshot =
currDocRef
.collection(Constants.FeedNewsFeeds).get().await()
//Traversing through each document in
// collecting and respectively adding feed on newsFeedClass
//adding newsFeedClass to list
for (documentOfFeed in newsFeedQuerySnapshot) {
val thisDocRef = documentOfFeed.reference
val feed =
thisDocRef
.get().await().toObject<FeedNewsFeed>()
if (feed != null) {
newsFeedClass.content = feed.newsfeed
list.add(NewsFeedClass(newsFeedClass.username, newsFeedClass.content))
}
}
}
list.shuffle()
return list
}
What you're experiencing in your code is the expected behavior. Why? Because at every iteration of your loop, you are reading data from Firestore using get() and right after that you call await(). This means that all the operations run one after another. It basically means, that at each iteration of your loop, you wait until you get the data from the database. The more iterations you have, the longer it will take. So these operations run sequentially, and not in parallel as you probably might think.
If you need to get the data in parallel you can add the kotlinx-coroutines-play-services library to your project and use the asDeferred extension function that converts a Task into a Deferred object. In the end, you can call the awaitAll() extension function that will wait while all Firestore read operations are loaded in parallel. So in code, it should look like this:
val tasks: MutableList<Deferred<DocumentSnapshot>> = mutableListOf()
for (document in querySnapshot) {
val currDocRef = document.reference
val deferredTask = currDocRef
.collection(Constants.UserProfileInfo)
.document(Constants.Profile)
.get()
.asDeferred()
tasks.add(deferredTask)
}
tasks.awaitAll().forEach { document ->
//Do what you need to do with your documents.
}
I have managed to add data as hashMap into Firestore as follows but I have a hard time reading all documents from Firebase without a model class.
val itemRequest = hashMapOf(
"item" to newItem,
"item_detail" to itemDetails,
"requested_by" to getCurrentUserID(),
"timestamp" to FieldValue.serverTimestamp(),
)
mFireStore.collection("item_request")
.document()
.set(itemRequest)
.addOnSuccessListener {
fragment.successAddingItemRequest()
}
.addOnFailureListener {
}
You can use DocumentSnapshot.data property to convert the DocumentSnapshot to Map<String, Any>.
In your case, you can use:
firestore.collection(collectionId).document(documentId).get()
.addOnSuccessListener { documentSnapshot ->
val data = documentSnapshot.data
val item = data["item"]
val itemDetails = data["item_detail"]
...
}
Here you will have to cast the returned Any map value to its appropriate data type.
But a better way to do all this would have been to create a model class for storing data and using DocumentSnapshot.getObject to get the data back.
The documentation describes that Firestore supports Unicode. You just need to insert already formatted text into Firestore. But when unloading, the following are not taken into account:
Line break;
Unicode characters inserted directly into the text (eg \u000a).
The code is below.
Repository
suspend fun getData(): Response<List<Model>> =
suspendCoroutine { cont ->
val collection =
firestore
.collection(COLLECTION_NAME)
.whereEqualTo(DEFAULT_CONDITION_FIELD, DEFAULT_CONDITION_VALUE)
.orderBy(SORT_FIELD, SORT_DIRECTION)
.get()
collection
.addOnSuccessListener { query ->
val data = arrayListOf<Model>()
query.toObjects(ModelDomain::class.java).forEach { data.add(it.toModel()) }
cont.resume(Response.Success(data))
}
.addOnFailureListener { cont.resume(Response.Error(it)) }
}
ViewModel
private val _data: LiveData<Response<List<Model>>> = loadData()
val data get() = _data
private fun loadData(): LiveData<Response<List<Model>>> =
liveData(Dispatchers.IO) {
emit(Response.Loading)
try {
emit(repository.getData())
} catch (e: Exception) {
emit(Response.Error(e))
}
}
Model
data class ModelDomain(
var description: String = ""
) : KoinComponent {
fun toModel() =
Model(
description = description
)
}
data class Model(
val description: String
)
Part of the code has been omitted.
UPDATE
Just wrote in Notepad ++:
Copied this to Firestore:
Result:
Firestore does not, in any way, modify data that you write to it. If you write something to a document, then read the document, you will get exactly the same data that you put into it.
If you're looking at the document in the Firebase console, you will not see all carriage returns and whitespace. Those are collapsed to save space on screen when rendering large amounts of data. But if you read the data programmatically, it will definitely be exactly as you wrote it.
I am learning to use Firebase Firestore and I have created a list of items that I want to display in my app. When trying to load the list, I don't receive the data but I also don't get any error. I cannot display the data in the Log. What could be happening?
fun getDriverData(): LiveData<MutableList<Driver>> {
val mutableData = MutableLiveData<MutableList<Driver>>()
FirebaseFirestore.getInstance().collection("drivers").get().addOnSuccessListener { result ->
val listData = mutableListOf<Driver>()
Log.i("repo","called")
for (document in result) {
val photoUrl = document.getString("photoUrl")!!
val name = document.getString("name")!!
val team = document.getString("team")!!
Log.i("repo", "${document.id}} => ${document.data}")
val driver = Driver(name,team,photoUrl)
listData.add(driver)
}
mutableData.value = listData
}.addOnFailureListener {
Log.i("repo", "getDriverData: ${it.message}")
}
return mutableData
}
Your collection is actually called "drivers" - WITH the quotation marks. Whatever is generating the documents here is using extra quota when building the name of the collection.
You could read them back by adding those quotes:
FirebaseFirestore.getInstance().collection("\"drivers\"").get()
But you probably want to fix the code that generates the document to not add those quotes.