Append to Firebase array without key - android

I have an array without keys. How can I append an element to the keyless array?
favorites:
- 0
|
-- name: "bs41"
- 1
|
-- name: "uie"
So my result would be:
favorites:
- 0
|
-- name: "bs41"
- 1
|
-- name: "uie"
- 2
|
-- name: "pts4"

Your best approach here would be a transaction.
But as usual: there are a lot of reasons why Firebase recommends against using arrays.
For example: can the same value be specified twice in favorites? Chances are that each user can only specify each favorite only once. So one of your next questions in that case is going to be: how can I prevent duplicate values in this Firebase array? And the answer to that one is again going to be: with a transaction.
Transactions hurt the performance and scalability of your application; and it also means that your app won't work when the user of offline. Whenever you need a transaction, it pays off to wonder if there is a data model that accomplishes the same use-case that doesn't require a transaction.
For storing favorites that could be as simple as putting the values into the keys of the collection:
favorites:
"bs41": true
"uie": true
"pts4": true
Now you have an automatic guarantee that each favorite value can be present only once. Adding a new item is also incredibly simple: ref.child("favorites").child("newfav").setValue(true).
The data model I just shared is how you model a set in Firebase. In my experience when you have an array/list and are checking if it contains a specific value before adding a new item, you often should be using a set instead.

Related

Iterating over a List with multiple Place AddressComponents - Kotlin

I am receiving a Place Detail (from Google Maps SDK Android) from a specific place.
This Place has different Place Details as a Result.
The output is the following:
Place found: AddressComponents{asList=[AddressComponent{name=58, shortName=58, types=[street_number]}, AddressComponent{name=Schwertstraße, shortName=Schwertstraße, types=[route]}, AddressComponent{name=Sindelfingen, shortName=Sindelfingen, types=[locality, political]}, AddressComponent{name=Böblingen, shortName=BB, types=[administrative_area_level_3, political]}, AddressComponent{name=Stuttgart, shortName=Süd, types=[administrative_area_level_2, political]}, **AddressComponent{name=Baden-Württemberg, shortName=BW, types=[administrative_area_level_1, political]}**, AddressComponent{name=Deutschland, shortName=DE, types=[country, political]}, AddressComponent{name=71065, shortName=71065, types=[postal_code]}]}
I am looking for the specific information of the typ=administrative_area_level_1 and level_2.
But i am not able to find a way of iteration.
With Kotlin i am able to access values hard, but this is not the solution.
The Objects AddressComponent change their posisitions in new requests to Google Place API. Even some places do only provide less information.
System.out.println(place.addressComponents?.asList()?.get(5)?.name)
System.out.println(place.addressComponents?.asList()?.get(5)?.shortName)
Results in:
Baden-Württemberg
BW
So how can i access the PlaceResult without hard-coding the positions, ignoring the order and find the specific information i am looking for?
The functionality that you're looking for is collections filtering. Assuming your list is named place, the following will return the two matching rows:
val desiredTypes = setOf("administrative_area_level_1",
"administrative_area_level_2")
println(place.filter { it.types.any(desiredTypes::contains) } )
// Prints: [name=Stuttgart, shortName=Süd, types=[administrative_area_level_2, political], name=Baden--Württemberg, shortName=BW, types=[administrative_area_level_1, political]]
The filter function returns any matching elements. it is the (default) name of the element under consideration. The any function returns whether the types sub-element for each element contains any of the items in the desiredTypes set.

How to get data from Firestore based on the person who is assigned to with many different ZIP Codes?

