Bulk Update using Batched Write of Firebase with conditions (using WHERE function) - javascript

The main goal of my system is to update the name of the user who posted on my forum if the authenticated user change or rename his or her account name.
The whole process is error-free but unfortunately, the other user who posted in the forum also updated their name.
So this is the output:
I try the following:
I use the WHERE function in Firebase to filter the post made by the user (log in user itself). I dont know why the whole process is failed.
This is the snippet code.
async updateAll(username) {
const batch = this.afs.firestore.batch();
// cUser is the USER ID
const userRef = this.afs
.collection('post', (ref) => ref.where('postedById', '==', this.cUser))
.ref.get();
(await userRef).forEach((element) => {
batch.update(element.ref, {
postedBy: username,
});
});
return batch.commit();
}

You end your query with .ref.get(). The .ref in there, actually returns the collection on which you run the query, so you end up loading the entire post collection.
You'll want to subscribe to snapshotChanges instead, or just use the regular JavaScript SDK to accomplish this (as you're not accessing the UI directly, I typically find that easier):
const userRef = firebase.firestore()
.collection('post').where('postedById', '==', this.cUser).get();
(await userRef).forEach((element) => {
batch.update(element.ref, {
postedBy: username,
});
});

Related

How to get the data of a user who created a document

I have two collections, one for registering properties and one for registered users.
The collection for registering properties is called listings and the collection for registered users is called users.
I access the documents inside the listings collection using the parameter listingId, which is the document ID.
I get the data from the document via the parameter I passed in:
const fetchListing = async () => {
const docRef = doc(db, 'listings', listingId)
// response
const docSnap = await getDoc(docRef)
if (docSnap.exists) {
listing = docSnap.data()
}
}
I need to get the fields (name,email, twitter, etc..) of the user who created the document.
How do I do this?
Each document has the ID of the user who creates it.
You have to do as follows:
Use the Object returned by the data() method, to get the user ID value (As explained in the doc, "this method retrieves all fields in the document as an Object").
Get the document corresponding to this user ID (exactly as you do to get the listing doc) and then, get the data of this document (again with the data() method).
const fetchListing = async () => {
const docRef = doc(db, 'listings', listingId);
const docSnap = await getDoc(docRef);
if (docSnap.exists) {
const userId = docSnap.data().user;
const userDocRef = doc(db, 'users', userId);
const userDocSnap = await userDocRef(docRef);
if (userDocSnap.exists) {
const userName = docSnap.data().name;
const userEmail = docSnap.data().email;
// ...
}
}
};
Note that a common approach for your use case in the NoSQL world is to denormalize your data in such a way that you get all the necessary data in your front-end in a minimum number of queries. Here is a "famous" post about NoSQL data-modeling approaches.
More concretely it means that in each Listing doc you would duplicate the data of its author/creator.
One side effect of this approach is that you need to keep the values in sync (i.e. the one in the User document and the ones in the Listing documents). This synchronization could be done with a Cloud Function triggered if the User doc is modified.

how to get users from firestore database with Like query

this function work fine with equal string but i need to search a substring: if i write "h" and the string is "hello" i need to return that
async getUsers(searchUser) {
return firestore().collection('Users').where(searchUser).where('firstName', '==', searchUser)
.limit(20).get().then(snapshot => {
snapshot.docs.forEach(doc => {
const usersData = { ...doc.data(), id: doc.id };
return usersData
});
})
}
I can give you answer!
In that case, you must use a dedicated third-party search service. These services provide advanced indexing and search capabilities far beyond what any simple database query can offer.
Please use "Algolia" and at that time your code according to your expectations must be like this.
const client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const index = client.initIndex('firstName');
index.search(searchUser, {
attributesToRetrieve: ['firstname', 'lastname'/*, .. etc the fields you need*/],
hitsPerPage: 20 /* the page Size */,
}).then(({ hits }) => {
console.log(hits); // the results you want
});
Just try it.
Helpful for you? If it's successful, I would be happy.
If you have a question please contact "Nykolai.B0411#outlook.com". I will help you.
Thanks.
When you register a new user to your app you can store their username/firstname in firestore as an array that includes the possible ways you would search for a user (look at the attached image). You can do that by splitting the name string.
then you can query the users collection by searching in that array using arrayContains like this:
await usersCollection
.where('searchOptions', arrayContains: searchText)
.get()
.then((value) =>
value.docs.map((doc) => User.fromSnapShot(doc)).toList());
If you need more capabilities than that you might need to use a 3rd party service. but this solution should be sufficient for your case.

