I use node.js and pg library (node-postgres) to interact with database.
This is the code I use to retrieve one user of specific ID:
const { rows: users } = await db.query("SELECT * FROM users WHERE id = $1", [id]);
const user = users[0];
Even if there is only one record postgres always returns an array, so I retrieve this first item in the second line.
Is there a way to shorten this code if there is only one user? Like make it one line instead of two.
You can directly get the first user object by destructuring the rows array
const { rows: [user] } = await db.query(...)
Related
I have this question:
I want to insert 2 documents into 2 collections.
One is for user and one is for company.
Both inserts are requested via api.
In created(Inserted) company I want to know, which user[Created/inserted] create this company. And in user i want to have _id of company that he inserted.
User
{
_id: "userGeneratedId",
companyId : Company._id
}
Company
{
_id: "companyGeneratedId",
registeredByID : user._id
}
How can this be done?
Thank you,
Dufino
There are two ways to go about this
The first and easy way
Add new fields to your user and company model. maybe call it userSaveId or whatever you choose. Now you will insert same unique id to these new fields fields, so that when you are retrieving a company, you can just retrieve it based on that ID.
The second way this could be done is by performing 4 operations. 2 insert operations and two update operations. Note that this would slightly increase the response time of your operations.
Suppose you have inserted a user and company, get the IDs of both the user document and company document as such:
const user = User.save(yourData);
const company = Company.save(yourCompanyData);
Afterwards get the ids and use it to update the documents that are already stored as such:
const userId = user._id;
const companyId = company._id;
User.updateOne({_id: userId}, {$set: {companyId: companyId}});
Company.updateOne({_id: companyId}, {$set: {registeredByID: userId}});
So the complete code would be this:
const user = User.save(yourData);
const company = Company.save(yourCompanyData);
const userId = user._id;
const companyId = company._id;
User.updateOne({_id: userId}, {$set: {companyId: companyId}});
Company.updateOne({_id: companyId}, {$set: {registeredByID: userId}});
I am importing the data from the currently signed in user in order to manage the entire user profile page and all the associated actions.
On one hand I have the auth.currentUser and on the other I have the USERS collection in the db which stores all the additional data related to that particular user.
Now, my question concerns optimization. What would be the ideal way to get this user's data? Currently I am getting the entire users collection and filtering to get the one that matched the uid from the route params, yet I was told that loading the entire users collection and filtering the one I want to display was less than ideal, that I should rather create a function to get a specific user by a property such as name or id. This is what confuses me, is that not essentially what I am doing by filtering the users collection? How else would it be best to get that user's info? By creating this function in the Store and not in the component itself?
Currently it's looking like this:
UserPage.vue
const storeUsers = useUserStore();
const users = storeUsers.users;
const route = useRoute();
const id = route.params.id;
const userData = computed(() => {
return users.find((u) => u.uid == id);
});
Any way to optimize this would be appreciated.
*Adding a screenshot of the Firestore console (data model):
Your code is loading every document from the users collection into your application code, and then choosing there which single document you are actually interested in. Since you pay for every document read from the database, and you (and your users) pay for all bandwidth that is used, this is wasteful - especially as you start adding more users to the collection.
Instead you should use a query to read only the document(s) you are interested in from the database into your application code. Read the documentation for examples for all supported SDK versions.
finally solved it using a query as suggested. I am triggering the getUserInfo action whenever a user signs in and then assigning it to a pinia state called currentUserData:
AUTH STORE
async getUsers() {
onSnapshot(userCollectionRef, (querySnapshot) => {
let users = [];
querySnapshot.forEach((doc) => {
let user = {
did: doc.id,
...doc.data(),
};
this.users.push(user);
});
});
},
getUserInfo(userCredential) {
const q = query(
userCollectionRef,
where("uid", "==", userCredential.user.uid)
);
onSnapshot(q, (snapshot) => {
let currentUserData = [];
snapshot.docs.forEach((doc) => {
currentUserData.push({ ...doc.data(), id: doc.id });
});
this.currentUserData = currentUserData;
});
}
I would like to get the last key (the latest message) from my realtime database but not sure how this can be achieved.
I see from this link i need to get Last child of my firebase databse that I can use orderByKey().limitToLast(1) to get this but it looks like I need to specify the complete ref in order to achieve this. Is that correct? Or is it possible if I can orderByKey().limitToLast(1) on the val()? Or is there another way I can achieve this?
Here is my messages structure in the database:
I have a timestamp child under each key as shown above which I thought I could query in order to extract the latest key but I really don't know how to do this. Can someone please help? Below is my code so far:
database().ref(`messages/`).once(`value`, snapshot => {
if(snapshot.exists()) {
snapshot.forEach(function (childSnapshot) {
if(childSnapshot.key.includes(auth().currentUser.uid)) {
console.log("show me the key: "+childSnapshot.key)
//not working
console.log("show last message: "+ JSON.stringify(childSnapshot.val().orderbyKey().limitToLast(1)))
}
})
}
})
console.log(JSON.stringify(messages)) => [{"-MfqYBzbusp1Cljgxpan":{"unreadMessage":true,"user":{"name":"Mike","avatar":"xxxxxx","_id":"tFhmw5oQoPhk8nF2sx5rE5BFqw93"},"timestamp":1627634061437,"senderId":"tFhmw5oQoPhk8nF2sx5rE5BFqw93","notification":{"body":"Hey","title":"Project","imageUrl":"./assets/xxxxx.png"},"text":"Hey"}}]
console.log(JSON.stringify(unreadMsgs)) => []
Firebase Realtime Database queries work on a flat list of nodes. So if you have a specific path /messages/nodeid already, you can find the latest message under that, but you can't find the latest message across all of /messages.
Reading all messages from all chatrooms, just to find the latest message for each chatroom this user is in is really wasteful though. As you add more users to the app, you're driving up the bandwidth cost for them, and for yourself too.
I recommend keeping a separate node where you track the chat rooms for each user, as explained in my answer on Best way to manage Chat channels in Firebase. With such a node you can then easily determine just the chat rooms for the current user, and then load the latest message for each of them with something like:
database().ref(`user_chatrooms/${auth().currentUser.uid}`).once(`value`, indexSnapshot => {
indexSnapshot.forEach((indexSnapshotChild) => {
let chatroomId = indexSnapshotChild.key;
let query = database().ref(`messages/${chatroomId}`).orderByChild("timestamp").limitToLast(1)
query.once(`value`, (msgSnapshot) => {
console.log(`Last message in ${chatroomId} was ${msgSnapshot.val().text}`);
})
}
})
The orderByKey and limitToLast methods exists on a DatabaseReference and not on the value you fetch from the snapshot fetched earlier. It seems the parent key for all messages is of format userId1userId2. If you know this combination then you run your query this way.
const uidsKey = "uid1" + "uid2"
const query = database().ref(`messages/${uidsKey}`).orderByChild("timestamp").limitToLast(1)
query.once("value").then((snapshot) => {
console.log(snapshot.val())
})
But it seems you are trying to get UIDs of others users who have chats with user1 and trying to real all nodes first. I won't recommend doing that as that might have issues with security rules and so on. Instead if you keep list of those UIDs somewhere else, it'll be better. But if you want to keep what you have right now, try this:
const userUID = auth().currentUser.uid
database().ref("messages/").once("value").then(async (msgSnapshot) => {
const keys = Object.keys(msgSnapshot.val() || {})
const userChatKeys = keys.filter(k => k.includes(userUID))
//const otherUserIDs = userChatKeys.map(k => k.replace(userUID, ""))
//userChatKeys will be chat IDs where current user is included
//now follow the same steps mentioned in first code snippet
const queries = userChatKeys.map(chat => database().ref(`messages/${chat}`).orderByChild("timestamp").limitToLast(1).once("value"))
const lastMessagesSnap = await Promise.all(queries)
const messages = lastMessagesSnap.map(m => Object.values(m.val())[0]))
console.log(`messages: ${messages}`)
const unreadMsgs = messages.filter((msg) => msg.unreadMessage === true)
console.log(unreadMsgs.length)
})
This will logs last message from each of user's chat.
I am building a simple react-native app using firebase, in which users can create posts, see lists of other users posts, and posts can be saved as a "saved posts" as a sub-collection within my users collection.
I am new to react-native and firebase so this is just for educational purposes.
I am getting stuck on querying the posts for my 'saved' list. Currently, when a user clicks the 'save' button, a sub-collection of 'saved posts' is added to (or created) of the post id. I do not want to rewrite the entire post data to the users saved list, only to create a reference to the post via the post id.
I am able to save the list, but have not been able to query firestore for the documents associated with each post id.
Currently I can query based on the users id to retrieve the list of their own posts, as the authorID matches the user uid, and I thought getting the saved posts would be similar.
Here is my code for getting a users own list of posts:
userId is a single string representing user.uid
const getUserPosts = async (userId) => {
const snapshot = await firebase
.firestore()
.collection('posts')
.where('authorID', '==', userId)
.get();
let data = snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
return data;
};
This function is exported and then wrapped in a try catch statement within the component.
The difference between the above and getting the users list of saved posts is that I have multiple different "postId's" which I need to input (vs a single userID), but the .get() method will not accept an array for the first argument, it requires a single string.
To quickly summarize I need a method or function in which I can take an array of postId's and query my posts collection to return those posts in which the doc.id matches the postId's provided.
I apologize if this is unclear or confusing, I am getting a bit lost on this one.
Any suggestions would be very welcomed.
Thank you so much for your time!
You can do a query for multiple document IDs at once using an "in" query and using FieldPath.documentId():
const array = [...];
const snapshot = await firebase
.firestore()
.collection('posts')
.where(firebase.firestore.FieldPath.documentId(), 'in', array)
.get();
But this only works if the array is of less then 10 items long (Firestore limitation). If you need more, you'll have to either batch the IDs, into smaller arrays and perform multiple queries, or simply iterate the IDs, and get() each document individually (which is just fine, really, don't worry about performance on that).
It's working :)
Big Thank you to Doug Stevenson for your help!!
Here is the function:
postedId is an array of id's referring to the id of saved posts from the 'posts' collection
export const getSaveData = async (postedId) => {
const array = [postedId];
const snapshot = await db.collection('posts').get();
const data = snapshot.docs.map((doc) => ({ postedId: doc.id,
...doc.data() }));
return data;
};
I am trying to only fetch username and user IDs that only start with the User entered text.
Below is my firebase database:
As you can see the database contains a list of user Ids which contains the username.
For Example: If the user enters M in the search box, Query should
return Mr Jois and it's the corresponding user ID.
I am trying to do this using javascript. Below is my code:
function* searchUsers(action) {
const database = firebase.database();
const ref = database.ref('users');
try {
console.log('about to fetch filters users');
const query = ref.orderByChild('username').startAt(action.searchText);
const snapshot = yield call([query, query.once], 'value');
console.log('done fetching users');
console.log(snapshot);
}
catch(error){
console.log(error);
}
}
But I am not getting the expected results. Can someone please tell me how to query the result to get the expected result?
Firebase Database queries do a prefix match, not a contains. But since you only specify startAt(...) the query matches all users from the ones whose name starts with the prefix, including all names after it. If you only want results that start with the prefix string, you'll want to also use endAt(...):
const query = ref.orderByChild('username').startAt(action.searchText)endAt(action.searchText+"\uf8ff");
const snapshot = yield call([query, query.once], 'value');
snapshot.forEach(function(child) {
console.log(child.key, child.val().username);
});
Initially, I was thinking the equalTo() query along with Firebase .indexOn the username.
However, what we really need is a substring like ECMAScript 6's String.prototype.startsWith() method:
.startsWith(inputValue);
So, The only way I see to do it with realtime DB is to get/fetch/.once it then process client side where you have more robust string matching capability. I guess the next question is how to pull/fetch only the username property of each user key.
To query based on the first character, you should get that character and pass it to the startAt() function:
const query = ref.orderByChild('username').startAt(action.searchText.charAt(0));