I am building a desktop application for Windows using Flutter. I am trying to retrieve all of the documents IDs from a collection in FireStore using the package firedart (as the official FireStore package does not support Windows).
This is a simple code to get the documents from a collection:
FirebaseAuth.initialize(apiKey, VolatileStore());
Firestore.initialize(projectId);
CollectionReference stream = Firestore.instance.collection('users');
final data = await stream.get();
print(data);
The output of this print will be a list of arrays where each one has the path to the document and the values sorted inside the document.
for example:
[/users/1dfU7emalRX89z5zlfX0AQLqehq1 {url: '', name: '', height:'' , weight: '',}, /users/JF6PMb2q9Igsb56jgPRs1DGzJ0d2{url: '', name: '', height:'' , weight: '',}]
How can I print the Ids only to a list?
you can get id from this collection by convert value to string and split this string to get it
'''
'''
/// to save return id only
List<String> ids = [];
/// first store return data in List<dynamic>
List<dynamic> list = [
"/users/1dfU7emalRX89z5zlfX0AQLqehq1 {'url': '', 'name': '', 'height':'' , 'weight': '',}",
"/users/JF6PMb2q9Igsb56jgPRs1DGzJ0d2{url: '', 'name': '', 'height':'' , 'weight': '',}"
];
/// forEach element in list convert it to string
list.forEach((element) {
/// it will return part of string as '/users/1dfU7emalRX89z5zlfX0AQLqehq1 '
final String firstPartString = element.toString().split('{').first;
/// split new string by / and it the last item (it is id)
final String id = firstPartString.split('/').last;
/// adding id to ids list (trim to remove any prefix of suffix space)
ids.add(id.trim());
});
print(ids);
'''
As I found from linked package's docs, API of the library you are using is similar to the official one.
This answer might help - you can get document ID when you have a reference to the document
Related
I'm making a product display app.
I'm going to create a "Wish List" widget that brings up the product that the user picked as 'wish item'.
I structured it as shown in picture 1.
And to create a widget,
I get all the documents of the collection('wish') of doc('User A').
And with their String values(product name) of the doc,
Get the product data from the collection ('Product') using Query.
The product collection is shown in the picture below.
Is there a more efficient way?
I thought it might be easier to change the data structure.
However, even if I create a new Collection('Wish'), at the same level as Collection('Product'), and put the product's name and user's e-mail in it,
I need to focus on the 'Product' collection with the 'Product name'.
Because I have to use the price, brand, name of product, in Collection('Product').
Is there any other efficient way I'm missing? Thank you!
Instead of storing the product name in string you can use reference type to store direct reference of the document inside other document.
Example code
DocumentReference ref = db.collection('products').doc('product-document-id');
Map<String,dynamic> data = {
'name' : 'Product A',
'product_ref' : ref, // Product document Reference
};
db.collection("users")
.doc("user-id")
.collection("wish")
.doc("your_product_name")
.set(data);
This will add the wish product with dynamic id.
Now you can directly read the document using the product_ref. You can use something like this
final docRef = db.collection("users")
.doc("user-id")
.collection("wish").doc("your_product_name");
final docSnapshot = await docRef.get();
if (docSnapshot.exists) {
final data = doc.data() as Map<String, dynamic>;
var productRef = data?['product_ref'];
productRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
// you will get your product here
}
});
}
I'm making an app using Flutter, with Cloud Firestore for the backend. I have a stream which retrieves a list of user documents for all users and want to filter the users whose favorite food is "pasta". I don't want to load the other documents. Here is my stream, and the function which maps it to my user model.
final CollectionReference usersCollection =
FirebaseFirestore.instance.collection('Users');``
List<MyAppUser> _userListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((DocumentSnapshot doc) {
return MyAppUser(
uid: doc.id ?? '',
name: (doc['name']).toString() ?? '',
email: (doc['email']).toString() ?? '',
favorite_food: (doc['favorite food']).toString() ?? '',
);
}).toList();
}
Stream<List<MyAppUser>> get users {
return usersCollection.snapshots().map(_userListFromSnapshot);
}
Here is my user model if needed:
class MyAppUser{
final String uid;
final String name;
final String email;
final String favorite_food;
MyAppUser({
this.name,
this.email,
this.uid,
this.favorite_food,
});
}
Should I use a where function after mapping or before?
If I filter before mapping, I will have to do a where on the original stream like
usersCollection.where('favorite food', isEqualTo: 'pasta')
If I filter after mapping, I can get type safety:
I listen to the stream with Provider: final users = Provider.of<List<MyAppUser>>(context);
Then query like this:
users.where((user) => user.favorite_food == 'pasta');
I would prefer to use typesafety, but, will I be billed for reading only the filtered documents or all documents?
I got this answer from Aurimas Deimantas, after commenting on their article on medium.com. Below, I have adapted their answer to suit this question.
Firestore bills you based on how many document reads you have.
It will be better to filter before mapping, with
usersCollection.where('favorite food', isEqualTo: 'pasta')
because this will only read the documents where favorite food is pasta.
If you filter after mapping, like this:
users.where((user) => user.favorite_food == 'pasta');
then all user documents will be read, and after that, filtered. So Firestore will bill you for all the document reads instead of only those whose favorite food is pasta.
This is why it saves money to filter on the userscollection directly, before mapping it to your model.
If you want to map the stream to your model, you can do it after the where filter, by adding the .map(...) function after the .where(...) function, and this will map (& read) only the documents that pass the where filter, saving money.
You can use where clause just after collection calling like
... Collection('Users').where(field, conditions)
With this, you don't have filter list using collection
I have a list of strings pulled from database as a string
[name 1, name2, name3, ...]
i am trying to convert it to this:
'name 1', 'name2', 'name3', '...'
so far no success.
method with which i am getting data. The method is just fine since I am using it beforehand for other part of code.
Future<List<String>> getNames() async {
var url = _api + "get_names.php?key=" + _key;
http.Response response = await http.get(url);
var resp = jsonDecode(response.body);
return resp.map<String>((m) => m['names'] as String).toList();
}
list is pulled from database as a String.
Basicaly I am using a part of formbuilder code which uses initialValue as dynamic.
so if I set initialValue: [widget.names] and the widget.names contains 'name1','name2' it is ok
if it is a list of string but It needs to be ofcourse single or double quoted and seperated with comma.
Thank you
void main() {
final input = '[name 1, name2, name3, ...]';
final removedBrackets = input.substring(1, input.length - 1);
final parts = removedBrackets.split(', ');
var joined = parts.map((part) => "'$part'").join(', ');
print(joined);
}
Using: https://api.flutter.dev/flutter/dart-core/String/split.html
Prints:
'name 1', 'name2', 'name3', '...'
That said... maybe you should find a better way to get your data. Maybe as Json or something, so you don't have to reinvent serialization. What happens for example if "name 1" has a comma in it? Or let's say it's d'Artagnan?
I want to store documentID in my cloud_firestore_database using flutter. I have made a authentication for every User ,so that the data of every User can store individually.Data storing in database in the following way(Student entry => auth(UserId) => vault =>documentID(xj23yvbfvbnbjgkb) => User details)
// collection reference
CollectionReference vaultCollection = Firestore.instance.collection('student entry').document(uid).collection('vault');
vaultCollection.document().setData({
"Entry-time": date,
'image': url,
'fname': name,
'year': year,
'purpose': visiting,
'vehicleno': vehicleno,
'contact': contact,
'Exit-time': "unknown",
'docId'://How can i add documentID ?
});
Use the DocumentReference returned from document(). It contains the randomly generated id before the document is actually added.
DocumentReference doc = vaultCollection.document();
doc.setData({
...
'docId': doc.documentID
});
So in my flutter app, when it's open, i get the type of the user from my RestApi (without account, with account type1, account type2 account type3), and each one of them has a special home_page (same appBar but different bodies with some similarities).
How can i do that ?
Do i have to create a specific route for each one ?
Yes the best practice is to create a route for each user, otherwise you will end up with 4 if blocks in the same file and things will get messy fast
You can create a generic widget how should a home_page should look, then create a model what information is different between users, create another file with all the data from where you get information that is related to the previous model.
Example:
Model:
class Model {
final String id;
final String name;
final String surname;
Model({this.id, this.name, this.surname});
}
Data:
const User_Data = [
Model(
id: 'user1',
name: 'Name1',
surname: 'Surname1',
),
Model(
id: 'user2',
name: 'Name1',
surname: 'Surname1',
),
];
In the data you can fetch the user by id by using User_Data.firstWhere() to get user id and after get all data for that user id.
final userId = ModalRoute.of(context).settings.arguments as String;
final selectedUser =
User_Data.firstWhere((user) => userId == user.id);