Getting document fields after 'onCreate' google cloud function (firestore) - google-cloud-firestore

I've written a google cloud function which fires every time a new user document is added to my firestore database.
The tricky (I hoped) part is done, but now i'm struggling to access the actual fields in the document that was created. It's a simple user document with an "email" field.
Here is my function. What do I need to replace email with?
exports.sendWelcomeEmail = functions.firestore
.document('users/{userId}')
.onCreate((snapshot, context) => {
sendEmail(snapshot.email)
});

you can find your user data in
snapshot.data()
exports.sendWelcomeEmail = functions.firestore
.document('users/{userId}')
.onCreate((snapshot, context) => {
let user = snapshot.data();
sendEmail(user.email)
});

Related

Flutter - Search for info from Firestore like creating email ID check while signing up a Google email

I am developing an app and need a page for users to set up a nickname. In the page, I want to allow users to real-time check if there is the same nickname while typing into a textformfield. User nicknames are stored in Firestore. I am not sure how to design it. Also, I wondered if I can make this happen, is this wise when it comes to cost. Does Firestore charge for every single letter when users type in? What is the best way to do so
Please help me.
Thanks,
// let the firestore document structure be like :
// **users
// userId123456
// {
// name: Somename,
// lastname: lastname,
// username : username,
// email : someone#mail.com,
// }**
class checkingUsername extends StatefulWidget {
const YellowBird({ Key? key }) : super(key: key);
#override
State<checkingUsername> createState() => checkingUsernameState();
}
class _checkingUsername extends State<checkingUsername> {
FirebaseFirestore firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
body : Container(
child : TextField(
onChange:(text){
firestore.collection('users').where('username',isEqualTO:text).get().then((doc)
{ if(doc.exist()){
return 'User already exist';
}else{
return null;
}});
}
);
);
);
}
}
It is just a rough code try it with your own code using my logic !!
IMHO, the fastest way to do is using search extensions like Algolia, Typesense, Elastic etc.. I use Algolia search. When you install the Algolia extension, you select the fields of documents to search, it creates cloud functions that write each insert/update/delete on that fields to Algolia's own database which is optimised for search.
Then Algolia gives you the api to search on that fields. Api sends you results while you type.
But Algolia is paid extension, (storing first 10.000 document is not charged) and also cloud functions will cost a bit. But it works like a charm and Api responses is extremly fast.

How do you get the wildcard auto id parent of a document snapshot firestore onCreate function?

