I have code like this for QuerySnapshot to get documents in collection that works good.
List<DaftarHazard> _userHazardFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((hazards) {
return DaftarHazard(
waktuHazard: hazards.data['waktuHazard'],
judulHazard: hazards.data['judulHazard'],
isiHazard: hazards.data['isiHazard'],
fotoHazard: hazards.data['fotoHazard'],
namaPelapor: hazards.data['namaPelapor'],
lokasiHazard: hazards.data['lokasiHazard'],
statusHazard: hazards.data['statusHazard']);
}).toList();
}
Stream<List<DaftarHazard>> get userHazardAsSuper {
return userHazards.snapshots().map(_userHazardFromSnapshot);
}
But then I need to Map DocumentSnapshot to class properties.
I don't know how to get this done.
How can I do this using the same method that I use for the QuerySnapshot?
The code above I get from collections.
Code below is the one I use to get fields in a document
Stream<List<UserDetails>> get userDetails {
return userData.document(uid).snapshots().map(_userDetailsFromSnapshot);
}
and using this to map to the properties
List<UserDetails> _userDetailsFromSnapshot(DocumentSnapshot snapshot) {
return snapshot.data.map((item) {
return UserDetails(
name: details.['name'],
email: details.data['email'],
);
}).toList();
}
But code above does not work.
Found the solutions here:
Net Ninja
You to extract the data from your DocumentSnapshot.
List<DaftarHazard> _userHazardFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((hazards) {
return DaftarHazard(
waktuHazard: hazards.data['waktuHazard'] != null ? WaktuHazard.fromData(hazards.data['waktuHazard'].data) : null,
judulHazard: hazards.data['judulHazard'],
isiHazard: hazards.data['isiHazard'],
fotoHazard: hazards.data['fotoHazard'],
namaPelapor: hazards.data['namaPelapor'],
lokasiHazard: hazards.data['lokasiHazard'],
statusHazard: hazards.data['statusHazard']);
}).toList();
}
And replace each property for its own object instead of DocumentSnapshots.
I'm not sure of the structure that you want, but basically, you just need to access the data property of your DocumentSnapshot and parse it accordingly.
Found the solution for getting field and value from a specific document in firestore here
[Net Ninja][1]
Related
I am working on an app and I'd like to display the events that are stored in MongoDB using my GraphQL end points. This is how I do it:
List appointments = Query(
options: QueryOptions(document: gql(_getEvents)),
builder: (result, {fetchMore, refetch}) {
if (result.isLoading) {
return const CircularProgressIndicator();
} else {
result.data!["events"];
}
throw const Text('Something happened');
}) as List;
return _DataSource(appointments);
}
However, I get an error saying that 'The type 'Query is not a subtype of type 'List' in type cast'. Apparently flutter doesn't like that I cast Query as List. The question is 'Is there a way to make the cast work'? Or is there any other applicable workaround?
Thank you in advance
_getLatestCompletedWorkout() async {
try {
QuerySnapshot workouts;
workouts = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('workouts')
.get();
for (var workout in workouts.docs) {
print('WORKOUT = ');
print(workout);
}
.....
What I really need is to get the last document saved; but before that I am just trying to fetch the "workouts" collection; the workouts.docs list always has 0 items. There are 2 items in the DB. What is wrong with this code? Also how to get the last saved item?
As mentioned by Frank :
You can refer Alex answer here :
The simplest way to achieve this is to add a date
property to each object in your collection, then simply query it
according to this new property descending and call limit(1) function.
This is the required query:
this.historyRef = afs.collection<History>('history', ref => ref.orderBy('date', 'desc').limit(1));
this.history = this.historyRef.snapshotChanges().map(actions => {
return actions.map(a => {
const data = a.payload.doc.data() as Hisotory;
const docId = a.payload.doc.id;
return { docId, ...data };
});
});
This has been found since cloud_firestore updates that prevent app that not regiter in App Check to take updates seriously "but it store it with lighter id color" which Protect your Cloud Firestore resources from abuse, such as billing fraud or phishing
Kindly check and ensure your app is registered inside App Check in Firebase console
QuerySnapshot querySnapshot = await _collectionRef
.limit(1)
.orderBy('date')
.getDocuments();
var list = querySnapshot.documents;
querySnapshot.documents.map((document) {
print('Document : $document'); // not printing anything.
});
if(list.length > 0) {
print('ID : ${list[0].documentID}'); // not working
}
This is the exact code i tried...
What is the mistake in the code?
According to the API documentation, DocumentSnapshot has a documentId property, so I would expect this to work:
querySnapshot.documents[0].documentID
product.id
documentID is deprecated and shouldn't be used. Deprecated in favor of .id.
Try replacing the use of the deprecated member with the replacement.
For DocumentSnapshot,
document.documentID
Also, use backticks when stringinterpolating.
print('ID : ${list[0].documentID}'); // not working
print(`ID : ${list[0].documentID}`); // working
For DocumentSnapshot,
document.id
I am trying to implement a feature in my blog app where users can make the post "followers-only". If it is followers-only then only followers can see the post and if not then everyone can see it. At the moment I made a solution but I don't if it is efficient way like doing this in security rules of my database. I have no idea writing a function in database rules.
I tried to implement this feature by addig a rule in my source code itself with an if-else statement.
Future _checkFollowing() async {
List<DocumentSnapshot> _checkFollow = [];
Query q = Firestore.instance
.collection('user')
.document(ownerId)
.collection('followers')
.where('profileId', isEqualTo: uid);
QuerySnapshot querySnapshot = await q.getDocuments();
_checkFollow = querySnapshot.documents;
if (_checkFollow.length == 1) {
setState(() {
isFollowing = true;
});
} else if (_checkFollow.length == 0) {
setState(() {
isFollowing = false;
});
}
}
if (followersOnlyPost) {
if (isFollowing) {
return buildPost();
} else if (!isFollowing) {
return null;
}
} else if (!followersOnlyPost) {
return buildPost();
}
I have a followers subcollection to every user in database.
A post collection in which in each post there is a followers-only boolean field.
This is how I craete a post document
Firestore.instance.runTransaction((transaction) {
var reference = Firestore.instance.collection('all_posts');
reference.add({
'timestamp': DateTime.now(),
'likes': {},
'followersOnly': false,
'bookmarks': {},
'likesCount': 0,
'ownerId': uid,
}).then((DocumentReference doc) {
String docId = doc.documentID;
reference.document(docId).updateData({"postId": docId});});
My code does what I wanted but for everypost the function _checkFollowing is called and as a result it counts as 1 read in evry post.
As there will be many post in the feed, it will result in lots of read count.
Also I want to implement block feature, but I haven't worked on that because I have no I idea how to do that.
What I would do is check who the user is following when the app is loaded (if there is an internet connection) and store it in a sqlite database. Then when it checks if the post is "Followers Only" you can check from the sqlite database locally, without having to call the database. This method will mean you will only get one database call every time the app is opened.
How to extract a pair of values out of a JSON using dart
This is the function I have so far:
This is the JSON:
{
"displayNames": [
"John Doe",
"ChloƩ Kamgang",
"Lady Testing of Tarth"
],
"users": [
{
"uid": "1tcPRqNZ7wexX2DWa11V9Ay1zES2",
"displayName": "John Doe"
},
{
"uid": "aZg7Emp8H9W42irnM73NBdTe6YM2",
"displayName": "ChloƩ Kamgang",
"photoURL": "https://firebasestorage.googleapis.com/v0/b/atalaku-fcf9e.appspot.com/o/Profilescaled_1e189635-0bc0-4791-ba20-6aded7ad3e8f7210544.jpg621jpg?alt=media&token=f5cffac3-a20c-4a83-8241-4fab16a9bd66"
},
{
"uid": "hXzyTuDE8eafnSxITmz7ZdMQDnw1",
"displayName": "Lady Testing of Tarth",
"photoURL": "https://firebasestorage.googleapis.com/v0/b/atalaku-fcf9e.appspot.com/o/Profilescaled_image_picker8162975920.jpg645jpg?alt=media&token=269007d1-09ee-4fe4-b7ad-72aa7cea756a"
}
]
}
And this is the function:
Future<List<Map<String, dynamic>>> getProfilesBySuggestion(
String sSuggestion) async {
try {
List<Map<String, dynamic>> listToReturn = [];
if (sSuggestion == null ||
sSuggestion.isEmpty ||
!sSuggestion.startsWith('#')) return listToReturn;
//Getting all the names
Map<String, dynamic> listUsers = await getAllUsers();
if (listUsers == null) return listToReturn;
//Returning only the names corresponding
List<dynamic> listNames = listUsers['displayNames'];
for (String name in listNames)
if (name
.toLowerCase()
.startsWith(sSuggestion.substring(1).toLowerCase())) {
List<Map<String, dynamic>> listOfusers = listUsers['users'] as List<Map<String, dynamic>>;
Map<String, dynamic> rightOne = listOfusers.firstWhere((user) => user['displayName'] == name);
String sPhotoURL = rightOne['photoURL'];
print('** name = $name, photoURL = $sPhotoURL');
listToReturn.add({'name': name, 'photoURL': sPhotoURL});
}
return listToReturn;
} catch (error) {
print('*** Error During getProfilesBySuggestion: ${error.toString()}');
return [
{'errorMessage': error.toString()}
];
}
}
What I want is a list of pair (name, photoURL). I am using the flutter_typeahead plugin, I want to display a list of names and their respective avatars. As you can see, I am using Firebase. Please let me know if there is a better option as well, as this will get pretty heavy with scale. Thank you!
A good starting point when working with data that's not organised the way you want it, is to re-organize it. I'd merge the two lists adding the display name to each user. But, of course, you see immediately that that's not necessary as each user already contains their display name. (The displayNames branch of your json is unnecessary for your purposes.)
So, you can just work with the users branch. Note that what you are doing is extracting the members of the users list and subtly changing the tag name (from displayName to name - leaving photoURL the same). Is the tag name change necessary? If not you could basically achieve what you want with a single where.
That said, this should achieve what you want (including the tag name change):
Future<List<Map<String, dynamic>>> getProfilesBySuggestion(
String sSuggestion) async {
try {
if (sSuggestion == null ||
sSuggestion.isEmpty ||
!sSuggestion.startsWith('#')) return [];
//Getting all the names
Map<String, dynamic> allUsers = await getAllUsers();
if (allUsers == null) return [];
//Returning only the names corresponding
List<dynamic> users = allUsers['users'];
var suffix = sSuggestion.substring(1).toLowerCase();
return users
.where((user) => user['displayName'].toLowerCase().startsWith(suffix))
.map<Map<String, dynamic>>((user) => {
'name': user['displayName'],
'photoURL': user['photoURL'],
})
.toList();
} catch (error) {
print('*** Error During getProfilesBySuggestion: ${error.toString()}');
return [
{'errorMessage': error.toString()}
];
}
}
A good starting point when working with data that's not organised the way you want it, is to re-organize it.
And if that's not an option, you can still mitigate the impact. It's always easier to work with plain old dart classes than json, so I'd encourage you to map the json to classes.
I usually use package:built_value for that. There is a tool that helps map json:
https://charafau.github.io/json2builtvalue/ Careful, it's not 100%. Eg. it will not include photoUrl field if you just copy-paste your json.
Basically, I'd take the json.users, and map it to objects. Make sure to make photoUrl optional.
Once that's done, you can create any lookup table you want. You could just have a List<User> to iterate over names and photoUrls, or you could create a Map<String, User> to look up users by their name.
Tip: class Map has got a number of constructors that you can work with.