Query firestore database for document id - javascript

I want to query a firestore database for document id. Currently I have the following code:
db.collection('books').where('id', '==', 'fK3ddutEpD2qQqRMXNW5').get()
I don't get a result. But when I query for a different field it works:
db.collection('books').where('genre', '==', 'biography').get()
How is the name of the document id called?

I am a bit late, but there is actually a way to do this
db.collection('books').where(firebase.firestore.FieldPath.documentId(), '==', 'fK3ddutEpD2qQqRMXNW5').get()
This might be useful when you're dealing with firebase security rules and only want to query for the records you're allowed to access.

Try this:
db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get()
(The first query is looking for an explicit user-set field called 'id', which probably isn't what you want.)

You can use the __name__ key word to use your document ID in a query.
Instead of this db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get() you can write
db.collection('books').where('__name__', '==' ,'fK3ddutEpD2qQqRMXNW5').get().
In this case you should get an array of length 1 back.
The firebase docs mention this feature in the rules documentation. https://firebase.google.com/docs/reference/rules/rules.firestore.Resource

June, 2021
The new v9 modular sdk is tree-shakeable and results in smaller compiled apps. It is recommended for all new Firestore apps.
import { doc, getDoc } from "firebase/firestore";
const snap = await getDoc(doc(db, 'books', 'fK3ddutEpD2qQqRMXNW5'))
if (snap.exists()) {
console.log(snap.data())
}
else {
console.log("No such document")
}
This is based on the example from the firestore docs
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "SF");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
}
else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
You could make this into a helper function
async function getDocument (coll, id) {
const snap = await getDoc(doc(db, coll, id))
if (snap.exists())
return snap.data()
else
return Promise.reject(Error(`No such document: ${coll}.${id}`))
}
getDocument("books", "fK3ddutEpD2qQqRMXNW5")

You can get a document by its id following this pattern:
firebase
.firestore()
.collection("Your collection")
.doc("documentId")
.get()
.then((docRef) => { console.log(docRef.data()) })
.catch((error) => { })

While everyone is telling to use .get(), which is totally reasonable but it's not always the case.
Maybe you want to filter data based on id (using a where query for example).
This is how you do it in Firebase v9 modular SDK:
import {collection, documentId} from 'firebase/firestore'
const booksRef = collection('books')
const q = query(booksRef, where(documentId(), '==', 'fK3ddutEpD2qQqRMXNW5'))

Currently only working way for Cloud Functions if you really need to use this way:
// Import firebase-admin
import * as admin from "firebase-admin";
// Use FieldPath.documentId()
admin.firestore.FieldPath.documentId()
const targetUser = await db.collection("users").where(admin.firestore.FieldPath.documentId() "==", "givenId").get();
Simpler way of this is directly using ID value thru path as there is only one document with given document ID:
const targetUser = await db.doc("users/"+ "givenId").get();
However, you may really need to use it if you are matching given IDs array to the Firebase collection like this:
const admin = require("firebase-admin");
const arr = ["id1", "id2"];
const refArr = arr.map(id => admin.firestore().collection("media").doc(id));
const m = await admin
.firestore()
.collection("media")
.where(admin.firestore.FieldPath.documentId(), "in", refArr)
.get();
This last example is from this discussion

If you are looking for more dynamic queries with a helper function, you can simply try this.
import { db} from '#lib/firebase';
import {query, collection, getDocs ,documentId } from "firebase/firestore";
const getResult = async (_value) => {
const _docId = documentId()
const _query = [{
field: _docID,
operator: '==',
value: _value
}]
// calling function
const result = await getDocumentsByQuery("collectionName", qColl)
console.log("job result: ", result)
}
// can accept multiple query args
const getDocumentsByQuery = async (collectionName, queries) => {
const queryArgs = [];
queries.forEach(q => {
queryArgs.push(
where(q.field, q.operator, q.value)
);
});
const _query = query(collection(db, collectionName), ...queryArgs);
const querySn = await getDocs(_query);
const documents = [];
querySn.forEach(doc => {
documents.push({ id: doc.id, ...doc.data() });
});
return documents[0];
};

From Firestore docs for Get a document.
var docRef = db.collection("cities").doc("SF");
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});

