Firebase - Firestore - get key with collection.add() - firebase

I am facing a problem with the new Firestore from Firebase.
Situation: I have a collection('room')
I create room with collection('room').add(room)
What I'm trying to do: I need to update a room.
For this, I use: collection('room').doc(ROOM_ID).update(update)
So I need to add ROOM_ID in the document in my collection:
|room
ROOM_ID
id:ROOM_ID,
someContent: ForTheQuery
Is there a possible way to achieve that?
An alternative is to create myself a generated ID with:
collection('room')
.doc(someId)
.set({
id: someId,
someContent: ForTheQuery
});
but i want to avoid it.

You can use doc() to create a reference to a document with a unique id, but the document will not be created yet. You can then set the contents of that doc by using the unique id that was provided in the document reference:
const ref = store.collection('users').doc()
console.log(ref.id) // prints the unique id
ref.set({id: ref.id}) // sets the contents of the doc using the id
.then(() => { // fetch the doc again and show its data
ref.get().then(doc => {
console.log(doc.data()) // prints {id: "the unique id"}
})
})

ANGULARFIRE:
get ID before add database:
var idBefore = afs.createId();
console.log(idBefore );
ANDROID FIRESTORE:
String idBefore = db.collection("YourCol").document().getId();

Firebase Javascript SDK:
Just use .id to get the key, here is an example using async/ await :
const KEYID = async() => (await fs.collection("testing").add({ data: 'test'})).id;

You can get the ID from the created document by using collection.ref.add(your item without id) and the response (res) will contain the new document reference created with the ID inside it. So get the ID by simply doing res.id.
createOne(options: { item: any, ref: AngularFirestoreCollection<any> }) {
const promise = new Promise((resolve, reject) => {
if (options.item) {
// Convert object to pure javascript
const item = Object.assign({}, options.item);
console.log('dataService: createOne: set item: ', item);
options.ref.ref.add(item)
.then((res) => {
console.log('dataService: createOne success: res: ', res);
resolve(res);
}).catch(err => {
console.error('dataService: createOne: error: ', err);
reject(err);
});
} else {
console.log('dataService: createOne: wrong options! options: ', options);
reject();
}
})
return promise;
}

Related

Updating displayed results after modifying Firestore doc React Native

I have a list of games that I'm able to add to without issue using UseEffect and onSnapshot. I can modify an item in the list without issue, and return one set of results (with the updated data properly displaying). When I try to modify another item (or the item same again), I get this error:
Could not update game: TypeError: undefined is not an object (evaluating '_doc.data().numPlayers') because the results/list of games are null. I'm sure I have something wrong with my code, but I can't figure it out.
Thanks in advance!
Here is my code:
useEffect(() => {
setIsLoading(true)
let results = [];
const unsubscribe = db
.collection('games')
.onSnapshot(
(querySnapshot) => {
querySnapshot.docChanges().forEach(change => {
const id = change.doc.id;
if (change.type === 'added') {
const gameData = change.doc.data();
gameData.id = id;
results.push(gameData);
}
if (change.type === 'modified') {
console.log('Modified game: ', id);
results = results.map(game => {
if (game.id === id) {
return change.doc.data()
}
return game
})
console.log(results)
}
if (change.type === 'removed') {
console.log('Removed game: ', id);
}
});
setIsLoading(false);
setGame(results);
return () => unsubscribe
},
(err) => {
setIsLoading(false);
console.log("Data could not be fetched", err);
}
);
}, []);
I forgot to add the doc ID to the gameData before adding it to the results. I did that in the "added" section, but not in the "modified" section (thinking that it was already included), forgetting that I hadn't added it as an actual field in the database (it just exists as the doc id).

Query array of object in firestore

