I am trying to add the ability to update the document name inside of my usernames collection inside my Flutter app. Here is my upload function:
Future<void> updateUsername() {
// Add a new user inside our the Usernames Collection. This is useful to check for username availablilty.
return usernames
.doc(username)
.set({
'uid': uid,
'timeCreated': DateTime.now(),
})
.then((value) => print("Username Added"))
.catchError((error) => print("Failed to add username: $error"));
}
Here is my database setup:
There is no option in firestore to rename a document. The way the most uses is to create a new document with the new name and the data that been in the old document, then delete the old document.
Take a look here: Can I change the name of a document in Firestore?
One more advice, if you're not going to put any data in the usernames documents, then make a document that has the name "usernames" and add them there, that would help to make less reads ... unless you know what you're doing.
Related
I have an array containing list of image URL in firestore. I want to delete the specific image of certain index when user clicks the delete button. This is how my firebase image array looks like Here's what i've tried
FirebaseFirestore
.instance
.collection(
'adsPost')
.doc(widget.id)
.update({
'${images[_current]}':
''
}).whenComplete(() {
print(
'image removed from firestore');
});
You have two options, to remove it from the list in your app, and then update your firebase document with the new modified listed after the image was deleted.:
.update({"images": images.remove(images[_current])});
Or delete it from Firebase:
.update({"images": FieldValue.arrayRemove([images[_current]])})
this is my function for deleting a document in my "files" collection
Future<void> deleteProgram(String id, String program) async {
try {
print(id + "----" + program);
await firestoreInstance.collection("files").doc(program).delete();
// await firestoreInstance.collection("programs").doc(id).delete();
print("done");
} catch (e) {
print(e);
}
}
program is the id of the document, when i use this nothing gets deleted, even if i hardcode the ID.
this is what my collection looks like:
as you can see, each document in the files collection also has a subcollection called files
what am i doing wrong here?
The only way to delete a collection is to delete each individual document from it. There is no atomic operation to delete a collection.
In your screenshot the opleiding4 is shown in italic, meaning that this document doesn't really exist, and the Firebase console merely shows that name to be able to show its files subcollection.
Once you delete all files from the /files/opeleiding4/files subcollection both that collection and its parent document will disappear from the Firebase console too.
Also see:
Firestore DB - documents shown in italics
How to recursively delete collection in firestore?
How to Delete all documents in collection in Firestore with Flutter
I am using Cloud Firestore as my database and I have collections of users where are stored basic information about user such as id, name, last name, email, company id.
Also I have collection of companies and in each company I have collection of tasks.
In each task I have one user assigned from collections of users (user data is replicated, so I have same data for that user as in collection users)
The problem is when I update user (change name or email...) from collection users because data is replicated that data is not changed in collection of tasks for that specific user.
Is there any way that using firestore when user from collection users is updated to automatically update it in collection of tasks?
This is quite a standard case in NoSQL databases, where we often denormalize data and need to keep these data in sync.
Basically you have two possible main approaches:
#1 Update from the client
When you update the "user" document, update at the same time the other documents (i.e. "tasks") which contain the user's details. You should use a batched write to do so: A batch of writes completes atomically and can write to multiple documents.
Something along the following lines:
// Get a new write batch
var batch = db.batch();
var userRef = db.collection('users').doc('...');
batch.update(userRef, {name: '....', foo: '....'});
let userTaskRef = db.collection('companies').doc('...').collection('tasks').doc('taskId1');
batch.update(userTaskRef, {name: '....'});
userTaskRef = db.collection('companies').doc('...').collection('tasks').doc('taskId2');
batch.update(userTaskRef, {name: '....'});
// ...
// Commit the batch
batch.commit().then(function () {
// ...
});
Note that you need to know which are "the other ("tasks") documents which contain the user's details": you may need to do a query to get these documents (and their DocumentReferences).
#2 Update in the back-end via a Cloud Function
Write and deploy a Cloud Function that is triggered when any "user" document is updated and which takes the value of this "user" document and update the "tasks" documents which contain the user's details.
Like for the first approach, you also need, in this case, to know which are "the other ("tasks") documents which contain the user's details.
Following your comment ("Is there any option to reference to another table or put foreign key?") here is a Cloud Function that will update all the ("tasks") documents that have their DocumentReference contained in a dedicated Array field taskRefs in the "user" doc. The Array members are of data type Reference.
exports.updateUser = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const newValue = change.after.data();
const name = newValue.name;
const taskRefs = newValue.taskRefs;
const promises = taskRefs.map(ref => { ref.update({ name: name, foo: "bar" }) });
return Promise.all(promises);
});
You would most probably set the value of this taskRefs field in the "user" doc from your frontend. Something along the following lines with the JS SDK:
const db = firebase.firestore();
db.collection('users').doc('...').set({
field1: "foo",
field2: "bar",
taskRefs: [ // < = This is an Array of References
db.collection('tasks').doc('....'),
db.collection('tasks').doc('....')]
});
Using Firebase Cloud Functions I'd like to search for documents that contain a certain other document in an array of document references. My structure looks as follows;
Users
name
email
cars
ref to cars/car1 for example
ref to cars/car2 for example
Cars
registration
make
model
There are multiple users and multiple cars. I need to search for users that have a certain 'car' in their car array.
I'm trying to write this in a Cloud Function and have the following;
admin.firestore()
.collection('users')
.where('cars', 'array-contains', registration)
.get().then(doc => {
console.log("TESTING: found the user " + doc.data().email)
return
}).catch(error => {
console.error(error);
});
I know this is currently just searching for the registration string in the array. Is there anyway to search for a specific document reference. I'm using Node.js.
Working code to get all the documents that have a document reference in an array;
// Notify the owner of the car
admin.firestore()
.collection('users')
.where('cars', 'array-contains', carRef)
.get().then(snapshot => {
snapshot.forEach(doc => {
console.log("TESTING found the user " + doc.data().email);
const message = {
notification: {
body: 'Your vehicle (' + carReg + ') recieved a report. Tap here to see!',
},
token: doc.data().cloudMessagingToken
};
sendMessage(message);
});
return
}).catch(error => {
console.error("Error finding a user that has the car in their garage");
console.error(error);
});
If you want to query using reference type fields, you will need to provide a DocumentReference type object to the query. If you pass a DocumentReference to a car, the query should work. For example:
const ref = admin.firestore().collection('Cars').doc(id)
where id is the id of the document.
However, you can't search using values of fields inside the referenced document. Firestore queries only work against data in a single collection at a time. With the way you have your data organized right now, it's not possible to make a single query for all users who have references to cars with a specific registration string field. For that query, you would need to also store an array of registration strings for each user that you could query with array-contains.
Yes, this involves duplication of data, and it's called "denormalization". This is very common in nosql type databases to enable the queries you need.
I have a collection called 'users'. I'm trying to add a user to the collection after Google authentication but I keep getting the following error:
FirebaseError: [code=invalid-argument]: Invalid document reference. Document references must have an even number of segments, but users has 1.
Here is the code
this.googlePlus.login({
'scopes': '',
'webClientId': environment.googleWebClientId,
'offline': true,
})
.then(user => {
// save user data on the native storage
const userRef: AngularFirestoreCollection<User> = this.afs.collection<User>(`users/`);
const data: User = {
email: user.email,
displayName: user.displayName,
uid: user.uid
};
userRef.set(data)
.then(() => {
this.router.navigate(['/home']);
Google+ is being discontinued so you should look at Firebase Authentication, or GCP's new Cloud Identity Platform.
In the case of Firebase Authentication, you must listen to the .onAuthStateChanged observer. Once it fires off your user object, you then take that and write a new user document to a users collection in Firestore. Best practise is to use the uid of the firebase.auth().currentUser.uid as the user document ID in your users collection.
Your userRef refers to a collection, and the type of object is called a CollectionReference. You're attempting to call set() on it with some object that should become a new document in that collection. But that's not the way it works. Instead, it looks like you want to call add() to add a new document with a new random ID.
If you somehow already know the ID of the new user document, you should build a DocumentReference with that id, then use set() on that DocumentReference to create the document.