This is the first link that came up when I was looking to solve it in the Golang SDK, so I'll add my solution in case anyone else is looking for it:
package main
import (
"context"
"fmt"
"log"
"cloud.google.com/go/firestore"
firebase "firebase.google.com/go/v4"
"google.golang.org/api/option"
)
type (
Car struct {
ID string
Name string `firestore:"name"`
Make string `firestore:"make"`
Price float64 `firestore:"make"`
}
)
func main() {
ctx := context.Background()
// Use a service account
options := option.WithCredentialsFile("PATH/TO/SERVICE/FILE.json")
// Set project id
conf := &firebase.Config{ProjectID: "PROJECT_NAME"}
// Initialize app
app, err := firebase.NewApp(ctx, conf, options)
if err != nil {
log.Fatal(err)
}
// Get firestore client
client, err := app.Firestore(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
collectionRef := client.Collection("CAR_COLLECTION")
// firestore.DocumentID == "__name__"
docSnap, err := collectionRef.Where(firestore.DocumentID, "==", collectionRef.Doc("001")).Get(ctx)
if err != nil {
log.Fatal(err)
}
// Unmarshall item
car := Car{}
docSnap.DataTo(&car)
car.ID = docSnap.Ref.ID
// Print car list
fmt.Println(car)
}

Just to clear confusion here
Remember, You should use async/await to fetch data whether fetching full collection or a single doc.
async function someFunction(){
await db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get();
}

Related

Firestore query returns nothing

Here is the structure of the firestore database
ingredients->(doc name)-> ingredientName: "Apple"
I am trying to figure out the document name of the document with Apple in it but I keep running into an issue where nothing is returned.
async function getIngredientID(){
const q = query(collection(fsdb, 'ingredients'), where('ingredientName', '==', 'Apple'));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
}
there is nothing that comes out on the console. At one point I console logged the value of q and there was no document there. All of the StackOverflow answers have to do with Web version 8 but I am working with Web version 9.
I imagine you must have the imports, make sure you have them all correctly. Now import >>fsdb<< make sure to start cloud firestore and get a reference to the service, check that the where method is correct as well as the collection, i don't know what information it has when initializing firebase, it would be nice if you could send more information, if this information does not help you
import { collection, getDocs, query, where } from "firebase/firestore";
import { fsdb } from '../fb';
async function getIngredientID() {
try {
const q = query(
collection(fsdb, "ingredients"),
where("ingredientName", "==", "Apple")
);
const { docs } = await getDocs(q);
const data = docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
} catch (error) {
console.log(error);
}
}

react Firestore query nested collection where field then update, or add new doc

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

How we save auto generated key of a document in firestore database in web version 9 [duplicate]

Heres what i am trying to achieve i want a unique id field for every document in my database and i want that unique id to the same as the document id.
Example:
documents: data:
eBgdTRE123 id:'eBgdTRE123'
name:'Jhon Doe'
job:'Programmer'
i want i databse to have this structure, now i got two ideas to achieve this
1: to use cloud function and have onCreate listener and everytime theres a new document grab document and set the id field and update it heres how i am doing it
exports.addDocIdToField =
functions.firestore.document('collectionname/{docID}').onCreate((snap,context) => {
const id = context.params.docID;
return admin.firestore().collection('collectionname')
.doc(id).set({ id: snap.id }, { merge: true })
.then(() => {
return null;
})
.catch((error) => {
return null;
});
})
2: to use the above kind of method on document creation. add a new document as soon as that document is added get the newly added document and update its id
both of them work but my question is can i rely on this kind of operation? i mean if the id is by any means undefined it can cause an error further in the app.
or if there are other ways to achieve this?
See JS SDK v9 syntax at the bottom
There is a simpler way to achieve that, using the doc() method, as follows (here with the JavaScript SDK v8)
var newDocRef = db.collection('collectionname').doc();
newDocRef.set({
name:'Jhon Doe',
job:'Programmer',
id: newDocRef.id
})
As explained in the doc:
(the doc() method) gets a DocumentReference for the document within the collection at the
specified path. If no path is specified, an automatically-generated
unique ID will be used for the returned DocumentReference.
You will find similar methods in the other Client SDKs, here for Android and here for iOS.
UPDATE FOR JS SDK v9:
import { collection, doc, setDoc } from "firebase/firestore";
const newDocRef = doc(collection(db, "collectionname"));
await setDoc(
newDocRef,
{
name:'Jhon Doe',
job:'Programmer',
id: newDocRef.id
}
)
the previous method works fine but just for the sake of clarification
what is it really like
const { doc, collection, getFirestore, setDoc, addDoc } = require('firebase/firestore');
let collectionId = "Cars";
let docId;
let firestore = getFirestore();
async function addDocWithId() {
let collectionRef = collection(firestore, collectionId)
addDoc(collectionRef, {}).then(res => {
docId = res.id
let docRef = doc(firestore, collectionId + "/" + docId)
setDoc(docRef, {
id: docId,
car: 'Benz'
})
})
};
how it has been clarified
const { doc, collection, getFirestore, setDoc, addDoc } = require('firebase/firestore')
let collectionId = "Cars"
let firestore = getFirestore()
async function addDocWithId() {
let collectionRef = collection(firestore, collectionId)
let docRef = doc(collectionRef)
setDoc(docRef, {
id: docRef.id,
car: "Volvo"
})
}
If in case if there's anyone who didn't have luck with above provided answers, try this -> docref.set({ 'id':docref.ref.id}). It worked for me. Below is a usecase of this.
create(tutorial: any): any {
var docref = this.db.collection('invxxx').doc()
docref.set({ 'id':docref.ref.id, anotherField: 'anotherValue'});
}

How to get a Subcollection inside a Collection in Firestore Web Version 9 (Modular)?

I was using the chaining mode of the Firestore Web 8, but I'm in the way of updated it to Module 9 and have been a hard time trying to figure out how to get all the content of my subcollection (collection inside my collection).
My older function is like this and works fine:
function getInfo(doc_name) {
let infoDB = db
.collection("collection_name")
.doc(doc_name)
.collection("subcollection_name")
.get();
return alunoHistorico;
}
so with the module way I tried this code
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const docRef = doc(db, "collection_name", "doc_name");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
but the function doc() expects a even arguments (not counting the db argument) so if I try to use with 3 arguments like this, I get a error:
const docRef = doc(db, "collection_name", "doc_name", "subcollection_name");
to it work I have to pass the exactly document that is inside the subcollection
const docRef = doc(db, "collection_name", "doc_name", "subcollection_name", "sub_doc");
but it doesn't work for me because I have a list os docs inside the subcollection, that I want o retrieve.
So how can I get all my docs inside my subcollection?
Thanks to anyone who take the time.
You need to use collection() to get a CollectionReference instead of doc() which returns a DocumentReference:
const subColRef = collection(db, "collection_name", "doc_name", "subcollection_name");
// odd number of path segments to get a CollectionReference
// equivalent to:
// .collection("collection_name/doc_name/subcollection_name") in v8
// use getDocs() instead of getDoc() to fetch the collection
const qSnap = getDocs(subColRef)
console.log(qSnap.docs.map(d => ({id: d.id, ...d.data()})))
I wrote a detailed answer on difference between doc() and collection() (in V8 and V9) here:
Firestore: What's the pattern for adding new data in Web v9?
If someone want to get realtime updates of docs inside sub collection using onSnapshot in Modular Firebase V9, you can achieve this like:
import { db } from "./firebase";
import { onSnapshot, collection } from "#firebase/firestore";
let collectionRef = collection(db, "main_collection_id", "doc_id", "sub_collection_id");
onSnapshot(collectionRef, (querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log("Id: ", doc.id, "Data: ", doc.data());
});
});