Following is the code I use to pull the data out from the firestore. However, as part of the development of the app, my requirement is also changed. Now, what I need is to pull the orders only for the person who is assigned to serve a particular area which can be identified with the ZIP Code which every order will have.
Presently, with my code, I can get the orders based on the status of the order and by the owner of the product who posted it, which is identified with the currently logged-in user. As per my new requirement, every ZIP Code is assigned to someone (a person can be assigned to provide service to many ZIP Codes.) and I need to show users only those orders that he is assigned to provide service at.
I know how to pull the data if only one ZIP is assigned to a service provider. But in this case, I have no idea how to design my database to assign ZIP codes to the service providers and to get the data.
I thought of doing it using .whereIn like I did for the 'order Status', but there are two issues, one, that there can't be two .whereIns and, two, I do not want to hard code it. So I do not know how to do it, even if my thought was right.
I hope my question is clear.
fun getOrderStatusList(fragment: OrdersByStatusFragment) {
mFireStore.collection("orders")
.whereIn(
"order_status",
listOf("Pending", "Order Received", "In Process", "Packed", "Shipped")
)
.whereEqualTo("product_owner_id", getCurrentUserID())
.get()
.addOnSuccessListener { document ->
val list: ArrayList<OrderStatus> = ArrayList()
for (i in document.documents) {
val orderStatus = i.toObject(OrderStatus::class.java)!!
orderStatus.id = i.id
list.add(orderStatus)
}
fragment.successOrderStatusList(list)
}
.addOnFailureListener {
fragment.hideProgressDialog()
}
}
EDIT:
Zip Codes that are assigned to each users are available as in the screenshot of the database. Document names are same as the id of the current user ID which is also available inside the document with the field name 'id'.
I want to get only those 'orders' that match any Zip code which is assigned to the user. Every order contain a field called zipCode.
If it's possible to change the database schema, I would think of something like this:
Firestore-root
|
--- users
| |
| --- $uid
| |
| --- zipCodes: ["zipCodeOne", "zipCodeTwo"]
|
--- orders
|
--- $orderId
|
--- zipCode: "zipCodeOne"
|
--- status: "Pending"
Since a user can provide service to more than one ZIP code, I would add an array of zip codes to each and every user. To get the orders that correspond to a user, I would perform a query for each zip code a user has:
rootRef.collection("orders")
.whereEqualTo("zipCode", "zipCodeOne").get()
rootRef.collection("orders")
.whereEqualTo("zipCode", "zipCodeTwo").get()
Each statement from above will return a Task object, so you can pass all Task objects you have to the whenAllSuccess method, like explained my answer from the following post:
Firestore - Merging two queries locally
Once I got the order, I can filter them on the client according to the status. However, if you need to get all the orders a user has, according to a specific status, you can also add another whereEqualTo() call like this:
rootRef.collection("orders")
.whereEqualTo("zipCode", "zipCodeOne")
.whereEqualTo("status", "Pending")
.get()
rootRef.collection("orders")
.whereEqualTo("zipCode", "zipCodeTwo")
.whereEqualTo("status", "Pending")
.get()
Since you cannot use multiple array queries, you should consider aggregating field values or separating the field types to boolean flags. such as FieldName:true pairs.
.whereEqualTo("Order_Recieved", "true")`
You could store the ZIP code in an array, while silly, this allows you to do an array contains-any
val citiesRef = db.collection("cities")
citiesRef.whereEqualTo("pending", "true")
.whereEqualTo("orderRecieved", "true")
.whereEqualTo("inProgress", "true")
.whereEqualTo("isPacked", "true")
.whereEqualTo("shipped", "true")
Order Received.whereArrayContainsAny("postcode", listOf("05723", "36236"))
Source: https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any

Firestore: Multiple whereArrayContains

I have a collection called "service". And the services have two attributes called, serviceableAt (where the service is available) and accessibleBy (who can access it).
service-1
serviceableAt
0 - Location-1
1 - Location-2
accessibleBy
0 - Seller
1 - Customer
I am trying to fetch all the services who are serviceable at Location-1 and accessible to Seller. So my query was:
fun services(at: String, by: UserRole) = firestore().collection(Refs.SERVICE_REF)
.whereArrayContains(Fields.SERVICEABLE_AT, at)
.whereArrayContains(Fields.ACCESSIBLE_BY, by)
Looks like multiple whereArrayContains are not supported. So what could be the alternate solution for temporarry basis until Firebase team comes up with a solution?
The common alternative would be to store the information as map fields:
servicableAtMap: {
"Location-1": true,
"Location-2": true
},
accessibleByMap: {
"Seller": true,
"Buyer": true
}
With the above you can now use equality conditions to find the matches, and you can have multiple equality filters in a query.
The downside of the above approach is that it will create/require a separate index for each sub-field, so that takes up more storage and contributes towards the maximum number of indexes you can have.

Firebase retrieved documents/collections are empty with no error

My Firebase database contains 2 collections of name "collectionA" and "collectionB", and they both contain different documents. They both have rules to allow authenticated users to read and write (however I don't get permission erros anyway). The problem is when I get a reference to collectionA and B as such:
var docRefA = db.collection("collectionA")
var docRefB = db.collection("collectionB")
Collection A retrieves docs with its respective documents, but collection B returns empty docs. I can still write to collection B succesfully though, so I know the docRef isn't wrong. I'll attach an image of the actual collections in case there is any differences between the collections I'm not aware of:
collection A and B - "users" collection would be collectionA and "store_exercises" would be B. The only difference I see is the documents in B are greyed out and italic, not sure what this could mean?
EDIT 1: This is how I generate/add items to collection A (I can see both write operations work, it's only reading):
val userMap = HashMap<String, String>()
userMap["username"] = username
userMap["email"] = email
db.collection("users").document(auth.currentUser!!.uid).set(userMap)
and collection B:
db.collection("store_exercises").document("whatever").collection("another_collection").document("name")
.set(myObject)
EDIT 2: Image of contents of the two collections:
contents of both collections
Else, how can I debug a query? or test this scenario?
Ok, just figured that one out, if a document doesn't contain fields, and just collections (even if those collections are composed of non empty documents), then it will think its an "empty branch" and not display anything chained in there.
I only wanted 2 collections so I had to create a document to link them so I didn't add any fields, but unless there's some notation or something it seems like adding at least one field is a must for them to be read. All I did was add one field to the documents in collection B, and now they're not empty.

Android widget, how to show the next value from the database on a touch event?

in my application I have a database, that I access through a custom ContentProvider, with the following columns:
id_widget = the appWidgetId of the widget I would like to show the values on
id_item = the id of a value in another table, which I'll get with a SQL JOIN query
name_item = the name of the item, assigned by me through the widget configuration activity that I've created.
An example of the table could be something like this:
id_widget | id_item | name­_item
----------+---------+----------
54 | 1 | avaible
54 | 2 | used
58 | 1 | left
58 | 3 | avaible2
58 | 5 | old
My widgets should show just the first value stored, but when pressed, I'd like them to show the next value and so on until the last one, then they should move to first again.
So for example, the widget which has appWidgetId = 58, should show "left" (and another value that I'll get from another table using an SQL JOIN QUERY. Those values are parsed from a site through a IntentService, and saved in another table). When touched, it should show the value "avaible2", then after another touch "old" and if pressed once again it should move to the first value, "left".
The first idea that I've got was to query the database in order to get all the values, and store them in some Collections inside the AppWidgetProvider, in order to use them in the onUpdate method. That way I wouldn't have to query the database everytime that i touched the widget. That was until i read the doc on AppWidgetProvider and found out how it actually works and the fact that I don't always have the same instance.
I've been searching for two days for another solution, but I still haven't found anything. The only idea that I have got is the following:
- keep track of which row of the table I'm showing (maybe by adding another column to the table)
- query the database for the next row everytime that I touch a widget.
Is it efficient enough? Are there any other ways to solve my problem?
I apologize for my English, but as this is not my first language, this is the best I could do.
Thanks in advance
First off, your English is great! Wouldn't even have known it wasn't your native language if you hadn't mentioned it. :)
As you already pointed out in your question, AppWidgetProviders are stateless: you're not guaranteed (in fact, it is unlikely) that you will get the same instance of your AppWidgetProvider class. They are a special type of BroadcastReceiver, which only exists as long as it needs to in order to process the event it is listening for. See this section, in particular.
The only way to maintain a widget's state/data is to store the needed information persistently, and in Android, that means writing to a file, database, or the app's SharedPreferences (which ends up being a file as well). So you have a couple options with that, but seeing as you already have a database/ContentProvider set up, it would probably make sense to simply add another column, like you mentioned. Ultimately, it needs to be saved somewhere, so even though it's better in general to minimize the number of file/database reads/writes, it is necessary to do so in this case. You could display a ProgressBar on the widget until the query completes, and then display the updated information, especially if you find that the database queries are taking a long time to execute.
Just for completeness's sake... some widgets use a background Service to maintain and/or update a widget's state. But on its own, this will obviously not persist the widget's state/data when the device is turned off, or in the event that the service is stopped for whatever reason. I don't think this would work well for your situation.
All that to say, based on my knowledge of widgets, I agree with the approach you've suggested!
This isn't directly related to your question about handling data, but I found this sample app by Commonsware very helpful in handling widget clicks when I was getting started.

Categories

Resources