Firebase querying data by two regions - android

{
"key1" : {
"region": 2,
"text": "This is text"
},
"key2" : {
"region": 8,
"text": "This is text"
}.
"key3" : {
"region": 6,
"text": "This is text"
}
}
I want to get the JSON objects with a region of 2 and 8.

Try this:
DatabaseReference ref=FirebaseDatabase.getInstance().getReference();
Query q=ref.orderByChild("region").startAt(2).endAt(8);
q.addValueEventListener(..){//..}
Assuming the the random keys are direct children to the root node.

If you want to use a range of regions, meaning all region from 2 to 8, in which will be also included all regions like (3, 4, 5, 6 and 7), Peter's answer will work perfectly fine.
But if you want to use only region 2 and 8, I recommend you duplicate data. This is a quite common practice when it comes to Firebase and is called denormalization and for that, I recommend you see this video, Denormalization is normal with the Firebase Database.
When you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
In your case, you should consider augmenting your data structure to allow a reverse lookup like this:
regionTwoAndEight
|
--- "key1": true
|
--- "key2": true

Related

Android Firebase : Update/Add record in Nested Array data

I am stuck in one case of firebase operation in android.
My Requirement :
I have table Named : "Values"
This value table contains following type of data.
{
"menu": [
{
"category": "cocktails",
"product": [
{
"id": "123",
"name": "test1"
},
{
"id": "456",
"name": "test2"
}
]
},
{
"category": "vodka",
"product": [
{
"id": "789",
"name": "test3"
},
{
"id": "901",
"name": "test4"
}
]
}
]
}
Now, I want to update id : 123 with name = "test5" , How to update this nested array object
in firebase database in android?
I tried following but it update/add Entire menu array not perticular product array object.
I just want to update in product array object.
Following are the code that i tried, but it update all menu array.
val listPorduct = ArrayList<HashMap<String, Any>>()
for ((i, product) in productList.withIndex()) {
var productMap = HashMap<String, Any>()
productMap = hashMapOf(
"id" to "123",
"name" to "test5")
listPorduct.add(productMap)
}
val map = hashMapOf<String, Any>()
map.put("category", list[position].category)
map.put("product", listPorduct)
repository.getTabs().update("menu", FieldValue.arrayUnion(map))
if i am try with
repository.getTabs().update("menu/product", FieldValue.arrayUnion(map))
or
repository.getTabs().update("menu.product", FieldValue.arrayUnion(map))
then getting issue / or dot not allowed. i have latest firebase gradle file.
How update particular position of product object?
Please anyone can help me to solve out this?
Image of firebase database.
The solution to this problem can be found in my answer from the following post:
firestore -- Add element in a field in hashmap
There is a slightly smaller difference. However, for your use-case, please note that the document that you gave us as an example contains a property named menu which is of type array. In this array, you have added some objects that contain two properties, a String property named category and an array named product. This array contains in term two other properties, one is the id and the other one is the name, both being of type String.
None of those arrays can be updated using FieldValue.arrayUnion(map). The only option that you have is to get the menu array as a list of HashMap<String, Any>. Once you have this map, you can simply iterate through it and get the desired data.
So to update the value of a property inside a Map, you should first get the map, do the changes and put it back. In the end, after you've done all the necessary changes, write the document back to Firestore.
Edit:
According to your comment:
In my case I have more than 5000 records in a list with a nested array.
It's hard to believe that you can nest 5000 elements into a single document because it might not fit since the maximum size for a document is 1 MiB (1,048,576 bytes). Please see usage and limits. So nesting 5000 objects into an array isn't a solution at all. In this case, the single solution that you have is to use a sub-collection and add each item from the array as a document. In this case, there are no limitations.
As a conclusion, the only highly scalable way to store an unknown large list of items is using documents in a collection. Array type fields do not scale for lists the are constantly growing because the items will eventually exceed that (1 MiB), which will obviously cause problems in the future.

Firestore use contains on multiple fields

I have an upcoming video games app. A game release can come out on multiple platforms. I heard that firestore is much more flexible than firebase real time database on how you can retrieve your data. I'm stuck on how can I check if my game release documents in my release collection contains the user chosen platforms, so the app can show the games coming out on his platforms.
This is what I currently have
platforms is a list of Integer which contains platforms ids
databaseReference.collection(getRegionNode())
.whereEqualTo("m_y", monthFilter)
.whereArrayContains("platforms", platforms)
.orderBy("date", Query.Direction.ASCENDING).get().addOnCompleteListener(listener);
Here's an example of a game release document:
1369: {
"src": "Images/dead.png",
"name": "red dead 2",
"date": 2018-10-26,
"region": worldwide,
"platforms": "[12, 13, 54]"
}
Let's say for example, user wants to only be shown platform 12 and 13 games, I want a query that checks and retrieves all releases documents where 12 and 13 are in their platforms list. Thank you!
Firestore Query's whereArrayContains(String field, Object value):
Creates and returns a new Query with the additional filter that documents must contain the specified field, the value must be an array, and that the array must contain the provided value.
According to your comments, your platforms object that is passed as the second argument to this method is of type array. What you are actually doing, you are searching in the platforms property which is of type array for an array, which is not possible since the platforms array in your database contains numbers:
"platforms": "[12, 13, 54]"
And not arrays. A query like this:
databaseReference.collection(getRegionNode())
.whereEqualTo("m_y", monthFilter)
.whereArrayContains("platforms", 12) //Passed a number as the second argument
.orderBy("date", Query.Direction.ASCENDING).get().addOnCompleteListener(listener);
Will work fine because we are searching within the platforms array for a number. Please also note, if you intend to use this king of query, an index is required. For how to create an index, please see my answer from this post.
Even if you using the above query, you can filter your items using only one whereArrayContains() method call. If you will use more than one, the following error will occur:
Caused by: java.lang.IllegalArgumentException: Invalid Query. Queries only support having a single array-contains filter.
If you need to filter on more than one platform, you'll need to change the logic of structuring your database by creating a property for each individual platform that you have and chain whereEqualTo() method calls. I know it sounds a little weird but this is how Cloud Firestore works.
Your schema should like this:
1369: {
"src": "Images/dead.png",
"name": "red dead 2",
"date": 2018-10-26,
"region": worldwide,
"platformsOne": 12,
"platformsTwo": 13,
"platformsThree": 54
}
To find all the games for platform 12, 13 and 54, you should use a query that looks like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query query = rootRef.
.whereEqualTo("platformsOne", 12)
.whereEqualTo("platformsTwo", 13)
.whereEqualTo("platformsThree", 54);

How the Sugar Record model will be created for given JSON

I have following JSON and I want to create Sugar Record Class for it to store it in a database but I am not able to figure out weather I will need multiple model class or single would be enough please help me with this.
{
"command": "AN",
"tripId": 0,
"tripData": {
"DeviceId": "30050",
"DispatcherId": 1,
"PaxLimit": 50,
"DriverId": 6,
"Trips": [{
"Stops": [{
"Id": 1,
"ScheduleTime": "06:00"
}, {
"Id": 5,
"ScheduleTime": "07:00"
}],
"RouteId": 5,
"ToStopId": 5,
"TripId": 123,
"FromStopId": 1
}]
}
}
It really depends on the object you want to store. Because Sugar doesn't support 1 to many relationships. I see your Trips and Stops attributes (in the JSON) are both arrays in the JSON. It is not possible to save that in the Sugar database. Because you cannot store a List with Sugar.
You can choose for yourself if you would like one single class containing all attributes that are in the JSON object. The other options is to use Nested Classes.
It doesn't matter what option you choose because you can get the data out of the JSON and put it in the right Class. The only thing that is important is that you can't use Lists in a Class.
So it really is your own choice if you use one or multiple classes.

Approach for rendering Dynamic Forms on Android?

I'm hoping someone in the wide world of Android development has come across this problem/use case before, and has a relatively straight-forward approach for implementation.
I have a client Android application, that needs to render a list of fields that the end-user then fills out. Each field is a certain known-type, such as "Text" or "Radio", "MultiSelect", etc. When the user taps on a form, an API call is made to a backend which returns the schema for that form (ie: each field's UUID, title, description, hint text, etc), and the data for that form where some fields are likely already filled out from a prior time. Example of what data I would get over an API call:
{
"submittedBy": 8,
"updatedBy": 8,
"createdBy": 8,
"submittedDateMillis": 1489680600000,
"updatedDateMillis": 1489680600000,
"createdDateMillis": 1489680600000,
"name": "My Form",
"formTemplateId": 3,
"id": 0,
"schema": {
"6051c1e3-b4bf-4e6a-afe3-de2497dbff11": {
"units": "ft.",
"hintText": "Length of measurement",
"required": false,
"description": "Take the length of the measured item to 4 decimal places.",
"title": "Measurement",
"type": "number"
},
"fdf6ff0b-e60d-4591-a3e7-5467cd7bc67e": {
"enum": [
"Foo",
"Bar",
"Baz",
"Bat"
],
"required": true,
"hintText": "",
"description": "This is a description for a multiple choice question",
"title": "Multiple Choice (radio) title",
"type": "radio"
},
"203ef6d8-03fe-48e8-9a45-b18d12721d44": {
"enum": [
"Option 1",
"Option 2",
"Option 3",
"Option 4"
],
"required": true,
"hintText": "",
"description": "This is the description for a multiselect question",
"title": "This is the title for a multiselect question",
"type": "multiselect"
},
"751e9b8f-a59d-4e81-b3da-17ae44daa44e": {
"enum": [
"A dropdown answer",
"This is another option for a dropdown question it's limit is 130 characters"
],
"required": true,
"hintText": "",
"description": "This is the description for a dropdown question",
"title": "This is the title for a dropdown question",
"type": "select"
},
"33e13828-9171-4680-b68b-9838d4d42af8": {
"required": true,
"hintText": "This is the hint text for a text question limit 130 characters",
"description": "This is the description for a text question limit 5000 characters",
"title": "This is the title for a text question limit 130 characters",
"type": "text"
}
},
"fields": {
"6051c1e3-b4bf-4e6a-afe3-de2497dbff11": "5555.5555",
"fdf6ff0b-e60d-4591-a3e7-5467cd7bc67e": "Bar",
"751e9b8f-a59d-4e81-b3da-17ae44daa44e": "A dropdown answer",
"203ef6d8-03fe-48e8-9a45-b18d12721d44": [
"Option 1",
"Option 2",
"Option 4"
],
"33e13828-9171-4680-b68b-9838d4d42af8": "My answer for your text question."
}
}
The API call, say /api/v1/forms/0, returns the above data. In that I have schema which describes the field types, and fields which give me the answers to populate (some of which could be missing). They both have UUIDs which "match up", so I know what field data to put into what form field.
Now I have to render that Form, and allow the user to tap "Submit" and POST/PUT the new data back to the API.
What is an approach for dealing with this? I consider myself a beginner in Android, and from what I've come up with so far, is probably not the best solution (and probably doesn't scale beyond say, 50 questions, as the "render" and "submit" portions of this activity will become slow):
Make the API call, get the data (above example) back.
For every schema type, .inflate() an XML layout that is whatever that .type is (number, text, radio, etc), and construct a Java type (FormElement is what I'm calling it) that represents that schema JSON type. After .inflate(), .setTag(formElement) and "attach" that Java FormElement to it.
Get the widget inside that layout we just inflated, and if we have corresponding data from the fields mapping in the JSON, set the data to whatever that is. If not, leave it blank.
When the user taps "Submit", grab the Parent Form View, and get it's children. Loop through every child and pull out its FormElement via .getTag(). Get the FormElement#getType to find the type of the View, then work backwards and knowing the View we are iterating on, cast it, get it's inner data value, build the resulting JSON data back up, and PUT that to the API.
I might assign every Widget that represents a data entry point (Text, Radio, etc) a unique ID, based on the UUID from the schema (UUID is v1, so one way is to get the timestamp, and hope for the best, since we would be going from 128 bits to 32 bits). Then use this ID later, when I need to pull data out after the user taps Submit.
There looks to be some promising capability in Android's Data Binding Library, but I don't think Android's data binding can handle the "dynamic" nature of laying out this UI, with different Widgets that have different data types (some of these are Pull Down menus).
Is data binding a better approach here?
Can data binding handle both concerns of rendering the UI here, and helping fetch the data from that UI to ultimately compose my API PUT request back to the server?
Resources I've looked at so far, which shine some light on this overall problem:
http://www.mysamplecode.com/2011/10/android-dynamic-layout-using-xml-add.html for adding/inflating UI's based on pre-defined XML.
https://realm.io/news/data-binding-android-boyar-mount/ a really good tech talk by #george-mount that covers some basics of data binding.
.setTag() & .getTag() - What is the main purpose of setTag() getTag() methods of View? which seems to fit my use case, where I need to have the views "know" things about themselves (like where they came from in the JSON response).
Thank you all ahead of time!
You should create a representation of those fields in memory (like a Collection of Field's).
Then you can use RecyclerView to lay them out efficiently.
In RecyclerView, you can have view types (one for each Field type) that knows how to handle a particular field.
Inside the RecyclerView, to bind the views, you can use data binding. There is a demo on github that shows how to effectively use data binding with RecyclerView.
Last but not least, make sure your network operations are completely de-coupled from the UI. UI just reads the collection so each time you do a network request, it updates the collection, that notifies a change and RecyclerView will update itself. (you probably want to make optimistic updates on the collection since network requests may take time but that is a large topic to cover here).

Nesting stars in Firebase Database

I find Firebase Database sample very helpful, but I noticed something which worries me a little bit.
I mean in this example user can give star to the post, something like "Like it" on Facebook. In provided sample they nested stars into post, so we have sample object like this:
"post_id" : {
"author": "username",
"body": "Some content",
"starCount": 1
"stars" : {
"user_id_who_gave_star" : "true"
}
"title": "Some title",
"uid": "author_id"
}
Such solution has many advantages, like e.g. we can check if have already gave star and hide or change icon, or we can one transaction to change "starCount" and add next value to "stars".
But the problem is when we have big application and 1000 users gave star, so everytime when we download post data we download 1000 userIds which may be not best solution.
Question
My question is, what is best approach for such applications and have someone tested how Firebase works in this situation?
firebaser here
When writing the examples for our documentation, we always have to balance the need for having enough context, keeping the example small enough , and following our own best practices.
This is indeed one of the cases where we violate one of our own best practices "not nesting data". As you said: this means that a user downloading a post, gets all the UIDs of users that liked that post.
As the usage of the application scales, that may become a concern. If that is the case for your app, you should model the "users who upvoted a post" as a separate top-level node:
"posts": {
"post_id" : {
"author": "username",
"body": "Some content",
"starCount": 1
"title": "Some title",
"uid": "author_id"
}
},
"upvotes": {
"post_id" : {
"user_id_who_gave_star" : "true"
}
}

Categories

Resources