I have a nested subcollection that looks like:
users > user.id > cart > doc.id
And I am trying to get ALL documents on that collection. Here's how I get a single one:
useEffect(() => {
const getStyleProfile = async (user: any) => {
if (user) {
const docRef = doc(db, "users", `${user.uid}`, 'cart', `${1}`);
onSnapshot(docRef, (doc) => {
setStyleProfile(doc.data())
});
}
}
getStyleProfile(user)
}, [user?.uid])
Which returns the first document:
{price: 400, property_id: 1} 'style values'
My question is: how can I return all the documents, no matter the ID?
Any question I've seen doesn't relate to subcollections, so not sure how to get it working, e.g this one
As shown in the documentation, you build a reference to a collection like this:
const usersCollectionRef = collection(db, 'users');
You build a reference to a subcollection in much the same way:
const userCartsCollectionRef = collection(db, 'users', uid, 'carts);
The collection reference is queried exactly the same way no matter how deeply nested it is, as illustrated in the documentation.
const querySnapshot = await getDocs(userCartsCollectionRef);
Related
So i've been researching about how do i get the subcollection of firebase documents. Basically its .get() but its not working now in 2022 I THINK. I have the code in below...
Let say this one I will create a subcollection path with the collections.
await setDoc(doc(db,list2[i],`${currentUser?.email}-${uid}`,`single_item`,`image`),{
creator:username,name:name,img:downloadURL,email:currentUser?.email
})
await setDoc(doc(db,list2[i],`${currentUser?.email}-${uid}`,`group_item`,`images`),{
creator:username,name:name,img:downloadURL,email:currentUser?.email
})
Now i'm getting all the items of firebase documents with these...
export const owneritemsRef = collection(db,'owner_items')
export const singleItemsRef = collection(db,'owner_items/single_item/image')
export const groupItemsRef = collection(db,'owner_items','group_item',`images`)
Now when I tried to read it in my react file...
useEffect(() => {
console.log(singleItemsRef)
const unsubscribe = onSnapshot(singleItemsRef,snapshot => {
console.log(snapshot)
setSearchFilter(snapshot.docs.map((doc,idx) => {
console.log(doc.data())
return {
...doc.data(),
name:doc.data().name
}
}))
setSearchList(snapshot.docs.map((doc,idx) => {
console.log(doc)
return {
...doc.data(),
name:doc.data().name
}
}))
})
return () => {
unsubscribe()
}
},[])
It doesn't show anything...like it is completely null.. but I can see the pathsegments of singleRef... How do I get those documents please? The diagram is like this
owner-items -> (single/group) -> image/s -> { document items }
A collection group consists of all collections with the same ID and the subcollections live under a specific document. To access the subcollection you'll need to specify that particular ID in the path reference.
Using Collection Group Queries might be the easiest way to fetch all documents from collections with the same name which are passed in the collectionGroup() method.
You need to use collection() to get a CollectionReference instead of doc() which returns a DocumentReference.
Also check these similar examples for Get Subcollection and fetching subcollection documents.
UPDATE
If you need to get that specific nested subcollection you could try something similar to below
db.collectionGroup("orders").get().then((querySnapshot) => {
console.log(querySnapshot.docs.map(d => ({id: d.id, ...d.data()})))
})
new to vue and firebase but stuck on this for hours. I have a vue3 app running a specific firebase9 query that will only return a maximum of 1 row (i.e. includes limit 1). The results are being returned but I am having trouble accessing the data so I can pass up to the template. I am using an example from a tutorial that was designed to return multiple rows from FB then iterate through in the template using v-for but in this case it will only ever be one row. All the tutorials I can find are similar not addressing how to deal with one row (or document) being returned. Oddly, the data is being returned to _rawValue but I can't seem to get to it...
Here's the JS snippet:
let collectionRef = collection(db, c)
collectionRef = query(collectionRef, where(...q), orderBy(...ob), limit(...lmt))
const unsub = onSnapshot(collectionRef, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
})
// update values
documents.value = results
})
return { documents }
Here's the Vue snippet:
const { documents: lastEvent } = getCollectionRt(
'events',
['userId', '==', user.value.uid],
['created', 'desc'],
['1']
)
console.log('lastevent: ', lastEvent)
I can see that lastEvent does indeed contain an array with the values I am looking for so the query is running and returning, BUT, it is listed in something called "_rawValue" that I can't seem to access. For example I would like to set a variable to one of the values being returned like let myVar = lastEvent.id or lastEvent.created, etc.
[edit: use case is that I want to query the users last input so I that can set some of the form data default values based on their last entry]
Any help or reference to get me unstuck would be greatly appreciated.
Screenshot of console.log
Came up with a solution. Probably hacky but works.
First modify the getCollectionRt.js that runs the query as
...
const document = ref(null)
collectionRef = query(collectionRef, where(...q), orderBy(...ob), limit(...lmt))
const unsub = onSnapshot(collectionRef, snapshot => {
let results = []
snapshot.docs.forEach(doc => {
results.push({ ...doc.data(), id: doc.id })
document.value = { ...doc.data(), id: doc.id }
})
// update values
documents.value = results
})
return { documents, document }
then pull in 'document' and return in vue as:
const { documents: lastEvent, document } = getCollectionRt(
'events',
['userId', '==', user.value.uid],
['created', 'desc'],
['1']
)
...
return {..., document }
then I can access it in the template as {{ document.id}}
Although this works, definitely spend more time learning about workign with object/arrays in VueJS
I'm currently ripping my hair out trying to query a nested collection in my Firestore db.
my db is set up as follows:
users (collection)
- username
- displayName
- ratedItems (collection)
- itemId
- timestamp
- likedItem
I want to be able to add/update the rated item in the ratedItems collection for a specific user but I seem to be hitting 2 problems:
I assume the query is incorrect as if(querySnapshot.empty) always returns false even if there are no matching items (matching the itemId) in the ratedItems collection, so it always attempts to add a new item which results in duplicate entries in the collection.
if I force the code to bypass the conditional it throws an error when it attempts to update the existing item:
Expected type 'mc', but it was: a custom yc object
My thoughts are I am using the collectionGroup query incorrectly but I haven't found a solution for this yet. Should I even be using collectionGroup at all?? from what I've read, if I understand correctly this will query every ratedItem collection regardless of the user, which isn't what I want
const rateItem = async (itemId, liked) => {
try {
const itemRef = collection(db, 'users', currentUser.uid, 'ratedItems');
const q = query(
collectionGroup(db, 'users', currentUser.uid),
where('itemId', '==', itemId)
);
const querySnapshot = await getDocs(q);
if (querySnapshot.empty) {
await addDoc(itemRef, {
itemId: itemId,
timestamp: serverTimestamp(),
likedItem: liked,
});
} else {
await updateDoc(itemRef, {
timestamp: serverTimestamp(),
likedItem: liked,
});
}
} catch (err) {
console.log(err.message);
}
};
I assume the query is incorrect as if(querySnapshot.empty) always returns false even if there are no matching items (matching the itemId) in the ratedItems collection, so it always attempts to add a new item which results in duplicate entries in the collection.
You used the itemRef for both of addDoc and updateDoc which is not the proper way to do it. You need a correct query syntax in order to update your Firestore document.
if I force the code to bypass the conditional it throws an error when it attempts to update the existing item
You can try the code below to update your nested document:
const updateitemRef = query(
collection(db, 'users', 'user_item', 'ratedItems'),
where('itemId', '==', itemId)
);
const itemSnapshot = await getDocs(updateitemRef);
itemSnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
updateDoc(doc.ref, {
likedItem: liked,
timestamp: serverTimestamp()
});
});
For the complete code, you can try the code below:
const rateItem = async (itemId, liked) => {
try {
const q = query(
collectionGroup(db, 'users', currentUser.uid),
where('itemId', '==', itemId)
);
const querySnapshot = await getDocs(q);
const additemRef = collection(db, 'users', currentUser.uid, 'ratedItems');
const updateitemRef = query(
collection(db, 'users', currentUser.uid, 'ratedItems'),
where('itemId', '==', itemId)
);
const itemSnapshot = await getDocs(updateitemRef);
if (querySnapshot.empty) {
await addDoc(additemRef, {
itemId: itemId,
likedItem: liked,
timestamp: serverTimestamp()
});
} else {
itemSnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
updateDoc(doc.ref, {
likedItem: liked,
timestamp: serverTimestamp()
});
});
}
} catch (err) {
console.log(err.message);
}
};
For more references in creating a query, you can refer to the guides below:
Add a document
Perform simple and compound queries in Cloud Firestore
So, i want to query some data from firestore.
this is my data structure
so, the collection is Modules, then i now have 2 documents but it will be 75 or something. Then in that document i want to get the specific document which has a specific LessonId (In this example '2')
How do i query this?
this is wat i tries but it's not working for me
async function getModuleData() {
let ModuleData = await firebase
.firestore()
.collection('Modules')
.where('Lessons', 'array-contains', {LessonId: 2})
.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
});
} getModuleData()
when i do this
async function getModuleData() {
let ModuleData = await firebase
.firestore()
.collection('Modules')
.where('Title', '==', 'Leven vanuit verlossing')
.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
});
} getModuleData()
it just works so it's something with my where statement i guess?
To use array-contains with an array of objects, you need to pass the complete object you are looking for in that array.
For example,
const lessonObj = {
Title: "Leven vanuit verlossing",
Description: "the desc",
...allTheOtherFieldsAsIs
}
firebase.firestore().collection("Modules").where("Lessons", "array-contains", lessonObj)
You should ideally use a sub-collection to store lessons in a module. Then you can easily query lessons using the following query:
const db = firebase.firestore()
const lessonsSnapshot = await db.collection("Modules")
.doc("moduleID")
.collection("Lessons")
.where("Title", "==", "Leven vanuit verlossing")
.get()
console.log(lessonsSnapshot.docs[0].data())
As Dharmaraj answered, the array-contains operator performs a complete match, so it only returns documents where the array contains the exact value you specified.
If you only want to filter on lesson IDs, I'd recommend adding an additional field to each document with just the lesson IDs. You can then filter on that field with:
firebase
.firestore()
.collection('Modules')
.where('LessonsIDs', 'array-contains', 2)
I already read few post on the topic, but for some reason I can not fetch the docs which I need. I have a collection users with auto generated id and each doc contains name and email. Here is my collection:
Please note, the ids are auto generated.
Then, what I try to do in the code is the following:
firebase.firestore()
.collection("users")
.where(
"id",
"in",
ids
)
.get()
.then((querySnapshot) => {
const people = [];
querySnapshot.forEach(documentSnapshot => {
people.push({
...documentSnapshot.data(),
key: documentSnapshot.id
});
});
console.log("people: ", people);
});
My people arrays is empty. I am pretty sure that my ids array has the correct ids. I am not sure if this part is correct:
firebase.firestore()
.collection("users")
.where(
"id",
"in",
ids
)
.get()
Is "id" the correct name of the auto generated column?
To query a document by it's ID, you should make use of firebase.firestore.FieldPath.documentId() which returns a special value that can be used with the where() filter to search by a document's ID.
The following code has been tweaked from this documented gist (Typescript/JavaScript):
function chunkArr(arr, n) {
if (n <= 0) throw new Error("n must be greater than 0");
return Array
.from({length: Math.ceil(arr.length/n)})
.map((_, i) => arr.slice(n*i, n*(i+1)))
}
async function fetchDocumentsWithId(collectionQueryRef, arrayOfIds) {
// in batches of 10, fetch the documents with the given ID from the collection
const fetchDocsPromises = chunkArr(arrayOfIds, 10)
.map((idsInChunk) => (
collectionQueryRef
.where(firebase.firestore.FieldPath.documentId(), "in", idsInChunk)
.get()
))
return Promise.all(fetchDocsPromises)
.then((querySnapshotArray) => {
const allDocumentsArray = [];
for (let querySnapshot of querySnapshotArray) {
querySnapshot.forEach(doc => allDocumentSnapshotsArray.push({...doc.data(), key: doc.id}))
}
return allDocumentsArray;
});
}
const usersColRef = firebase.firestore()
.collection("users");
const ids = [ /* ... */ ];
const docDataArray = fetchDocumentsWithId(usersColRef, ids);
If you were to use the unedited version of the gist, you would instead use:
const usersColRef = firebase.firestore()
.collection("users");
const ids = [ /* ... */ ];
const docDataArray = [];
await fetchDocumentsWithId(usersColRef, ids, (doc) => docDataArray.push({ ...doc.data(), key: doc.id }))
console.log(docDataArray)
Note: I'd avoid using the term "key" for Firestore and instead use "id". If you are using "id" in your documents, you could always use "_id" instead.
In this situation I am assuming that ids is an array that you have in your client side code and are looking to query only for those ids in firestore?
Unfortunately, when using the .where() functionality in firestore, it does not query against the document ID's but rather the content of the document.
If you wanted to use the .where() indexing capability of firestore, you would need to add a uid field to the actual document data. Once you do that, you should be able to query for only the ID's you would like.
From the firestore docs