After authenticating i'm trying to lookup a user document at /users/, then i'd like to update the document with data from auth object as well some custom user properties. But I'm getting an error that the update method doesn't exist. Is there a way to update a single document? All the firestore doc examples assume you have the actual doc id, and they don't have any examples querying with a where clause.
firebase.firestore().collection("users").where("uid", "==", payload.uid)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
doc.update({foo: "bar"})
});
})
You can precisely do as follows (https://firebase.google.com/docs/reference/js/v8/firebase.firestore.DocumentReference):
// firebase v8
var db = firebase.firestore();
db.collection("users").doc(doc.id).update({foo: "bar"});
//firebase v9
const db = getFirestore();
async (e) => { //...
await updateDoc(doc(db, "users", doc.id), {
foo: 'bar'
});
//....
check out the official documentation as well
Check if the user is already there then simply .update, or .set if not:
var docRef = firebase.firestore().collection("users").doc(firebase.auth().currentUser.uid);
var o = {};
docRef.get().then(function(thisDoc) {
if (thisDoc.exists) {
//user is already there, write only last login
o.lastLoginDate = Date.now();
docRef.update(o);
}
else {
//new user
o.displayName = firebase.auth().currentUser.displayName;
o.accountCreatedDate = Date.now();
o.lastLoginDate = Date.now();
// Send it
docRef.set(o);
}
toast("Welcome " + firebase.auth().currentUser.displayName);
});
}).catch(function(error) {
toast(error.message);
});
-- UPDATE FOR FIREBASE V9 --
In the newer version of Firebase this is done like this:
import { doc, updateDoc } from "firebase/firestore";
const washingtonRef = doc(db, "cities", "DC");
// Set the "capital" field of the city 'DC'
await updateDoc(washingtonRef, {
capital: true
});
in your original code changing this line
doc.update({foo: "bar"})
to this
doc.ref.update({foo: "bar"})
should work
but a better way is to use batch write:
https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes
correct way to do this is as follows;
to do any data manipulation in snapshot object we have to reference the .ref
attribute
firebase.firestore().collection("users").where("uid", "==", payload.uid)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
doc.ref.update({foo: "bar"})//not doc.update({foo: "bar"})
});
})
You only need to found official ID of document, code here!
enter code here
//Get user mail (logined)
val db = FirebaseFirestore.getInstance()
val user = Firebase.auth.currentUser
val mail = user?.email.toString()
//do update
val update = db.collection("spending").addSnapshotListener { snapshot, e ->
val doc = snapshot?.documents
doc?.forEach {
//Assign data that I got from document (I neet to declare dataclass)
val spendData= it.toObject(SpendDt::class.java)
if (spendData?.mail == mail) {
//Get document ID
val userId = it.id
//Select collection
val sfDocRef = db.collection("spendDocument").document(userId)
//Do transaction
db.runTransaction { transaction ->
val despesaConsum = hashMapOf(
"medalHalfYear" to true,
)
//SetOption.merege() is for an existing document
transaction.set(sfDocRef, despesaConsum, SetOptions.merge())
}
}
}
}
}
data class SpendDt(
var oilMoney: Map<String, Double> = mapOf(),
var mail: String = "",
var medalHalfYear: Boolean = false
)
Related
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.
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'});
}
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 transitioning a Firebase real-time database to a Firebase Firestore database but am having trouble finding the appropriate reference to query the current user.
onAuthUserListener = (next, fallback) =>
this.auth.onAuthStateChanged(authUser => {
if (authUser) {
this.user(authUser.uid)
.once('value')
.then(snapshot => {
const dbUser = snapshot.val();
// default empty roles
if (!dbUser.roles) {
dbUser.roles = [];
}
// merge auth and db user
authUser = {
uid: authUser.uid,
email: authUser.email,
emailVerified: authUser.emailVerified,
providerData: authUser.providerData,
...dbUser,
};
next(authUser);
});
} else {
fallback();
}
});
Most specifically, what would be the replacement for once('value') and snapshot.val();?
I had thought that
.onSnapshot(snapshot => {
const dbUser = snapshot.val();
...
The equivalent of once('value' in Firestore is called get(), and the equivalent of val() is data(). Calling get() returns a promise, so:
.get().then(snapshot => {
const dbUser = snapshot.data();
...
If you have a collection of users, where the profile of each user is stored within a document with their UID as its ID, you can load that with:
firebase.firestore().collection('users').doc(authUser.uid)
.get()
.then(snapshot => {
const dbUser = snapshot.val();
Note that this is pretty well covered in the documentation on getting data, so I'd recommend spending some time there and potentially taking the Cloud Firestore codelab.
How to get the push ID?
I have a JSON which looks like this:
purchaseTransaction: {
xxxxxx: {
amount: 1000,
user: yyyyyyy
}
}
and when this purchaseTransaction document is updated, I would like to get the pushID (this is generated by firebase)?
exports.addPurchaseTransactionToUser = functions.database.ref('/purchaseTransaction/{pushId}').onWrite((change, context) => {
const snapshot = change.after;
const val = snapshot.val();
console.log('val',val);
// How to get the pushID?
});
From the documentation on handling event data:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
console.log('Uppercasing', context.params.pushId, original);
So in your case the push ID will be available as context.params.pushId.
For your code, use context.params.pushId.