Firebase Real time database execute conditional queries

I am using Express and Firebase Real time database Admin SDK to manage database. I need to get all the tickets whose status are pending or open from the logged in user, but I am not sure how to get this data and I am not finding the solution in the documentation. In SQL would be something like:
SELECT *
FROM tickets as t
INNER JOIN users as u
WHERE t.owner = u.id
AND (
status = 'pending'
OR status = 'open'
);
In case you need to see some code to understand what I am talking about this is what so far I've tried:
router.post('/', async (req: Request, res: Response) => {
const existingRef = database.ref('tickets')
.orderByChild('owner')
.equalTo(req.user.key)
.orderByChild('status')
.equalTo('open')
.orderByChild('status')
.equalTo('pending);
const existingSnapshot = await existingRef.on('child_added');
if (existingSnapshot.val() !== null) {
return res.json(existingSnapshot.val());
}
}
The previous code will return the tickets whose status are pending and open which does not make sense since there should be only one status per ticket.
So, what should I do? Thanks in advance
The Realtime Database can only query on one child field at a time, so the query you're proposing won't work. You'd need to filter the results after fetching them, something like:
const snap = await database.ref('tickets')
.orderByChild('user').equalTo(user.uid)
.once('value');
const out = {};
snap.forEach(t => {
if (['open','pending'].includes(t.child('status').val())) {
out[t.key] = t.val();
}
});
res.json(out);
You may want to consider using Cloud Firestore instead, as it can do more complex queries:
firebase.firestore().collection('tickets')
.where('user','==',user.uid)
.where('status','in',['open','pending'])

delete incoming write event after calculations in firebase functions

I have an app that uses firebase, the whole stack pretty much, functions, database, storage, auth, messaging, the whole 9. I want to keep the client end very lightweight. So if a user comments on a post and "tags" another user, let's say using the typical "#username" style tagging, I moved all of the heavy lifting to the firebase functions. That way the client doesn't have to figure out the user ID based on the username, and do everything else. It is setup using triggers, so when the above scenario happens I write to a "table" called "create_notifications" with some data like
{
type: "comment",
post_id: postID,
from: user.getUid(),
comment_id: newCommentKey,
to: taggedUser
}
Where the taggedUser is the username, the postID is the active post, the newCommentKey is retrieved from .push() on the comments db reference, and the user.getUid() is from the firebase auth class.
Now in my firebase functions I have a "onWrite" trigger for that specific table that gets all of the relevant information and sends out a notification to the poster of the post with all the relevant details. All of that is complete, what I am trying to figure out is... how do I delete the incoming event, that way I don't need any sort of cron jobs to clear out this table. I can just grab the event, do my needed calculations and data gathering, send the message, then delete the incoming event so it never even really exists in the database except for the small amount of time it took to gather the data.
A simplified sample of the firebase functions trigger is...
exports.createNotification = functions.database.ref("/create_notifications/{notification_id}").onWrite(event => {
const from = event.data.val().from;
const toName = event.data.val().to;
const notificationType = event.data.val().type;
const post_id = event.data.val().post_id;
var comment_id, commentReference;
if(notificationType == "comment") {
comment_id = event.data.val().comment_id;
}
const toUser = admin.database().ref(`users`).orderByChild("username").equalTo(toName).once('value');
const fromUser = admin.database().ref(`/users/${from}`).once('value');
const referencePost = admin.database().ref(`posts/${post_id}`).once('value');
return Promise.all([toUser, fromUser, referencePost]).then(results => {
const toUserRef = results[0];
const fromUserRef = results[1];
const postRef = results[2];
var newNotification = {
type: notificationType,
post_id: post_id,
from: from,
sent: false,
create_on: Date.now()
}
if(notificationType == "comment") {
newNotification.comment_id = comment_id;
}
return admin.database().ref(`/user_notifications/${toUserRef.key}`).push().set(newNotification).then(() => {
//NEED TO DELETE THE INCOMING "event" HERE TO KEEP DB CLEAN
});
})
}
So in that function in the final "return" of it, after it writes the finalized data to the "/user_notifications" table, I need to delete the event that started the whole thing. Does anyone know how to do that? Thank you.
First off, use .onCreate instead of .onWrite. You only need to read each child when they are first written, so this will avoid undesirable side effects. See the documentation here for more information on the available triggers.
event.data.ref() holds the reference where the event occurred. You can call remove() on the reference to delete it:
return event.data.ref().remove()
The simplest way to achieve this is through calling the remove() function offered by the admin sdk,
you could get the reference to the notification_id through the event, i.e event.params.notification_id then remove it when need be with admin.database().ref('pass in the path').remove(); and you are good to go.
For newer versions of Firebase, use:
return change.after.ref.remove()

#ionic change the data key in firebase

I'm trying to update/add data on firebase. I used the Facebook login and I want to use the UserID as a key for the new data aded.
(check pict below)
The userID that I want to use it:
I want to replace that key with the userID:
fblogin(){
this.facebook.login(['email'])
.then(res=> {
const fc = firebase.auth.FacebookAuthProvider.credential(res.authResponse.accessToken);
firebase.auth().signInWithCredential(fc)
.then(fs => {
this.facebook.api('me?fields=id,name,email,first_name,picture.width(720).height(720).as(picture_large)', []).then(profile => {
this.newuser = {name: profile['first_name'] ,email: profile['email'],picture: profile['picture_large']['data']['url'],phone:''}
this.navCtrl.push(HomePage);
console.log(fs.uid);
this.db.list('/users/'+ fs.uid).update(this.newuser);
});
I got this error in compilation:
supplied parameters do not matchany signature of call target
In this line: this.db.list('/users/'+ fs.uid).update(this.newuser);
Any help?
The FB error looks correct. You cant update on the uid as the user has been saved with a unique FB id
You do not show the code that created the users record in the database, but what i think you want to do is set and object when you first save the users record. However this could be an issue because the user could be saved before the return of the uid. I cant tell with your code snippet. Regardless, I will write the code that i think will work if the users/ record is created at the time that of registration.
The service
async signupUser(email: string, password: string) {
try {
const result = await this.afA.auth.createUserWithEmailAndPassword(email, password);
return result;
} catch (err) {
console.log('error', err);
}
}
So this initially creates a user without facebook, The key here is that the users FB uid was created and is held in the returned result
The component
this.authData.signupUser(email,password).then(userData => {
console.log(userData) // <- this is result
}
Then we create a record in FB with the uid returned
this.db.object(`users/${userData.uid}/`).set(data);
.set(data) is whatever data you want to save in the users/uid namespace.
So basically you need to create that user table with its uid namespace when the user first registers. Then you can update the user with the uid returned from the facebook fs.uid
With your current code you could find the user based on the email ( because the email should be unique to all users) and then update ...
with lodash is it just
let foundUser = find(this.db.list('users'),{ 'email' : fs.email }
// and then update based on the object key
this.db.list('/users/'+ Object.keys(foundUser)).update(this.newuser);
i fixed the problem by using:
this.db.object('/users/'+ fs.uid).update(this.newuser);
instead of :
this.db.list('/users/'+ fs.uid).update(this.newuser);
And it works correctly !
Thanks all for help.

Categories

Resources