i have a collection that is called employees, which includes documents and each document contains some data, and an array of objects that is called orgsanizations, for instance:
orgsanizations: [
{
orgId: 'org1',
registrationDate: '08/05/2021',
status: 'pending'
},
{
orgId: 'org2,
registrationDate: '12/01/2021',
status: 'approved'
}
];
I am trying to retrieve all the documents in employees that contains orgId === org1 in the orgsanizations, here is what i tried to do but keeps returning empty array.
const allEmployees = async () => {
const employeesList = db.collection('employees');
const snapshot = await employeesList
.where('orgsanizations', 'array-contains', { orgId: 'org1' })
.get();
if (snapshot.empty) {
console.log(snapshot.empty);
} else {
snapshot.forEach((doc) => {
console.log(doc.data());
});
}
};
};
Is there a solution for this or should start considering changing the structure to something else?
Thanks in advance
You can't check for the contents of a map, using array-contains. There are a couple of solutions for this...
Create a second array called orgIds, which contains only the orgId strings. You can then find any documents which contain these orgIds. To achieve this, you will need to write the orgId into the map AND the orgIds array.
Create an organizations sub-collection of your employee document and use a collectionGroup query.
{
organizations: [
{orgId: 'org1', registrationDate: '08/05/2021', status: 'pending'},
{orgId: 'org2', registrationDate: '12/01/2021', status: 'approved'}
],
orgIds: ['org1', 'org2']
}
const employeesList = db.collection('employees');
const snapshot = await employeesList
.where('orgIds', 'array-contains', 'org1')
.get();
You may also want to change your registrationDate to either a Timestamp or an ISO8601 string, so that you can sort them (if needed).

Map Firestore saving field name

can you help me? I have a problem to my code coz instead of updating my map value the path changes also
const userId = firebase.auth().currentUser.uid;
const availableRecord = firebase.firestore().collection('orders').doc(this.state.OrderId);
availableRecord.update({
stores: { userId: 'On the way'}
}).then(( res) => {
console.log('Product is set into AVAILABLE')
})
Instead of
the result is
Using the square brackets notation, as follows, should do the trick:
const userId = firebase.auth().currentUser.uid;
const availableRecord = firebase.firestore().collection('orders').doc(this.state.OrderId);
const stores = {};
stores[userId] = 'On the way';
availableRecord.update({ stores }).then(() => {
console.log('Product is set into AVAILABLE');
});
Doing
availableRecord
.update({ stores: { [userId]: 'On the way' } })
also works, as you noted in your comment.

Delete Firestore documents based on where condtiion [duplicate]

