Read nested data from firebase realtime database in flutter - flutter

If you see the screenshot of my database, the data is stored in a nested way (cartProduct is a key which has a value of an entire Json file with keys: "id", "price" etc.). In my code, I create a map of each record in the "Orders" table in order to retrieve key values of any key that I specify. This is done by specifying the key name in the databaseMapper variable.
I am trying to read the value of each "id" and store it in a list called "testerList". I am able to store each orderNum, totalAmount or any of those key values that I specify in the databaseMapper. However, if I specify ["id"] it does not work.
I did some research and saw that the only way to reference nested items in a map is by using the logic: databaseMapper["cartProduct"]["id"] like I did below, but I keep getting an error (see last screenshot).
Any help would be appreciated!
Future _readItemIDsTest() async {
//Stores each record in the table as a map
var snapshot = await _dbRef.child("Orders").get();
snapshot.children.forEach((childSnapshot) {
var databaseMapper = childSnapshot.value as Map;
testerList.addAll([databaseMapper["cartProduct"]["id"]]);
});
print(testerList);
}

Nvm, I figured it out. The code below solved my issue.
Future _readItemIDsTest() async {
//Stores each record in the table as a map
//Adds the itemName value of each item from the map
var snapshot = await _dbRef.child("Orders").get();
snapshot.children.forEach((childSnapshot) {
databaseMapper = childSnapshot.value as Map;
var cartProductList = databaseMapper["cartProduct"];
(cartProductList as List).forEach((cartProductElement) {
testerList.addAll([cartProductElement["id"]]);
});
});
print(testerList);
}

Related

Adding map in Firestore?

How can I append a new Map type in firestore?
void addUser() async {
final us = _firestore.collection("users").doc(_search.text);
us.update({
"requests": (
{_auth.currentUser?.email: rep}
),
});
}
Am using this method but the requests field in my firestore overwrites the previous one I want it to be appended. Any idea?
The update() method will always overwrite your previous field with the new one, so achieving this with one operation using the update() is not possible, however, you can always get the current field from the document, then update its value, then save it again in the document like this:
void addUser() async {
final us = _firestore.collection("users").doc(_search.text);
final currentDoc = await us.get(); // we get the document snapshot
final docDataWhichWeWillChange = currentDoc.data() as Map<String, dynamic>; // we get the data of that document
docDataWhichWeWillChange{"requests"]![_auth.currentUser?.email] = rep; // we append the new value with it's key
await us.update({
"requests": docDataWhichWeWillChange["requests"],
}); // then we update it again
}
But you should use this after being aware that this method will make two operations in your database, a get() and update().
If you want to record multiple values, an array is an appropriate type. So, you could use the .arrayUnion method to record multiple entries.
final washingtonRef = db.collection("cities").doc("DC");
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
"regions": FieldValue.arrayUnion(["greater_virginia"]),
});

Sembast read only 1 item

How can I read only 1 item from NoSql using Sembast? Every article about reading items are showing me creating a list but I only need just 1 item from databse.
You can access any record using the RecordRef api (and extension): https://pub.dev/documentation/sembast/latest/sembast/SembastRecordRefExtension.html
// Lint warnings will warn you if you try to use different types
var store = intMapStoreFactory.store();
// add a record, autogenerate a key
var key = await store.add(db, {'offline': true});
// Reading a single record falue
var value = await store.record(key).get(db);

Read key value from array using document id (Flutter)

Very new to flutter. I want to retrieve arrays of notifications using a specific document id and display [date, message, type] in my App. Tried to google but can't seem to find any solution.
Try this with this you will get specific document's data.
firebaseFirestore.collection('Customer').doc('paste-your-document-id').get();
you'll get whole data of Notification data then use it accordingly.
Whole Notification data will print.
FirebaseFirestore.instance.collection("Customer").doc(document_id).get().then((value) {
Map<dynamic, dynamic> map = value.data();
for(var element in map.values) {
element.asMap().forEach((index, firebaseValue) {
print(index);
print('${firebaseValue['Date']}');
print('${firebaseValue['Message']}');
print('${firebaseValue['Type']}');
});
}
}
);

Flutter: Parsing Firebase Realtime Database snapshot to list