I have looked at similar answers to similar questions but the answers don't specifically apply.
I am seeking the simple document parent auto-id in a firestore onCreate function in js?
The firestore function log reads function returned undefined for documentiD
How do you reference the documentID ?
Firestore Log result :sendMailtransaction Function returned undefined, expected Promise or value
//Send the transaction email
exports.sendMailtransaction = functions.firestore
.document('Companys/{companyid}/Transaction/{transactionid}')
.onCreate((snap, context) => {
const transDocument = functions.firestore.document('Companys/{companyid}/Transaction/{transactionid}');
const documentiD = transDocument.documentID;
const mailOptions = {
from: 'L App<report#sample.com>', // You can write any mail Address you want this doesn't effect anything,
to: snap.data().companyemailcf, // This mail address should be filled with any mail you want to read it,
bcc: 'admin#l.com',
subject: 'L Record, New Tranaction ',
html: `<h1>Confirmed Transaction</h1>
<p>
<b>Ref: </b>${documentiD}<br>
<b>Datetime: </b>${snap.data().datetime}<br>
<b>User: </b>${snap.data().user}<br>
<b>Vehicle: </b>${snap.data().vehicle}<br>
</p>`
};
}
You don't need to query the created document to receive it in a function as you already have the document in the onCreate parameters: snapshot is the first parameter.
Example:
export const eventCreated = functions.firestore
.document('/events/{eventId}')
.onCreate(snapshot => {
console.log("this is the new document id: ", snapshot.id)
console.log("Document content:", snapshot.data())
})
In your case, use snap.id
Firebase Functions guide is here
The snapshots reference is explained here

How can I set up a Stream for a subcollection in Firestore?

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.

Firestore Subcollection '!=' query

I am aware Firestore doesn't support '!=' queries, but I was wondering if anybody has run into a similar situation as below:
Here is my db structure:
Posts -> postId -> postDocument -> likedBy -> uid
What I'm looking to do is only show posts that don't have the current user's uid in the 'likedBy' subcollection. That itself isn't possible, but I'm struggling to find even a semi-decent work around.
Currently I get all the posts and do the check locally to display the correct ones. Is this possible with perhaps a magic cloud function something?
You might find success and use cases beyond this one by maintaining a user's feed and then only calling that at runtime. I utilize this method and have found I'm given a lot of freedom and Cloud Functions let me dictate what types of posts show and under what changes are new posts added to a user's feed.
The way I do it is I look for new posts via an onCreate cloud function and then look up who should see that post, etc. and add it to each of their feeds.
In your case I can see it being used by looking for new likes on a post. On new likes, it can remove the post from the user's feed.
An example of a function (edited for brevity) that I use to add posts to the user's follower's feeds. By grabbing using a collectionGroup query I can query the list of all users who follow the author of the post.
The schema looks like this:
Users (collection)
--- User1 (document)
------- Following (collection of people User1 is following)
----------- FollowingUser1 (document, contains a uid of "followed" user)
----------- FollowingUser2
and the Cloud Function:
exports.newReview = functions.firestore
.document('reviews/{reviewId}')
.onCreate((snap, context) => {
var reviewId = context.params.reviewId
var reviewData = snap.data()
var userFollowers = db.collectionGroup('following').where('uid', '==', userId)
var followingTransaction = db.runTransaction(transaction => {
return transaction.get(userFollowers).then(restDocs => {
reviewData['added_via'] = 'following'
restDocs.forEach(doc => {
var followerId = doc.ref.parent.parent.id
var followerRef = db.collection(`feeds/${followerId}/posts`).doc(reviewId)
transaction.set(followerRef, reviewData);
})
return true
});
});
return followingTransaction.then(values => {
console.log(reviewData)
var shouldPostToTwitter = reviewData.postToTwitter
return Promise.resolve()
}) .catch(error => {
console.log(error)
return Promise.reject(new Error("Error deleting"));
});
});
DB look something look this:
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL
});
const db = admin.firestore()

Can I change the name of a document in Firestore?

I'm currently working on an app where I have to retrieve data from Google's Firestore.
My data structure looks like this:
users
- name#xxx.com
- more data
Is there a way for me to change the name#xxx.com to just name?
I can't see a method to change the name of a document.
I think the best way to do this is to get the data from 'name#xxx.com' and upload it to a new document called 'name' and then delete the old one.
Just as an example:
const firestore = firebase.firestore();
// get the data from 'name#xxx.com'
firestore.collection("users").doc("name#xxx.com").get().then(function (doc) {
if (doc && doc.exists) {
var data = doc.data();
// saves the data to 'name'
firestore.collection("users").doc("name").set(data).then({
// deletes the old document
firestore.collection("users").doc("name#xxx.com").delete();
});
}
});
There might be a case when u realize that you should have used autogenerated IDs for all your firebase documents in a collection and now you want to change the document IDs in your Collection ("collection_name") and also want to store their previous IDs as a new field ("prevID") in the new document then here's what you can do:-
FirebaseFirestore firebaseDb;
ArrayList<Map<String,Object>> data = new ArrayList<>();
firebaseDB.collection("collection_name").get.addOnSuccessListener(
queryDocumentSnapshots -> {
for (DocumentSnapshot d : queryDocumentSnapshots) {
Map<String,Object> map = d.getData();
map.put("prevID", d.getId());
data.add(map);
}
for(Map<String, Object> d : data){
Task<DocumentReference> dd = firebaseDb.collection("collection_name").add(d);
dd.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
firebaseDb.collection("collection_name").document((String) d.get("prevID")).delete();
}
});
}
});