Adding document İD to firebase document as a field - firebase

I have a model with default values. My app gets the data from user through EditTexts and add them to Firebase Firestore. I hava an addData function (in AddAnalyzeActivity) and savefunction (in AddAnalyzeViewModel) for this operation. I'm getting EditText entries in AddAnalyzeActivity and adding them to my model but on this step ı want to add document id to my model but I can't access the documentIds properly in AddAnalyzeActivity. I can only access them with a forEach method when I try to retrieving the mentioned data with retrieveData function (in PairDetailVM) from Firestore but If I try to add document Ids in retrieveData method it only adds default value of documentId.
What I tried to:
Using #DocumentId annotation in my model.
Setting null default value of documentId in my model.
Getting a list of all documents' ids but can't match them with actual items.
Here is the screenShot for logic:
AnalyzeModel:
data class AnalyzeModel(
var concept: String?="",
var reason: String?="",
var result: String?="",
var rrRatio: Double?=0.0,
var tarih: Timestamp=Timestamp.now(),
var tradingViewUrl: String="",
var id : String="")
addData :
fun addData(view: View) {
val tarih = com.google.firebase.Timestamp.now()
val rr = rrText.text.toString()
var doubleRR = rr.toDoubleOrNull()
if (doubleRR == null) { doubleRR = 0.0 }
val analyzeDTO = AnalyzeModel(
conceptText.text.toString(),
reasonForText.text.toString(),
resultAddingText.text.toString(),
doubleRR,
tarih,
chartImage.text.toString()
)
viewModel.save(analyzeDTO)
val intent = Intent(this, PairDetailActivity::class.java)
startActivity(intent)
finish()
}
save :
fun save(data: AnalyzeModel) {
database.collection(dbCollection!!).document("Specified").collection("Pairs")
.document(chosenPair!!)
.collection("Analysis")
.add(data)
.addOnFailureListener { exception ->
exception.printStackTrace()
Toast.makeText(getApplication(), exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}
retrieveData:
private fun retrieveData() {
val docRef = collectionRef.orderBy("tarih", Query.Direction.DESCENDING)
docRef.addSnapshotListener { value, error ->
try {
if (value != null && !value.isEmpty) {
val allAnalysis= ArrayList<AnalyzeModel>()
val documents = value.documents
documents.forEach {
val analyze = it.toObject(AnalyzeModel::class.java)
if (analyze!=null){
allAnalysis.add(analyze)
}
}
list.value = allAnalysis
} else if (error != null) {
Toast.makeText(Application(), error.localizedMessage, Toast.LENGTH_LONG).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

If you want to save the document ID into the document itself, consider separating the creation of the new DocumentReference from writing to it, by using set instead of add.
fun save(data: AnalyzeModel) {
val newRef = database.collection(dbCollection!!).document("Specified").collection("Pairs")
.document(chosenPair!!)
.collection("Analysis")
.document() // 👈 generates a new reference with a unique ID
data.id = newRef.id // 👈 set the ID into your object
newRef.set(data) // 👈 writes the data to the new reference
.addOnFailureListener { exception ->
exception.printStackTrace()
Toast.makeText(getApplication(), exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}
Also see the second snippet in the documentation on adding a document

Related

How do i make my code in kotlin read data from the firebase firestore

I am trying to make an app that reads data from the firebase firestore and then shows in a screen that same data
The problem the data only appears when shown in log but i want the in text(string)
Can anyone help me understand how to do it
I already tried many videos explaining but none of then work so my last option is really ask in here for help.
Here is the code
#SuppressLint("UnrememberedMutableState")
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val data = mutableStateOf(mapOf<String, Any>())
val job = remember { Job() }
remember {
GlobalScope.launch(Dispatchers.Main) {
val documentSnapshot = collectionReference.document("Computador").get().await()
try {
data.value = documentSnapshot.data ?: mapOf()
}catch (e: Exception){
Log.e("Firestore", "Error retrieving data", e)
}
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}
And here is the database structure:
It seems like you forgot to use remember on your MutableState (and also suppressed the lint warning with (#SuppressLint("UnrememberedMutableState"))):
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
Also, using GlobalScope in Android is not recommended. Consider using a LaunchedEffect instead:
#Composable
fun DB () {
val db = Firebase.firestore
val collectionReference = db.collection("Inventário")
.document("Bloco E")
.collection("Sala E0.05")
val (data, setData) = remember { mutableStateOf(mapOf<String, Any>()) }
LaunchedEffect(collectionReference) {
try {
val documentSnapshot = collectionReference.document("Computador").get().await()
setData(documentSnapshot.data ?: mapOf())
} catch (e: Exception) {
Log.e("Firestore", "Error retrieving data", e)
}
}
Column() {
data.value.forEach { (key, value) ->
Text("$key: $value")}
}
}

Cannot fill a MutableLiveData of type ArrayList, outcome is always null

Im working on a quizgame and i want to store some ids in a MutableLiveData-arraylist. Therfore i made a function to loop all my documents in de database and add each ID to the arraylist. BUT the outcome is always null. I don't see where i go wrong?
I'm working with a MVVM-structure
GameViewModel:
class GameViewModel : ViewModel() {
// database instance
val db = FirebaseFirestore.getInstance()
// the current category
private val _category = MutableLiveData<String>()
val category: LiveData<String>
get() = _category
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
// the current question
private val _question = MutableLiveData<String>()
val question: LiveData<String>
get() = _question
/**
* Set Current Category
*/
fun SetCategory (categoryName: String){
_category.value = categoryName
}
/**
* Get the list of QuestionIds
*/
fun GetListQuestionIds() {
db.collection("questions")
.whereEqualTo("category", "$_category")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
_questionIdsArray.value?.add(document.data["questionid"] as Long)
Log.d("GetSize","${_questionIdsArray.value?.size}")
}
Log.d("GetSize2","${_questionIdsArray.value?.size}")
}
.addOnFailureListener { exception ->
Log.w("errorforloop", "Error getting documents: ", exception)
}
}
/**
* Get a Question
*/
fun GetQuizQuestion() {
Log.d("retro","${_questionIdsArray.value?.size}")
db.collection("questions")
.whereEqualTo("category", "$_category")
.whereEqualTo("questionid", "${_questionIdsArray.value?.get(0)}")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
_question.value = document.data["question"].toString()
}
}
.addOnFailureListener { exception ->
Log.w("err", "Error getting documents: ", exception)
}
}
GAMEFRAGMENT:
class GameFragment : Fragment() {
private lateinit var viewModel: GameViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentGameBinding.inflate(inflater)
// Get the viewModel
viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
binding.lifecycleOwner = this
// Set the viewModel for DataBinding - this allows the bound layout access to all of the data in the VieWModel
binding.gameviewModel = viewModel
//arguments passed
val selectedCategory = arguments?.getString("selectedCategory")!!
//set current category so that the viewModel can use it
viewModel.SetCategory(selectedCategory)
viewModel.GetListQuestionIds()
viewModel.GetQuizQuestion()
return binding.root
}
If someone can enlighten me ...
Your Problem
You're not initializing the array. This is your code:
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>()
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
This declares a MutableLiveData of type ArrayList<Long>, but does not initialize it so its value defaults to null.
In your for loop you conditionally add items:
_questionIdsArray.value?.add(document.data["questionid"] as Long)
But of course value was never initialized so it's null so add is no-op (does nothing).
The Solution
Just ensure you initialize the live data object at some point.
You could do this inline in the declaration:
// the list of questionIds of the selected category
private val _questionIdsArray = MutableLiveData<ArrayList<Long>>(arrayListOf())
val questionIdsArray: LiveData<ArrayList<Long>>
get() = _questionIdsArray
Or during your attempt to populate the list:
.addOnSuccessListener { documents ->
val idsArray = arrayListOf<Long>() // Non-null list to add to
for (document in documents) {
idsArray.add(document.data["questionid"] as Long)
Log.d("GetSize","${idsArray.size}")
}
_questionIdsArray.value = idsArray // Now set live data with a valid list
Log.d("GetSize2","${_questionIdsArray.value?.size}")
}

Firestore pagination with Jetpack Paging 3 - startBefore not loading the correct page

This is my attempt at paginating a collection of Firestore chat messages with Paging 3. It correctly loads the next pages, so the startAfter operator seems to be working as expected. But it's not loading previous pages correctly. Instead, it always loads the very first page again and appends it at the beginning of the list. (I start dropping pages after 100 items so lazy loading works in both directions).
The prevKey seems to be passed correctly. It has the correct value at beginning of the load method right before we build the query.
timeStamp is a Firestore server timestamp annotated with #ServerTimestamp if that matters.
class ChatMessagesPagingSource(
private val messageCollection: CollectionReference
) : PagingSource<ChatMessagesPagingSource.PagingKey, ChatMessage>() {
override suspend fun load(params: LoadParams<PagingKey>): LoadResult<PagingKey, ChatMessage> {
return try {
var query = messageCollection
.orderBy("timeStamp", Query.Direction.DESCENDING)
.limit(params.loadSize.toLong())
val key = params.key
Timber.d("key = $key")
query = when (key) {
is PagingKey.PreviousKey -> query.endBefore(key.endBefore)
is PagingKey.NextKey -> query.startAfter(key.startAfter)
null -> query
}
val querySnapshot = query.get().await()
val chatMessages = querySnapshot.toObjects(ChatMessage::class.java)
val firstDoc = querySnapshot.documents.firstOrNull()
val lastDoc = querySnapshot.documents.lastOrNull()
val prevKey = if (firstDoc != null) PagingKey.PreviousKey(firstDoc) else null
val nextKey = if (lastDoc != null) PagingKey.NextKey(lastDoc) else null
Timber.d("first message: ${chatMessages.firstOrNull()}")
Timber.d("last message: ${chatMessages.lastOrNull()}")
LoadResult.Page(
data = chatMessages,
prevKey = prevKey,
nextKey = nextKey
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
sealed class PagingKey{
data class PreviousKey(val endBefore: DocumentSnapshot) : PagingKey()
data class NextKey(val startAfter: DocumentSnapshot) : PagingKey()
}
}
You need to check for the type of LoadParams, to see if it is refresh, prepend or append.
Since your query always fetches items after in descending order, when it requests a prepend you're probably loading the incorrect items.

I'm trying to write some test with firstore and swiftui, I can create and read, but now i don't know how to list unique data,

You can see I have two fields in this document, "ing01", and "nameReceta" every document in this collection has the same name fields, in the field "ing01" I have "Pimienta" in description, my app it allows more documents whit "Pimienta" in "ing01" and this is fine, but when I want to list it I need list only one "Pimienta", how can I remove the others?
I have this data model
struct ModeloRecetasIng : Identifiable, Hashable, Equatable {
var id: String
var nameReceta: String
var ing01: String
}
and here I get the data, and it works fine.
class ingredientesAdd : ObservableObject {
// #Published var datosNoDupl = [DataNoDuplicates]()
#Published var datas = [ModeloRecetasIng]()
init() {
// Borra el cache
let settings = FirestoreSettings()
settings.isPersistenceEnabled = false
let db = Firestore.firestore()
db.settings = settings
// Borra el cache
db.collection("DespensaIng01").getDocuments { (snap, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
for i in snap!.documents
{
let id = i.documentID
let nameReceta = i.get("nameReceta") as! String
let ing01 = i.get("ing01") as! String
//
self.datas.append(ModeloRecetasIng(id: id, nameReceta: nameReceta, ing01: ing01))
}
// I'm trying to use set but it doesn't work
let uniqueUnordered = Array(Set(self.datas))
self.datas = uniqueUnordered
print(self.datas)
}
}
}
When I get my Print I see this
Pimienta
Pimienta
Zanahoria
Zanahoria
Zanahoria
and I only want this
Pimienta
Zanahoria
Solved.. just using a custom ID in firestore, and no more duplicates and now i need to sum de quantity only

How to deserialize number value in Firestore field and read this inside de while/loop using kotlinx.coroutines

In the firestore I created a field named PararUm, type number (it does not have Int, when I enter it manually) and I put value 1.
The problem is that the return has been PararUm(PararUm=1) and not just 1.
(99-below)
When I resolved this, I would have solved the first part of the project.
Regarding the second, I want to use kotlinx.coroutines to work within a while/loop (which queries the value of the PararUm field) in a synchronous, non-asynchronous way (as firebase requires)
Can I do something like???(999-below):
I threw this topic down, but I was not happy1.
99-below:
model
#IgnoreExtraProperties
data class PararUm(
var PararUm: Int? = 0
)
Activity
var db = FirebaseFirestore.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var pararumRef =
db.collection("smartmodel").document("xxxxxxxxxxxx")
pararumRef.get().addOnSuccessListener { documentSnapshot ->
var PararUm = documentSnapshot.toObject(PararUm::class.java)
Log.i(ContentValues.TAG, "1999 1999 1999" + PararUm)
}
}
999-below:
while (!FCMotorUmA.value) {
var snapshot = pararumRef.get().await()
    
var pararum = snapshot.toObject(PararUM::class.java)
if (pararum.equals(0)) {
// Do something 1
} else if (pararum.equals(1)) {
// Do something 2
}
}

Resources