Firebase Real time database execute conditional queries - javascript

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'])

Related

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

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,
});
});

How to check if a container exists in cosmos DB using the node sdk?

I want to check if a container exists and if not, initialize it. I was hoping for something like the following:
const { endpoint, key, databaseId } = config;
const containerName = "container1"
const client = new CosmosClient({ endpoint ,key});
const containerDefinition = getContainerDefinition(containerName);
const db = await createDatabase(client, databaseId);
if (!db.containers.contains(containerName)
{
// Do something
}
The reason I'm not using "createIfNotExists" is because I would need to make a 2nd call to check if the container returned is populated with items or not. The container I'm creating is going to hold settings data which will be static once the container is initially created. This settings check is going to happen per request so I'd like to minimize the database calls and operations if possible.
I tried doing something like:
try
{
db.container(containerName).read();
}
catch(err)
{
if(err.message.contains("Resource Not Found"))
{
// do something
}
}
But that doesn't seem like the right way to do it.
Any help would be appreciated!
I'm not quite clear on why you would need to do this since typically you only need to do this sort of thing once for the life of your application instance. But I would not recommend doing it this way.
When you query Cosmos to test the existence of a database, container, etc., this hits the master partition for the account. The master partition is kind of like a tiny Cosmos database with all of your account meta data in it.
This master partition is allocated a small amount of the RU/s that manage the metadata operations. So if you app is designed to make these types of calls for every single request, it's quite likely you will get rate limited in your application.
If there is some way you can design this such that it doesn't have to query for the existence of a container then I would pursue that instead.
Interesting question. So i think you have few options
Just call const { container } = await database.containers.createIfNotExists({ id: "Container" }); it will be fast probably few milliseconds, since I went via code at looks like it will always try to read from cosmos :( If you want to still check if container exists sdk has methods(But again no real benefits ):
const iterator = database.containers.readAll();
const { resources: containersList } = await iterator.fetchAll();
Create singleton and first time just initialise all your containers so next time you dont call it, sure if you scale each instance will do the same
My favourite, use terraform/armtemplates/bicep to spin up infrastructure so you code wont need to handle that
You can try this code:
async function check_container_exist(databaseId,containerId) {
let exist = false;
const querySpec = {
query: "SELECT * FROM root r WHERE r.id = #container",
parameters: [
{name: "#container", value: containerId}
]
};
const response = await client.database(databaseId).containers.query(querySpec).fetchNext();
if(response.resources[0]){
exist = true;
}
return exist;
}

How to (using React JS web) and Firestore, can you find out when a chatRoom (on the Firestore Database) receives new messages?

I am trying to build an app using FireStore and React JS (Web)
My Firestore database basically has:
A collection of ChatRooms ChatRooms
Every chat-room has many messages which is a subcollection, for example:
this.db.collection("ChatRooms").doc(phone-number-here).collection("messages")
Also, every chat-room has some client info like first-name, last-name etc, and one that's very important:
lastVisited which is a timestamp (or firestamp whatever)
I figured I would put a React Hook that updates every second the lastVisited field, which means to try to record as accurately as possible on Firestore the last time I left a chat-room.
Based on that, I want to retrieve all the messages for every customer (chat-room) that came in after the last visit,
=> lastVisited field. :)
And show a notification.
I have tried from .onSnapshot listener on the messages subcollection, and a combination of Firestore Transactions but I haven't been lucky. My app is buggy and it keeps showing two, then one, then nothing, back to two, etc, and I am suffering much.
Here's my code!
Please I appreciate ANY help!!!
unread_messages = currentUser => {
const chatRoomsQuery = this.db.collection("ChatRooms");
// const messagesQuery = this.db.collection("ChatRooms");
return chatRoomsQuery.get().then(snapshot => {
return snapshot.forEach(chatRoom => {
const mess = chatRoomsQuery
.doc(chatRoom.id)
.collection("messages")
.where("from", "==", chatRoom.id)
.orderBy("firestamp", "desc")
.limit(5);
// the limit of the messages could change to 10 on production
return mess.onSnapshot(snapshot => {
console.log("snapshot SIZE: ", snapshot.size);
return snapshot.forEach(message => {
// console.log(message.data());
const chatRef = this.db
.collection("ChatRooms")
.doc(message.data().from);
// run transaction
return this.db
.runTransaction(transaction => {
return transaction.get(chatRef).then(doc => {
// console.log("currentUser: ", currentUser);
// console.log("doc: ", doc.data());
if (!doc.exists) return;
if (
currentUser !== null &&
message.data().from === currentUser.phone
) {
// the update it
transaction.update(chatRef, {
unread_messages: []
});
}
// else
else if (
new Date(message.data().timestamp).getTime() >
new Date(doc.data().lastVisited).getTime()
) {
console.log("THIS IS/ARE THE ONES:", message.data());
// newMessages.push(message.data().customer_response);
// the update it
transaction.update(chatRef, {
unread_messages: Array.from(
new Set([
...doc.data().unread_messages,
message.data().customer_response
])
)
});
}
});
})
.then(function() {
console.log("Transaction successfully committed!");
})
.catch(function(error) {
console.log("Transaction failed: ", error);
});
});
});
});
});
};
Searching about it, it seems that the best option for you to achieve that comparison, would be to convert your timestamps in milliseconds, using the method toMillis(). This way, you should be able to compare the results better and easier - more information on the method can be found in the official documentation here - of the timestamps of last message and last access.
I believe this would be your best option as it's mentioned in this Community post here, that this would be the only solution for comparing timestamps on Firestore - there is a method called isEqual(), but it doesn't make sense for your use case.
I would recommend you to give it a try using this to compare the timestamps for your application. Besides that, there is another question from the Community - accessible here: How to compare firebase timestamps? - where the user has a similar use cases and purpose as yours, that I believe might help you with some ideas and thoughts as well.
Let me know if the information helped you!

How to query database with firebase cloud functions

I am trying to query my firestore database using cloud functions.
I want to trigger an email notification every time a new reading in my database is under the value of 10.
Here is the relevant database structure for reference: database structure.
The "readings" field is an array and each "reading" is a map which holds the fields "date" and "value".
Currently I am at the point where I can send an email notification every time a new user is created however I want this to work for the database. I am unsure how to query for the "readings" array and then for each individual reading.
Here is my code so far which sends an email when a new user is created
exports.sendNotification = functions.auth.user().onCreate((user) => {
const mailOptions = {
from: '"Spammy Corp." <noreply#firebase.com>',
to:"fakeEmail#btopenworld.com",
text: "TEST"
};
return mailTransport.sendMail(mailOptions)
.then(() => console.log("It worked"))
.catch((error) =>
console.error('There was an error while sending the email:', error));
});
See: https://firebase.google.com/docs/firestore/extend-with-functions
For example, to fire on all new readings added to that first child:
exports.sendEmail = functions.firestore
.document('sensor/UGt.../readings')
.onCreate((snap, context) => {
const newValue = snap.data();
const value = newValue.value;
if (value < 10) {
// send email
}
});
In further comments you mentioned listening for new readings in all sensor elements, not just your first one. This is unfortunately not possible in an efficient / simple way (source). Instead you will have to listen to all onUpdate events on /sensor/, check if the update is adding a reading, then check the value & send your email.
It may be easier to call the cloud function directly from wherever adds the reading, depending on how many times the /sensor/ path is going to be updated for other reasons (since every time this happens, it's a waste of resources).

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()

Categories

Resources