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
}
});
}
Related
In my firestore, i have a creators collection created and i am trying to create an edit profile collection. i want to get the value of a field in the creators collection and add it to the edit profile collection. After some research, i found out a way to go about doing this but when i want to set the value to the field, i get the error above. I would really appreciate some help.
here is the code
editProfile()async{
CollectionReference creatorCollection = FirebaseFirestore.instance.collection('Creators');
CollectionReference editProfileCollection = FirebaseFirestore.instance.collection('EditProfile');
String profileImageUrl =await uploadProfilePictureToStorage(_image);
String profilePicUrl =await uploadProfilePictureToStorage(_profilePic);
creatorCollection.where("fullName", isEqualTo: widget.enterName).get().then((querySnapshot) {
querySnapshot.docs.forEach((result) {
final DocumentSnapshot creatorDoc = result;
editProfileCollection.doc(FirebaseAuth.instance.currentUser!.uid).set({
'FullName': creatorDoc.data()?["fullName"],
'EditedFullName': _name,
'location': _location,
'links': _linked,
'contact':_contact,
'dob':_dob,
'uid':FirebaseAuth.instance.currentUser!.uid,
'likes':[],
'Headers': profileImageUrl,
'ProfilePic':profilePicUrl
}).whenComplete((){
Navigator.pop(context);
});
});
});
what i am trying to get from the creators collection is the fullname. If there is a better way to go about getting the value, that would also be appreciated. Here is the collection by the way
You can use .get or use as Map? like 'FullName': (creatorDoc.data() as Map?)?["fullName"],
'FullName': creatorDoc.get("fullName"),
I am building an Appointment Booking application want to retrieve all my documents from firestore at once on a button click. I used this:
Future<void> userAppointmentHistory() async {
String collectionName =
FirebaseAuth.instance.currentUser?.displayName as String;
String doc_id = "YyWqd9VlB1IdmYoIIFTq";
await FirebaseFirestore.instance
.collection(collectionName)
.doc(doc_id)
.snapshots()
.listen(
(event) {
print(
event.get("selectedDate"),
);
},
);
}
From the above code, I am getting only the specified document id details. So please help me modify the above code so that I get all the document details as I want to display these details on cards as my booked appointment history.
Here you are passing the doc id String doc_id = "YyWqd9VlB1IdmYoIIFTq";
You don't want to pass that if you want the full documents.
just pass the collection reference.
Follow the below code
fetchData() {
CollectionReference collectionReference =
FirebaseFirestore.instance.collection(collectionName);
collectionReference.snapshots().listen((snapshot) {
setState(() {
document = snapshot.docs;
});
print(document.toString);
});
}
Rudransh Singh Mahra,
It's as easy as putting text widget in our application.
Solution
You did it in right way but by mistake you have passed a specific id of documents in doc() as doc('YyWqd9VlB1IdmYoIIFTq'). you just need to remove that id from there and you may get your desired output.
What actually happens :
In your query you pass specific docId. So that it will returns that specified id document from your collection. To get all the documents from that collection you just need to do as follows,
Future<void> userAppointmentHistory() async {
String collectionName =
FirebaseAuth.instance.currentUser?.displayName as String;
// String doc_id = "YyWqd9VlB1IdmYoIIFTq";
await FirebaseFirestore.instance.collection(collectionName).doc().get()
}
And you will get your desired output if collectionName is valid and exist in firestorm database.
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 Firestore database with the following structure:
usersCollection => user1 => productsHeldCollection => product1, product2, ...
I can set up a Stream for the userCollection, but am struggling to set one up for the productsHeldCollection which is a subcollection for each user. This is what I have tried.
First I created a reference for the subcollection:
final CollectionReference productsHeldCollection = Firestore.instance.collection('users').document().collection('productsHeld');
But I am not sure what to pass into the document() as I want it to be whatever the current user id is.
I then did the following:
List<ProductHeld> _productsHeldListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return ProductHeld(
productName: doc.data['Product Name'],
purchaseDate: doc.data['Purchase Date'],
expiryDate: doc.data['Expiry Date'], // these are fields within the subcollection
);
}).toList();
}
// Stream of products
Stream<List<ProductHeld>> get products {
return productsHeldCollection.snapshots().map(_productsHeldListFromSnapshot);
}
The names of documents within a collection are unique. You can provide your own keys, such as user IDs, or you can let Cloud Firestore create random IDs for you automatically.
The parameter you pass into the document() is the name of the document.
Google public documentation provides more details on how to design your hierarchical data structures to work in Cloud Firestore.
I'm creating a simple job board site.
You have a JobSeeker, a JobListing and a JobApplication
Both the JobSeeker and the JobListing should have a collection of JobApplications.
When a JobSeeker applies for a job, I want to create a JobApplication document, and add it to both the JobSeeker's collection, and the JobListing's collection.
But that should be a reference to a single document. (ie. if you update it in one place, it should update in the other).
How do I achieve this?
I see according to this answer:
Cloud Firestore multiples document with the same reference
I can add a Reference as a datatype in Firestore - but I'm not exactly sure which method to use to add it.
ie. the collection.add method accepts DocumentData, but I can't see how to set that as a reference?
Can you tell me what syntax to use to:
Create the JobApplication document
Add the document reference to a collection.
Retrieve the document reference from either collection.
Here's the way I ended up solving this:
To set the data:
const docData = {
listingId: "someExistingId",
jobSeekerId: "anotherExistingId",
otherData: "whatever other data goes here",
}
const docRef = await db.collection("job-application-collection")
.add(docData);
await db.collection(`job-seeker-collection/${docData.jobSeekerId}/applications`)
.add({ref:docRef});
await db.collection(`job-listing-collection/${docData.listingId}/applications`)
.add({ref:docRef});
That as, what we do is we create one 'real' document, that goes into the job-application-collection and in the JobSeeker and JobListing collections we add a 'pointer document' that just contains a single field ref, containing the document reference.
To retrieve it (in this example, retrieve all of the applications for a given JobSeeker):
const jobSeekerId = "someJobSeekerId";
const colRef = await db.collection(`job-seeker-collection/$jobSeekerId}/applications`);
const colSnapshot = await colRef.get();
/**
* The docs on the collection are actually just documents containing a reference to the actual JobApplication document.
*/
const docsProms = colSnapshot.docs.map((async (colDocData) => {
const snapshot = await colDocData.data().ref.get();
return {
...snapshot.data(),
id: snapshot.id,
}
}));
const data = await Promise.all(docsProms);
return data;
Pretty straight forward, we get the collection on the JobSeeker document, and then on each of those documents, there is a ref field, which we can use the .get() method to return a document snapshot.