I developing a simple chat applicaiton for my website using firebase firestore. where every chat session has an id
provided i have an array of ids
chat_sessions = ["q93XhadA9QQLu6X8yfZB", "YXEYISEI8b2iixCcP3VO", "GXrSZbtrmN5NcrtvjFYp"]
I want to get all document whose id is equal to any of the id's in the chat_sessions object using the code below.
return this.afs
.collection('chats', ref => ref.where('uid','in_array',chat_sessions)).snapshotChanges...
but I am not getting any results.
I come from a PHP/MYSQL background
the PHP equivalent of what i am trying to achieve will be sth like this in PHP
if(in_array(uid,chat_sessions)){
get(doc/uid)
}
can anyone help with the right query where we check for document id against a list of ids in an array? Thank You!
Thank you #frank van Puffelen. You were almost right. I should have used in instead of in_array
ref.where(firebase.firestore.FieldPath.documentId(),'in_array',chat_sessions)
did not work. Instead I replaced in_array with in :
ref.where(firebase.firestore.FieldPath.documentId(),'in',chat_sessions)
This worked! Thank you
Your query is:
ref.where('uid','in_array',chat_sessions)
This checks a field called uid in each document against the values of the chat_sessions.
It seems that instead you want to the check the ID of each document against the array, which you can do with:
ref.where(firebase.firestore.FieldPath.documentId(),'in_array',chat_sessions)
I found something else on firestore i.e "array-contains-any" for this case.
Maybe it's updated now.
UPDATE
Hi, firebase did some update recently, so for do it I found out this method
`
const [products, setProduct] = useState([]);
const ids = ['H11LlJsh3sObwORZhA0b','om9m0lU9HYWyOJZKvEdi','1AoHyHuSFcF01zoyXyTD','6xoBlxsRXUoyzBUcWl0F',
'GJqthlmBGZaFAJqtC2jK','QNT3PxMfhNGg1RZnuqcq','RZgGoFZHyDAYaVZJWxGk','g4UO5P0EgtEqJnawwhXX','gyrZm8p0cEgJdDvTuB1g','mrscldfeYlkaSF151MpI',]
useEffect(() => {
const saveFirebaseTodos = [];
ids.forEach((element) => {
fetchMyAPI()
async function fetchMyAPI() {
const q = query(collection(db, "a"), where('__name__', '==', element));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
saveFirebaseTodos.push(({id: doc.id, ...doc.data()}));
/*console.log(doc.id, " => ", doc.data());*/
if (ids.length == saveFirebaseTodos.length) {
setProduct(saveFirebaseTodos)
}
});
}})
}, [])`
In this way, you can check how many element in arrays you want (bypassing 10 elements limit of firebase).
Hope can help anyone :D
Related
If I create a document reference, and fetch it with getDoc, I get the document back just fine, but if I do a query for id == 'adsadasfsge' or id in ['adsadasfsge'] on the same database, I get nothing back. Here's the code I'm trying:
// Directly fetching one doc by id, using a docRef
const dr = doc(firestore, 'TestPrograms', id);
getDoc(dr).then((doc) => {
console.log(doc.exists());
});
// Fetching with an unambiguous == query
const q = query(collection(firestore, 'TestPrograms'), where('id', '==', id));
getDocs(q).then((docs) => {
console.log(docs.size);
});
// Fetching with an 'in' query
const q2 = query(collection(firestore, 'TestPrograms'), where('id', 'in', [id]));
getDocs(q2).then((docs) => {
console.log(docs.size);
});
Running this logs:
true
0
0
I'm baffled. What am I doing wrong here? Thanks!
Your query checks for a field named id inside the document with a specific value. If you want to check for documents whose document ID has a specific value, you need to specify the special documentId() marker as the field for the query.
I have user and group collections. Under the user collection, each document id is a user UID and each user document has an array field "userGroups" which contains the groups that user belongs to and those groups are the group's document ID under group collection.
I have been able to retrieve the userGroups array for the current user which i stored in groupRef (see code below). What I'm trying to do now is to map those array values into groups collection and retrieve only those documents that are in the groupRef. (Basically the goal is to just show in the UI the groups that the current user is a member of)
user collection
group collection
const [groupsList, setGroupList] = useState([]);
const [groupRef, setGroupRef] = useState([]);
const [track, setTrack] = useState('')
const handleSubmit = () => {
setTrack('start')
fire.firestore().collection('users').doc(fire.auth().currentUser.uid).get().then((value) => {
console.log("userGroups " + value.data().userGroups) // this returns an object
setGroupRef([value.data().userGroups])
})
}
useEffect(() => {
handleSubmit()
}, [track])
console.log("sample list " + groupRef)
fire.firestore().collection('groups').onSnapshot(snapshot => (
setGroupList(snapshot.docs.map(doc => doc.data()))
))
^ this returns all the documents under groups collection. any ideas how i can retrieve only those that the current user is a member of? any help would be much appreciated. (ive been stuck on this for a long time. im also new to firebase.)
#DougStevenson directed me to the right path of using array-contains which is a helper function on querying/getting data. the code below is the answer to my problem. this way is shorter and more efficient than the work i came up with.
fire.firestore().collection('groups').where("groupMembers", "array-contains", fire.auth().currentUser.uid).onSnapshot(snapshot => ( setGroupList(snapshot.docs.map(doc => doc.data()))
))
I iterate throught all the users node, I just want to send notifications to the users who have shop as a value inside the document, if there is users that dont have this attribute inside the document I dont want to send them a notification
exports.rememberToOpenStore = functions.pubsub.schedule('30 09 * * *').onRun(async (context) => {
var db = admin.firestore();
let snap = await db.collection('user').get();
return snap.forEach((doc) => {
if(doc.data().shop !== null){
const deviceToken = doc.data().deviceToken
const payload = {
notification: {
title: "Good morning ! 🌅😊",
body: "Remember to open your store."
}
}
return admin.messaging().sendToDevice(deviceToken,payload)
}
return null;
});
});
I want to know if this
if(doc.data().shop !== null)
will do the trick to know which user has or not that attribute
Is there a better way to do this with a where query with something like
let snap = await db.collection('user').where("shop" "!=", null).get();
to just get users that have that shop value inside the docuement ?
Thanks !
I'm unsure about the values that shop may have, but according to the documentation you could orderBy a field and if it doesn't exist it will not be returned. Thus, querying Firestore ordering by the field shop will not return the documents that don't contain said field.
The query should be something like this:
db.collection('user').orderBy('shop').get()
Another option would be creating the field for all the documents and assigning it a null value. This way you could also retrieve the documents that have said field.
In this case the query should be something like this:
db.collection('user'),where('shop', '!=', null).get()
I currently have a basic CRUD site that I started working on recently to list a number of products and the associated prices. My skills aren't the best but I am fighting the good fight and would like some assistance/guidance with what I am trying to achieve.
I have data stored in a Firestore database and I have it being pulled and shown in a list. In order to display it I have the values in my sub-collection being added to an array and then I am using a for loop to loop through and pull each value and display it using an html template string. I decided that since I have the create and display aspect of this to a decent point, I would like to add a button (or in my case an icon) that would allow me edit each value that is returned.
const shelf = doc.data();
doc.ref.collection("products").get().then((Snapshot) => {
const products = Snapshot.docs.map(doc => doc.data());
// start of for loop
for(let product of products){
// start of template string inside backticks
const li = `
<p><div>${product.name} <div class="right"><font class=pink-text>Price:</font> $${product.price} <i class="material-icons">create</i></div></div></p>
`;
// end of template string inside backticks
html += li;
// start of edit price section
let price = product.price;
const priceeditForm = document.querySelector('#priceedit-form');
priceeditForm.addEventListener('submit', (e) => {
e.preventDefault();
console.log("Document with ID: ", doc.id);
doc.ref.collection("vendors").doc(price)get().then((Snapshot) => {
const priceedit = Snapshot.docs.map(doc => doc.data());
price: priceeditForm['price'].value
}).then(() => {
// close the signup modal & reset form
const modal = document.querySelector('#modal-priceedit');
M.Modal.getInstance(modal).close();
priceeditForm.reset();
}).catch(err => {
console.log(err.message);
});
});
//end of edit price section
}
// end of for loop
productList.innerHTML = html;
});
I would like that when the icon is clicked, that a model pops up with a single input fields for the price and I can enter the new price and submit it, hence updating the value in the database.
I will be very grateful to anyone who can assist me with this. Thank you in advance.
Output Image
Thanks to everyone who assisted on this. I decided to redo my code from scratch using DOM elements to appendChild. Once done this made everything sooooo much easier. Decided to change it to give myself more flexibility with the code.
This should be a simple query, but I can't seem to get it to work or find how to get it done.
I have a list of accepted ids of a certain collection in an array. I want to do a get each item of that collection that matches one of those ids. Kind of like the reverse of the commonly used array-contains. For example:
const acceptableIds = ['id1', 'id2', 'id3']
const myCollectionDispose = await db
.collection('myCollection')
.where('id', 'is-in-array', acceptableIds)
.onSnapshot(doSomething)
I know I could just do a map on the acceptableIds with a Promise.all to fetch them but for this particular case I need a to also set onSnapshot at the end of it (as you can see in the example code), so that won't do.
Anyway, what would be the best way to do this?
It could be either to be able to fetch the items of myCollection by id using the where or being able to set onSnapshot on the array generated from the Promise.all. Just in case the latter one is relevant, here is the code for that:
const acceptableIds = ['id1', 'id2', 'id3']
const myCollectionDispose = await Promise.all(
acceptableIds.map(id => {
return db
.collection('myCollection')
.doc(id)
.onSnapshot(doSomething)
})
)
Thanks!
You can save each of the Promises in an array and then use a Promise.all() when each is complete.
const acceptableIds = ['id1', 'id2', 'id3']
const promises = []
for (var i = 0; i < acceptableIds.length; i++) {
promises.push(
db.collection('myCollection')
.where('id', 'array-contains', acceptableIds[i])
.onSnapshot( /* Do Something */ )
)
}
Promise.all(promises).then(() => {
// Whatever you need to do next with all the snapshots (e.x. remove them all)
})
you have to use
import * as admin from "firebase-admin"
db.collection('myCollection')
.where(admin.firestore.FieldPath.documentId(), "in", yourArrayOfIds)
Here, yourArrayOfIds can be up to 10 items. You will have to implement required batch logic to gather up your result, if necessary.