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'});
}
Related
I'm creating one document per user to store the id's of the users bookmarks. I'm using addDoc so the ID of the created doc is generated by firebase. I want to check if that document exists to create a new one or update the old. I need to have a document reference to update the doc and I had a horrible time trying to figure out a way to get a hold of that auto generated ID. My code works but it feels really hacky - Is there a better way to accomplish this?
//Add a value to the bookmarks array for individual user
export const addBookmarkForUser = async (userAuth, showId) => {
const bookmarkDocRef = collection(db, 'users', userAuth.uid, 'bookmarks')
const bookmarkSnapshot = await getDocs(bookmarkDocRef)
let userBookmarkDocId
if(bookmarkSnapshot){
bookmarkSnapshot.forEach((doc) => {
if(doc.id){
userBookmarkDocId = doc.id
console.log(userBookmarkDocId)
}
})
}
try {
if(!bookmarkSnapshot) {
await addDoc((collection(db, 'users', userAuth.uid, 'bookmarks'), {
favorites: [{showId: showId}],
}))
}else {
const userBookmarkRef = doc(db, 'users', userAuth.uid, 'bookmarks', userBookmarkDocId)
await updateDoc(userBookmarkRef, {
favorites: arrayUnion({showId: showId})
})}
} catch (error) {
console.log('Error creating bookmark', error.message);
}
};
I know I will only have one document in there for each user - is there a better way to find the doc.id?
Here is the firestore structure -
If you just want to check how many documents are present in a collection then you can use getCountFromServer() that loads the count without the actual document data.
import { collection, getCountFromServer } from 'firebase/firestore';
const bookmarkColRef = collection(db, 'users', userAuth.uid, 'bookmarks');
const bookmarksCount = (await getCountFromServer(bookmarksColRef)).data().count
console.log(`${bookmarksCount} documents found in ${bookmarksColRef.path}`)\
if (!bookmarksCount) {
// no document exists, create new
}
However, since you need the document ID and its data. You can run a simple query:
import { collection, getDocs } from 'firebase/firestore';
const bookmarkColRef = collection(db, 'users', userAuth.uid, 'bookmarks');
const bookmarksSnap = await getDocs(bookmarksColRef);
if (bookmarksSnap.empty()) {
// no document
} else {
const docData = bookmarksSnap.docs[0].data();
console.log(docData)
}
I have some code that gets a collection reference to the users collection and then queries an animeID field whether it contains a certain value or not. I want to change this and only query inside the document with the id i pass. So if you look at the picture of my firestore you can see that i have two documents inside the users collection each with their unique id. I want to query for the animeID field only in the document that i want. Not all the documents as it does right now. How would i go about doing this? I have tried using doc and then passing in the id of the document but i don't think query works on doc as it gives me an error. Thanks
const docRef = collection(db, 'users')
const q = query(docRef, where('animeID', 'array-contains', parseInt(id)))
onSnapshot(q, (snapshot) => {
let results = []
snapshot.docs.forEach((doc) => {
results.push({...doc.data(), id: doc.id})
})
if(results.length > 0){
console.log(true)
}
else{
console.log(false)
}
}, (error) => {
console.log(error)
})
Firestore structure:
You need to do as explained in the doc:
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "users", "dbmbEiR6....");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const animeID = docSnap.data().animeID;
// Do whatever you want with animeID
// E.g. log its value:
console.log(JSON.stringify(animeID));
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
So you don't need to declare a Query in this case. Just declare a DocumentReference and use the getDoc() method.
I'm new with firebase cloud function, and have some trouble to get() et set() data from firestore documents within a firebase function.
Here what I try to do within a firebase function :
Access the data of the new document "doc1" when its created in firestore;
Access the value associated with the "user" field of "doc1";
This value is of type "reference", i.e. a path pointing to another document in another firestore collection "col2/doc2"
Use this path to access the second document "doc2" and retrieve two new values belonging to this second document to add it to the first document "doc1";
Final goal is to add the values belonging to the fields "name" and "city" of "doc2" to "doc1" ;
Here what I try up to now, I'm sure I have few problems with syntax and use of then() chain, but the main idea is there :
exports.addDataFromDoc2ToDoc1 = functions.firestore
.document('col1/{doc1Id}')
.onCreate((change, context) => {
const doc1Id = context.params.doc1Id
const doc1 = change.data()
const refToDoc2 = doc1.refField
const doc2Data = refToDoc2.get()
.then(function (documentSnapshot) {
if (documentSnapshot.exists) {
doc2Data = documentSnapshot.data()
return doc2Data
}
})
const doc1Name = doc2Data.doc1Name
const doc1City = doc2Data.doc1City
db.collection('col1')
.doc(doc1Id)
.set({
name: doc1Name,
city: doc1City
});
})
I start from firebase documentation :
https://firebase.google.com/docs/functions/firestore-events
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
It would be appreciated if someone could help me with this task, and how I can restructure my algorithm to be more efficient maybe?
Thank you very much for your help and your time!
Since the field is of type Reference, you need to use the path property of the DocumentReference object, as follows:
exports.writeToFirestore = functions.firestore
.document('col1/{doc1Id}')
.onCreate((snap, context) => {
const newValue = snap.data();
const refToDoc2 = newValue.refField;
return db.doc(refToDoc2.path).get()
.then((doc) => {
if (doc.exists) {
const name = doc.data().name;
const city = doc.data().city;
return snap.ref.update({ name, city })
} else {
throw new Error('No document corresponding to the Reference!')
}
})
.catch(error => {
console.log(error);
return null;
});
});
In addition, note how we chain the promises returned by the asynchronous Firestore methods and, very important, how we return this promise chain.
Also note that we use the update() method, instead of the set() one.
I am trying to query specific document from firestore database. The problem seems to be that If I add the doc(id) statically, it works but with variable it does not even tho the variable has correct and exact same value I tested statically with.
The document I am trying to retrieve is a User node/document under /users collection.
read is the function I am using to retrieve the data:
export default class GenericDB {
constructor(collectionPath) {
this.collectionPath = collectionPath
}
/**
* Read a document in the collection
* #param id
*/
async read(id) {
const result = await (await firestore())
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (isNil(data)) return null
this.convertObjectTimestampPropertiesToDate(data)
return { id, ...data }
}
}
This is my vuex action:
getUser: ({ commit }, userId) => {
return new Promise((resolve, reject) => {
//usin UsersDB() instead of Generic() because my UsersDB() has constructor with correct path to /users
new UsersDB().read(userId).then(user => {
//Empty user if userId value is from variable and not empty if I use static value
resolve(user)
})
})
}
And I do call it out:
mounted() {
if (this.id) {
//getUser function is declared inside ...mapActions('authentication', ['getUser'])
this.getUser(this.id)
}
}
Update 1: I did compare static string against my variable with logical operator and it turns out that the variable userId has space at the end. I have no clue why and where does it come.
There is no error just empty data. I can not see what can be wrong with this simple query. Any help is appreciated!
Try making connection this way instead of directly using it.
const db = firebase.firestore();
async function read(id) {
const result = await db
.collection(this.collectionPath)
.doc(id)
.get()
const data = result.exists ? result.data() : null
if (isNil(data)) return null
this.convertObjectTimestampPropertiesToDate(data)
return { id, ...data }
}
Generally, standard format we mostly use to get document is :
const db = firebase.firestore();
const result = await db
.collection("collection_name")
.doc("document_id")
.get();
I hope this helps you. Please let me know for any issues.
After you edited the question I tried passing a valid variable and I am getting response. This is giving me data of document.
//Firebase
const admin = require("firebase-admin");
let serviceAccount = require("./firebase.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let db = admin.firestore();
//End of Firebase
id = "lWxkvqZnBxNRke4SFyJj"
async function getData(id) {
const result = await db
.collection("users")
.doc(id)
.get();
data = result.data()
console.log(data)
return data
}
getData(id)
It turned out that the userId was not exatctly the same if I compared them with logical operator. The variable version had space at the end.
So the solution was to use userId.replace(/\s/g, '')
I was facing the same issue recently. Then I figured out that my string has quotes. The following code solved the problem.
roomId.trim().replace(/['"]+/g, '')
I am trying to query a Firestore sub collection in an ionic 4 app/angular app. My database looks as follows people(id) ----> tasks(id)
{ description : this is a description about a cat,<br>
title: this is a cat <br>
category: cat }
I am using a Firestore function to query all of the data in the collection. This is what the Firestore function looks like:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'
admin.initializeApp()
export const getFeed = functions.https.onCall(async (req,res) =>{
const docs = await
admin.firestore().collection('people').limit(5).get()
return docs.docs.map(doc => {
return {
postID: doc.id,
...doc.data()
}
})
})
the typescript home.ts file looks like this :
const getFeed = this.aff.httpsCallable('getFeed')
this.posts = getFeed({}).subscribe(data=> {
console.log(data)
// this.posts = data
})
}
I've tried to use the array-contains option to query, but it doesn't
work. The array shows up empty on the console.
export const getFeed = functions.https.onCall(async (req,res) =>{
const docs = await
admin.firestore().collection('people').where("category",
"array-
contains", "cat").limit(5).get()
return docs.docs.map(doc => {
return {
postID: doc.id,
...doc.data()
}
})
})
It's not very clear from your question, but it looks like the category field of your database isn't actually a list type field. array-contains only works if the value is a list, not if it's just a single string value. If it's just a string, then use a == filter on it.
I have found workaround by adopting array field type instead of subcollection.
Here is an example of my code:
var coll = this.firestore.collection('credentials', ref => ref.where('tags', 'array-contains-any', ['agile']).orderBy('shortName')); // selects part of collection where roomId = roomStripped, sorted by name
return coll.snapshotChanges();
I have main collection called credentials, and a list of tags in each document.