var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id);
jobskill_ref.delete();
Error thrown
jobskill_ref.delete is not a function
You can only delete a document once you have a DocumentReference to it. To get that you must first execute the query, then loop over the QuerySnapshot and finally delete each DocumentSnapshot based on its ref.
var jobskill_query = db.collection('job_skills').where('job_id','==',post.job_id);
jobskill_query.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
doc.ref.delete();
});
});
I use batched writes for this. For example:
var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id);
let batch = firestore.batch();
jobskill_ref
.get()
.then(snapshot => {
snapshot.docs.forEach(doc => {
batch.delete(doc.ref);
});
return batch.commit();
})
ES6 async/await:
const jobskills = await store
.collection('job_skills')
.where('job_id', '==', post.job_id)
.get();
const batch = store.batch();
jobskills.forEach(doc => {
batch.delete(doc.ref);
});
await batch.commit();
//The following code will find and delete the document from firestore
const doc = await this.noteRef.where('userId', '==', userId).get();
doc.forEach(element => {
element.ref.delete();
console.log(`deleted: ${element.id}`);
});
the key part of Frank's answer that fixed my issues was the .ref in doc.ref.delete()
I originally only had doc.delete() which gave a "not a function" error. now my code looks like this and works perfectly:
let fs = firebase.firestore();
let collectionRef = fs.collection(<your collection here>);
collectionRef.where("name", "==", name)
.get()
.then(querySnapshot => {
querySnapshot.forEach((doc) => {
doc.ref.delete().then(() => {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
or try this, but you must have the id beforehand
export const deleteDocument = (id) => {
return (dispatch) => {
firebase.firestore()
.collection("contracts")
.doc(id)
.delete()
}
}
You can now do this:
db.collection("cities").doc("DC").delete().then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
And of course, you can use await/async:
exports.delete = functions.https.onRequest(async (req, res) => {
try {
var jobskill_ref = db.collection('job_skills').where('job_id','==',post.job_id).get();
jobskill_ref.forEach((doc) => {
doc.ref.delete();
});
} catch (error) {
return res.json({
status: 'error', msg: 'Error while deleting', data: error,
});
}
});
I have no idea why you have to get() them and loop on them, then delete() them, while you can prepare one query with where to delete in one step like any SQL statement, but Google decided to do it like that. so, for now, this is the only option.
If you're using Cloud Firestore on the Client side, you can use a Unique key generator package/module like uuid to generate an ID. Then you set the ID of the document to the ID generated from uuid and store a reference to the ID on the object you're storing in Firestore.
For example:
If you wanted to save a person object to Firestore, first, you'll use uuid to generate an ID for the person, before saving like below.
const uuid = require('uuid')
const person = { name: "Adebola Adeniran", age: 19}
const id = uuid() //generates a unique random ID of type string
const personObjWithId = {person, id}
export const sendToFireStore = async (person) => {
await db.collection("people").doc(id).set(personObjWithId);
};
// To delete, get the ID you've stored with the object and call // the following firestore query
export const deleteFromFireStore = async (id) => {
await db.collection("people").doc(id).delete();
};
Hope this helps anyone using firestore on the Client side.
The way I resolved this is by giving each document a uniqueID, querying on that field, getting the documentID of the returned document, and using that in the delete. Like so:
(Swift)
func rejectFriendRequest(request: Request) {
DispatchQueue.global().async {
self.db.collection("requests")
.whereField("uniqueID", isEqualTo: request.uniqueID)
.getDocuments { querySnapshot, error in
if let e = error {
print("There was an error fetching that document: \(e)")
} else {
self.db.collection("requests")
.document(querySnapshot!.documents.first!.documentID)
.delete() { err in
if let e = err {
print("There was an error deleting that document: \(e)")
} else {
print("Document successfully deleted!")
}
}
}
}
}
}
The code could be cleaned up a bit, but this is the solution I came up with. Hope it can help someone in the future!
const firestoreCollection = db.collection('job_skills')
var docIds = (await firestoreCollection.where("folderId", "==", folderId).get()).docs.map((doc => doc.id))
// for single result
await firestoreCollection.doc(docIds[0]).delete()
// for multiple result
await Promise.all(
docIds.map(
async(docId) => await firestoreCollection.doc(docId).delete()
)
)
delete(seccion: string, subseccion: string)
{
const deletlist = this.db.collection('seccionesclass', ref => ref.where('seccion', '==', seccion).where('subseccion', '==' , subseccion))
deletlist.get().subscribe(delitems => delitems.forEach( doc=> doc.ref.delete()));
alert('record erased');
}
The code for Kotlin, including failure listeners (both for the query and for the delete of each document):
fun deleteJobs(jobId: String) {
db.collection("jobs").whereEqualTo("job_id", jobId).get()
.addOnSuccessListener { documentSnapshots ->
for (documentSnapshot in documentSnapshots)
documentSnapshot.reference.delete().addOnFailureListener { e ->
Log.e(TAG, "deleteJobs: failed to delete document ${documentSnapshot.reference.id}", e)
}
}.addOnFailureListener { e ->
Log.e(TAG, "deleteJobs: query failed", e)
}
}

Firestore - How to get document id after adding a document to a collection

Is there a way to acquire the document id that was generated after adding a document to a collection?
If I add a document to a collection that represents a "post" in a social media app, I want to get that document id and use it as a field in another document in a different collection.
If I can't get the document Id that was generated after adding a document, should I just compute a random string and supply the id when creating the document instead? That way I can use that same string as the field in my other document?
Quick structure example:
POST (collection)
Document Id - randomly generated by firebase or by me
USER (collection)
Document Id - randomly generated by firebase
userPost: String (this will be the document id
in the post collection that I'm trying to get)
Yes it is possible. When you call the .add method on a collection, a DocumentReference object is returned. DocumentReference has the id field, so you can get the id after the document was created.
// Add a new document with a generated id.
db.collection("cities").add({
name: "Tokyo",
country: "Japan"
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
This example is in JavaScript. Visit the documentation for other languages.
If using promises, I'd recommend using fat arrow function as it opens up the possibility for using this.foo even in the .then function
db.collection("cities").add({
name: "Tokyo",
country: "Japan"
})
.then(docRef => {
console.log("Document written with ID: ", docRef.id);
console.log("You can now also access this. as expected: ", this.foo)
})
.catch(error => console.error("Error adding document: ", error))
Using function(docRef) means you cannot access this.foo, and error will be thrown
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
console.log("You can now NOT access this. as expected: ", this.foo)
})
While fat arrow functions will allow you to access this.foo as expected
.then(docRef => {
console.log("Document written with ID: ", docRef.id);
console.log("You can now also access this. as expected: ", this.foo)
})
Edit/addition 2020:
A more popular way these days may be to use async/await instead. Notice that you have to add async in front of the function declaration:
async function addCity(newCity) {
const newCityAdded = await db.collection("cities").add(newCity)
console.log("the new city:", newCityAdded)
console.log("it's id:", newCityAdded.id)
}
And if you only want the id it can be grabbed using descructuring. Destructuring allows you to grab any key/value-pair in the response:
async function addCity(newCity) {
const { id } = await db.collection("cities").add(newCity)
console.log("the new city's id:", id)
}
It's also possible to use destructuring to grab the value and rename to whatever you want:
async function addCity(newCity) {
const { id: newCityId } = await db.collection("cities").add(newCity)
console.log("the new city's id:", newCityId)
}
If you want to use async/await instead of .then(), you can write it like this:
const post = async (doc) => {
const doc_ref = await db.collection(my_collection).add(doc)
return doc_ref.id
}
If you want to catch any errors in this function, include .catch():
const doc_ref = await db.collection(my_collection).add(doc).catch(err => { ... })
or you can have the calling function catch the error.
For Android, Java, you're suppose to get the Document ID before you set() or add() something to Firestore. Like so:
//Fields:
CollectionReference toolsCollectionRef = FirebaseFirestore.getInstance().collection(toolsCollection);
CustomPOJO_Model toolToPost;
//In Methods:
String newDocID= toolsCollectionRef.document().getId(); //Get Doc ID first.
toolToPost.setToolID(newDocID);
//Now use the doc ID:
toolsCollectionRef.document(newDocID).set(toolToPost.getConvertedTool_KeyValuePair ()).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
}
});
//Re-use same ID in another post:
usersCollectionRef.document(mAuth.getUid()).collection(usersToolsCollection).document(toolToPost.getToolID()).set(toolToPost.getConvertedTool_KeyValuePair());
using v9, you can also get the ID even before creating the document
Get a new docRef and read its random id
Use the id as you want
For example, insert the id in the document data
Then create the document
const usersRef = collection(db,'users') // collectionRef
const userRef = doc(usersRef) // docRef
const id = userRef.id // a docRef has an id property
const userData = {id, ...} // insert the id among the data
await setDoc(userRef, userData) // create the document
As others mentioned also, we can get the document reference once it added.
After we get the document reference on the behalf of id, we can update the same
Service.ts file
async funName(data: Data){
let docRef = this.firestore.collection('table-name').add(data);
console.log(docRef)
try {
const docAdded = await docRef;
console.log(docAdded.id);
this.firestore.doc('table-name/' + docAdded.id).update({ id: docAdded.id });
return docRef;
}
catch (err) {
return err;
}
}
component.ts file
async addData(){
try{
let res = await this.dataServ.funName(this.form.value);
this.snackbar.open('success', 'Success');
}catch(ex){
this.disabled = false;
this.snackbar.open('err', 'Error')
console.log(ex, 'exception');
}
}
for FB Firestore version 9 (JS/Web) use the following syntax:
import { addDoc, doc, Timestamp, updateDoc } from "firebase/firestore";
//add document to 'posts' collection with auto id
const newItem = await addDoc(collection(db, 'posts'), {
caption: post.value.caption || "No caption provided",
location: post.value.location || "No location provided",
imageUrl: imageUrl.value,
createdAt: Timestamp.now(),
});
//get new document id an update it to the file as id field.
const fileID = newItem.id
console.log('added file:', fileID);
const updateDocId = doc(db, "posts", fileID) ;
await updateDoc(updateDocId, {
id: fileID
})
I'm not sure why this one was voted out. This is what I needed I was looking for adding the doc().set() instead of doc().add().
I will be using uuid as the document to search for my users inside collection.
firebase.firestore().collection("cities").doc().set({ name: Tokyo,
country: Japan })
According to documentation in firebase v9 i think you'd want to use addDoc() method like so:
import { collection, addDoc } from "firebase/firestore";
// Add a new document with a generated id.
const docRef = await addDoc(collection(db, "cities"), {
name: "Tokyo",
country: "Japan"
});
console.log("Document written with ID: ", docRef.id);
There is also setDoc() method which allows you to set your own id for the document
import { doc, setDoc } from "firebase/firestore";
// Add a new document in collection "cities"
await setDoc(doc(db, "cities", "YOUR_CUSTOM_ID"), {
name: "Los Angeles",
state: "CA",
country: "USA"
});
Here's what I do, as mentioned in your question. Not sure if it is best practice, but it works for easy access.
When creating the document in the first place
firebase.firestore().collection("cities").doc().set({ name: Tokyo,
country: Japan })
you can set the id of the doc and put that exact id in it as a property:
firebase.firestore().collection("cities").doc('id-generated-from-somewhere')
.set({ id: 'id-generated-from-somewhere', name: Tokyo, country: Japan })

Resources