I tried searching other questions but the only similar question to this had answers in JavaScript instead of Dart/Flutter. I'm trying to get a list from my Firebase Realtime Database into my app as a List<BaseModel>
So far from what I've searched in the net, I think the result of the DataSnapshot is a map that I could parse so I tried it out but got this error: List<dynamic>' is not a subtype of type 'Map<dynamic, dynamic>
My Code:
Future<List<BaseModel>> getList(
{DatabaseReference query, Models modelType}) async {
List<BaseModel> list = new List();
DataSnapshot snap = await query.once();
Map<String, dynamic> json = Map.from(snap.value);
json.forEach((key, value) {
list.add(BaseModel(model: modelType, key: key, snapshot: value));
});
return list;
}
The weird thing is, even if I try to parse a non-list model I also get the same error.
My database structure looks like this:
Update:
BaseModel:
abstract class BaseModel {
factory BaseModel({Models model, String key, Map map}) {
switch (model) {
case Models.MyModel:
return MyMod.fromSnapshot(key: key, map: map);
break;
default:
throw ("Not a valid model.");
}
}
}
MyModel:
MyModel.fromSnapshot({String key, Map map}) {
_id = key;
_title = map['title'];
}
My Firebase query is just the database reference with .child("Root")
I found a solution!
My new code:
Future<List<BaseModel>> getList({DatabaseReference query, Models modelType}) async {
List<BaseModel> list = new List();
DataSnapshot snap = await query.once();
List<dynamic> resultList = snap.value;
for(var i = 0; i < resultList.length; i++) {
Map<dynamic, dynamic> map = Map.from(resultList[i]);
list.add(BaseModel(model: modelType, key: i.toString(), snapshot: map));
}
return list;
}
This should work assuming you parse the values from your model's .fromMap(yourMap) constructor method. Something like _title = yourMap['key'];
I had a similar experience where the snapshot.value sometimes returned a List and sometimes returned a Map. I searched for a long time to get an answer with no luck but I came up with a workaround.
I suspected that the problem was being caused by using a record key with a value of zero so I added 100 to each key before I wrote it to the db and then subtracted it when I had read and was processing the records. The problem went away in that I then always got a Map returned.
I have since seen a reason given for this behaviour and it confirmed that the zero key value was the culprit but unfortunately I didn't save the link. I think it was on one of the Firebase blogs.
I think the 0 record returns a List and the ones with positive values return a Map.
Anyway, try the adding 100 trick and see it that helps. if it helps, upvote me....I don't think you need code to add or delete 100. :-)
Found the article, Firebase is deciding if it should render an array or a map based on the snapshot content: https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html?m=1
UPDATE:
My 'starting at 0' theory was a red herring, sorry.
The key to this behaviour (bits in bold) is in the part of the Firebase blog (link above) that states:
However, to help people that are storing arrays in Firebase, when you
call .val() or use the REST api to read data, if the data looks like
an array, Firebase will render it as an array.
In particular, if all of the keys are integers, and more than half of
the keys between 0 and the maximum key in the object have non-empty
values, then Firebase will render it as an array. This latter part is
important to keep in mind.
// we send this ['a', 'b', 'c', 'd', 'e'] // Firebase stores this {0:
'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
// since the keys are numeric and sequential, // if we query the data,
we get this ['a', 'b', 'c', 'd', 'e']
// however, if we then delete a, b, and d, // they are no longer
mostly sequential, so // we do not get back an array {2: 'c', 4: 'e'}
You can't currently change or prevent this behavior.
I have now tested this by setting up a db node that looks like the below image. I tested what was being returned in snapshot.value by using snapshot.value is List and snapshot.value is Map, both return true or false.
When ONLY nodes 0, 1 and 3 were there, Firebase RTDB was happily returning a List in snapshot.value. When I added nodes 4 and 6, which have inconsistent data, it decided it was time to return a Map. :-)
So, the quick fix is to test the contents of snapshot.value with is List and/or is Map and then process the contents accordingly, otherwise rethink your keys...the fact that they are sequential or close-to-sequential (with gaps) but have the same children structure is the issue.

Firebase: How to retrieve data from two tables using id

I come from an SQL background and recently started using Firebase for building an ionic shopping cart. This is the database schema:
To retrieve a user's cart, i used the following
var user_id="user1"; // Temporary initialised
var refCart = new Firebase("https://testing.firebaseio.com/cart");
var cart=$firebase(fireBaseData.refCart().child(user_id)).$asArray();
This gives the result:
item1 1
item2 2
item3 5
So tried using foreach()
var refMenu = new Firebase("https://testing.firebaseio.com/menu");
refCart.child(user_id).on("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var item_id = childSnapshot.name();
var qty = childSnapshot.val();
//var data= refMenu.child(item_id).val();
// val is not a function
//var data= refMenu.child(item_id).exportval();
// exportval is not a function
//var data = $firebase (refMenu.child(item_id)). $asArray();
// Give me an array of objects , ie attributes - OK! But what to do next ?
//console.log("DATA",data );
console.log("Item",item_id+" "+qty);
});
});
How can i use item_id to retrieve item details.
Is it the correct way of doing data retrieval from multiple tables?
Update:
Using on() function , i managed to get the item attributes.
refMenu.child(item_id).on("value", function(snapshot) {
console.log("Price",snapshot.val().price);
});
But is there any better implementation for the same.
Is there any better ways to retrieve (from the server side) specific attributes for the item.
Like in SQL
SELECT name,price, ... from menu;
NOTE: .on('event', callback) method will call your callback every time the event is fired.
If you need to retrieve data from a reference once, you should use: .once('event', callback)
NOTE2: snapshot.val() will give you a JSON object that you can assign to a variable
I would do it this way:
refCart.child(user_id).on("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var item_id = childSnapshot.name();
var qty = childSnapshot.val();
refMenu.child(item_id).once("value", function(snapshot) {
var item = snapshot.val()
console.log(item.name +' '+ item.price)
});
});
});
Hope it helps ;)