Firebase Firestore is not creating a doc - any idea how to debug?

I've been struggling with a weird error. Can't create a doc in firebase. There are no security rules to speak of, just:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Firestore is initialised the normal way and is active:
import { Firebase } from "../db";
let firebase = Firebase();
let firestore = firebase.firestore();
But nothing happens after this is run other than printing "here1", the other consoles aren't doing anything and the userid doc is not being created and no collection and doc under it.
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
The above doesn't print anything other than "here1" and gets stuck on the setting of the doc. The doc isn't created in Firestore either.
Any idea what might be wrong and how to debug it? Wasted a good 4 hours on trying to figure it out and worried if Firestore is so buggy that it's unsafe to use it in production.
First of all, I assure you Firebase is not buggy at all, we have it running on several production applications and they're running fantastic.
Second, I think your issue here is that you're passing a function as the second argument in the set() method, which is nowhere that I can find in the API reference. Instead, it returns a promise. Your code should look like this:
firebase.firestore()
.collection("users")
.doc(uid)
.set({ uid: uid })
.then((doc) => { console.log(doc.id) })
.catch((err) => { console.log(err) })
Cheers.
Here is an example which will work for you:
file test.mjs
import { Firestore } from '#google-cloud/firestore';
const firestore = new Firestore()
export default (uid) => firestore.collection("users")
.doc(uid)
.set({ uid })
.then(() => console.log('success')) // documentReference.set() returns: Promise < void >
.catch(err => console.error(err))
It's super weird, but what solved the issue for me is adding an unnecessary doc.get() like so:
export const addEnquiry = async (data) => {
let user = await firebase.auth().currentUser;
data.uid = user.uid;
console.log("here1");
const enquiry = await firestore.collection("users").doc(data.uid).collection("enquiries").doc();
const x = await firestore.collection("users").doc(data.uid).get();
// ^^^ added the above line I don't actually need, but somehow it
// establishes a connection to firebase or something which allows the
// promise below to resolve, rather than just hang!
// Doesn't resolve without it for whatever reason!
return await enquiry
.set(data)
.then((doc) => {
console.log("here2");
return true;
})
.catch((err) => {
console.log("here3");
console.log(err);
return false;
});
};
When removing the line, the function hangs again. So have to keep it in for now!
A bit worrying that we have to use such workaround hacks to make a simple write to the firestore, to work, Firebase!
Nonetheless, hope it helps someone facing this undebuggable problem.

Categories