I want to remove one value from an array in Firestore. I am using firebase functions and am updating this field after the deletion of a User. I have tried updating the array, I have tried arrayRemove, and a variety of other lines. Because I have to deploy every time I want to run a test it is getting time consuming and data is difficult to build and delete every time.
So I was hoping someone could help with this solution I found here:
I want to loop through a list of IDs (Users in the DB) and pull out their matches, and remove a specific ID from their list of matches.
When I run this, it doesn't know what firebase.firestore is. So I am not sure how to register firestore with this type of FieldValue.delete(). What is this section looking to target? "admin.FieldValue.delete()"?
admin.firestore()
.collection("Users")
.doc(matches[i])
.update({
[userId]: firebase.firestore.FieldValue.delete(),
});
I have also tried this:
admin
.firestore()
.collection("Users")
.doc(matches[i])
.update({
matches: admin.firebase.firestore.FieldValue.arrayRemove(userId),
});
but again it doesn't know what firebase.firestore is. What is the correct method to removing one value from an array?
Also, I am concerned about making a callout in a for loop but I am not seeing a way around it. Does anyone have any suggestions? These don't need to be async as all of the transactions are not reliant on one another.
error message i get in firebase functions:
ReferenceError: firebase is not defined
If you imported the Admin SDK like this:
const admin = require("firebase-admin");
You will want to use admin.firestore.FieldValue.delete(). Same pattern for arrayRemove().
Related
I am attempting to update the "likes" field for every document in a collection in Firebase. This operation must be done once every week, so we are using Firebase Cloud Functions to achieve this. As of now, the following code works to achieve this goal (in Web version 8 namespaced):
await db.collection("events").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
doc.ref.update({
likes: 0
});
});
However, I would like to implement batch writes to make this operation scalable. I would also like to keep the bill generated from using Firebase to a minimum. How can this be implemented in this particular case? I have been looking through the Firebase Documentation in the Transactions and Batched Writes section, but have not found much help. The issue is that the document names must be known ahead of time according to the docs.
Is this safe to be added on the function once you added a bot to a server or I can be abused?
I'm kinda scared of getting banned or rate limited So I want to make sure that everything is safe. Currently the fetch in Docs v12 is not that well documented and lack of infos about this. All I know is it fetch all data to the server. The reason I need this is to get all of the information like roles, members, etc because when trying to list all members that has the specific role all try to list members without roles it require me to fetch data since if I don't instead of members(100) it will only fetch like members(3) since the cache is not yet ready or data is not fully loaded to the cache.
await message.guild.members.fetch()
.then(console.log)
Currently I have this command /fetch which will fetch the data with a 1 hour cooldown. I am planning to remove this and add it to "once they added the bot" function. I use this fetch to instances/commands like listing users without roles
message.guild.members.cache.filter(member => member.roles.cache.array().length < 2).map(member => member.user.tag);
If you have like recommendation or a better way to do this you can check the project at PruneBot/commands/custom where full code of this is included:
fetch data
kick no role
kick users with specific role
list users without role
list users with specific role
if you have a large scale bot, that is joining many servers then you might have issues with this call, but if you're not having this issue, then you can pull it when the bot is added to a server, by putting it in the guildCreate event handler. Something like:
client.on('guildCreate', guild=> {
guild.members.fetch()....
So in my web application, I am fetching data from firebase, it will have about 5000 documents by the time it's ready. So to reduce the number of reads, I keep an array in firebase with a list of ids of each documents, which I read and then compare with the list I have saved locally. If an id is missing, I read that particular doc from firebase (Firebase website definitely slowed down with having an array with 3000 docs so far).
However, consider the situation where I have 2000+ docs missing and I have to fetch them one by one according to missingList I created from comparing the firebase and local arrays. It is heck a lot of slow, it takes so long to fetch even a hundred documents because I am using await. If I don't, then firebase is overloaded with requests and for some reason it shows a warning that my net is disconnected.
What is the best way to fetch documents with ids given in an array, with no loss and reasonably fast enough? Is there a way for me to batch read documents like there is batch write, update and set?
Say Firebase has [1234, 1235, 1236, 1237] and I only have [1234] so I need to read [1235, 1236, 1237] from it.
I am using a for loop to iterate through each id and get the corresponding document like so
for (let i = 0; i < missingList.length; i++) {
var item = await db.collection('myCollection').doc(missingList[i]).get().then((snapshot) => snapshot.data())
await saveToDB(item) //I use PouchDB for local storage
}
The closest thing to a 'batched read' in Firestore is a transaction, but using a transation to read hundreds of document is not really efficient. Instead, I suggest you add one field to every document, like token. Then, every time you get data for the first time, generate a random token client-side, then write it to every document that you read. After that, if you detect a change in the list of ids you mention above, run a query on that token field, like :
db.collection('myCollection').where('token', '!=', locallySavedToken).get()
Remember to write your local token back to the read documents. This way, your query time will be much faster. The down side is you need 1 extra write request for every document read. If you need to re-read your data a lot then maybe look into transactions, since write requests pricing is more expensive than read requests
How do I find a subcollection based on a field value? I am currently using this code but it doesnt work:
var user = db()
.collection('myCollection')
.doc()
.collection('private')
.where("nam", "==", this.state.passcode);
What I am trying to achieve is making a custom authentication, so giving a custom username and password to users (in this case just a passcode). So I decided to store those credentials in a separate sub-collection inside a document. How can a user authenticate by comparing the values of username and password with the ones of a sub-collection?
Another question, is sub-collection for credentials a good idea? Will it cost the same to me as if I would store those info in the document?
First of all, what you're doing right now is not secure at all. You should never store user credentials in a database, especially not one that's directly accessible to your web and mobile clients. To do this properly, you should be making use of Firebase Authentication to sign in users. (You tagged this question firebase-authentication, which refers to that product.) In fact, doing security properly is very difficult. Firebase Auth will make sure everything is done correctly.
Secondly, the query you have now will never yield any documents. That's because you're not passing anything to doc(), which means it will return a DocumentReference to a non-existent document with a random ID. If you meant to have some sort of unique identifier for each user, perhaps that's something you would want to pass to doc() so that each user's subcollection would be correctly identified.
I am trying to migrate from Firebase Real time Database to Cloud Firestore in order to use "complex" queries among collections but I found some "issues" that I don't know how to solve.
My server uses Express JS and Firebase Admin SDK. I am trying to create a CRUD router for my admin dashboard but I am getting the following error:
"9 FAILED_PRECONDITION: The query requires an index. You can create it here: ...
When I access that URL (...) I get some error I don't have enough permissions... Well, anyway, after some research I understood that "complex" queries requires indexes and they have to be more than one.
This is my current router code so you can see what I want to achieve:
// GET - Get users
router.get('/', async (req: Request, res: Response) => {
...
let ref: any = db.collection('users').orderBy('createdAt');
Object.keys(req.query).forEach(key => {
ref = ref.where(key, '==', req.query[key]);
});
...
});
As you can see when I iterate req.query key I am adding a conditional statement to the query. My idea is to progamatically add custom filters from the client.
According to what I understood from documentation, I should create a complex index type for every document property.
Therefore, what should I do in order to achieve the mentioned idea? Thanks in advance.
Firestore compound queries have some limitations.
One is that to use more than one method, for example "where" and "orderBy", you need to create an index for this query.
So in your case, as your queries are dynamic, for each query you will have to create an index.
My suggestion is that you only use the "where" in your queries and in the other filters use javascript to filter. Or migrate to another database like MongoDB.
This is the link to the Firestore documentation explaining the compound queries: https://firebase.google.com/docs/firestore/query-data/queries?hl=en-us