First time poster here!
While I was trying to build a little exercise organizer application with ReactJS and Firebase realtime database I encountered a problem with the Firebase push() method.
I have a couple elements on my page that push data to the database once they are clicked, which looks like this:
const planRef = firebase.database().ref("plan");
const currentExName = e.currentTarget.firstChild.textContent;
const exercise = {
name: currentExName,
type: e.currentTarget.children[1].textContent,
user: this.state.user.displayName
};
planRef.push(exercise);
Also, if the element is clicked again, then it gets removed from the database like this:
planRef.orderByKey().on("value", snapshot => {
let exercises = snapshot.val();
for (let ex in exercises) {
if (exercises[ex].name === currentExName) {
planRef.child(ex).set(null);
}
}
});
This is working fine as long as I don't try to push something to the database when I just deleted the last bit of data from it. In that case it gets removed right away.
Data getting removed
Summary:
Write data to the realtime database using ref.push()
Delete data using ref.child(child).set(null) (I tried remove() before, same problem)
Try to push the same data to the database again which leads to the data getting deleted right after being written to the database
I couldn't find anything about this kind of problem so far so I guess I might have made a mistake somewhere. Let me know if the information provided is not sufficient.
Thanks in advance.
Removing a child is an asynchronous operation. I guess what is happening here is the removing operation takes more time than the new writing operation. You will need to await for it if you want to write again on the same key.
planRef.child(ex).set(null).then(() => {
planRef.child(ex).push(new);
});
Or using async/await:
await planRef.child(ex).set(null);
planRef.child(ex).push(new);
Let me know if it worked.
Related
I have recently implemented firebase into my project and I have created a user collection, this collection has a document for each user and each document has about 8 fields, when my user launches the app, I am trying to pull the document that corresponds to his data, so im doing the following query:
async function getUserData() {
const _collection = collection(db, "users")
const _query = query(_collection, where("userid", "==", uniqueUserID))
const querySnapshot = await getDocs(_query)
querySnapshot.forEach((doc) => {
console.log(doc.data())
})
setLoadingStatus(false)
}
This query works and gives me the corresponding user data, but the problem is, if the user is too far down the collection, this will execute 8 reads per document until it gets to the corresponding user, I have tried to implement a cache system using a lastModified but I still need to read the document data for that field and it will end up using more or less the same amount of reads. My question is: How do I reduce the amount of read operations that get executed when im trying to compare values in the documents, I have also thought of adding an a like so a_uniqueUserID so it gets ordered alphabetically and takes the first spot of the document but it's hacky.
EDIT: Here is what my structure looks like:
I think you are misunderstanding the definition of a document and a field. When you read a document, you always get all fields out of it. The snapshot contains everything read, even if you don't use it. There is no additional cost per field, other than the storage required to hold it all. In your screenshot, you show 5 documents, and one of those documents have 8 fields.
You are probably misunderstanding the metrics in the console. When you read and write documents using the console, those are also billed as reads and writes - use of the console is not "free". What you are seeing is a combination of what your app is doing in combination with what you're doing in the console.
I have a fully functioning CRUD app that I'm building some additional functionality for. The new functionality allows users to make changes to a list of vendors. They can add new vendors, update them and delete them. The add and delete seem to be working just fine, but updating doesn't seem to be working even though it follows a similar method I use in the existing CRUD functionality elsewhere in the app. Here's my code:
// async function from AXIOS request
const { original, updatedVendor } = req.body;
let list = await Vendor.findOne({ id: 1 });
if (!list) return res.status(500).json({ msg: 'Vendors not found' });
let indexOfUpdate = list.vendors.findIndex(
(element) => element.id === original.id
);
list.vendors[indexOfUpdate].id = updatedVendor.id;
list.vendors[indexOfUpdate].name = updatedVendor.name;
const updated = await list.save();
res.json(updated);
The save() isn't updating the existing document on the DB side. I've console logged that the list.vendors array of objects is, indeed, being changed, but save() isn't doing the saving.
EDIT:
A note on the manner of using save, this format doesn't work either:
list.save().then(res.json(list));
EDIT 2:
To answer the questions about seeing the logs, I cannot post the full console.log(list.vendors) as it contains private information, however, I can confirm that the change made to the list is showing up when I run the following in the VendorSchema:
VendorSchema.post('save', function () {
console.log(util.inspect(this, { maxArrayLength: null }));
});
However, the save still isn't changing the DB side.
Since you are using nested objects, Mongoose will not be able to detect the changes made. You need to mark the modified as an object before the save
list.markModified('vendors');
I am using Firebase Realtime Database. I have an object which has all the posts created by all our users. This object is huge.
In order to display the posts in a fast way, we have given each user an object with relevant post IDs.
The structure looks like this:
/allPosts/$postID/
: { $postID: {id: $postID, details: '', title: '', timestamp: ''} }
/user/$userID/postsRelevantToThisUser/
: { $postID: {id: $postID} }
'postsRelevantToThisUser' only contains the IDs of the posts. I need to iterate over each of these IDs and retrieve the entire post information from /allPosts/
As a result, the client won't have to download the entire allPosts object and the app will be much faster.
To do this, I've written the below code. It is successfully retrieving and rendering only the relevant posts. Whenever a new postID is added or removed from /postsRelevantToThisUser/ in Firebase Realtime Database, React Native correctly re-renders the list.
However, when anything in /allPosts/$postID changes, for exampe: if title parameter changes, it is not reflected in the view.
What's a good way to solve this problem?
let userPostRef = firebase.database().ref(`/users/${uid}/postsRelevantToThisUser`)
userPostRef.on('value', (snapshot) => {
let relPostIds = [];
let posts = [];
snapshot.forEach(function(childSnapshot) {
const {id} = childSnapshot.val();
relPostIds.push(id);
})
relPostIds.map(postId => {
firebase.database().ref(`allPosts/${postId}`).on('value', (postSnapshot) => {
let post = postSnapshot.val()
posts.push(post);
this.setState({ postsToRender:posts });
})
})
Since you've spread the data that you need to show the posts to the user over multiple places, you will need to keep listeners attached to multiple places if you want to get realtime updates about that data.
So to listen for title updates, you'll need to keep a listener to each /allPosts/$postID that the user can currently see. While it can be a bit finicky in code to keep track of all those listeners, they are actually quite efficient for Firebase itself, so performance should be fine up to a few dozen listeners at least (and it seems unlikely a user will be actively reading more post titles at once).
Alternatively, you can duplicate the information that you want to show in the list view, under each user's /user/$userID/postsRelevantToThisUser nodes. That way you're duplicating more data, but won't need the additional listeners.
Either approach is fine, but I have a personal preference for the latter, as it keeps the code that reads the data (which is the most critical for scalability) simpler.
I am using Firebase realtime database in Android app, and have data like this:
How can I access that data from javascript.
I just wanna code function to get value of every endTime and compare with current time and if endTime <= Current Time then delete values yellow marked on anther image.
Events is fixed value.
Indjija, Vojvodina and -KsB2mVkpgQe_fRyFFH4 are auto generated so it's possible to have more values like that two.
I know how to solve that in android studio with Java code but I'm interesting about implementing server functions.
The answer to this question is buried deep within Firebase Database Reference.
forEach() is used to get to the child without knowing the child's exact path.
var leadsRef = database.ref('leads');
leadsRef.on('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childData = childSnapshot.val();
});
});
Now childSnapshot will contain the required data, the same thing can also be accessed using child_added.
leadsRef.on('child_added', function(snapshot) {
//Do something with the data
});
The only difference is that in case of forEach(), it will loop through from the start so if any new data will be added, it will also load the previous data but in case of child_added, the listener is only on new child and not the previous existing childs.
Useful Link:
Firebase Read and Write Web
Hope it helps.
I went through docs, github repositories but nothing worked for me yet.
My datastructure:
App {
posts : {
<post_keys> : {
auth_name : "name",
text : "some text" //and many other fields
}
}
}
1) Github repository : If I use this, I only get one field from one function, if I need all the fields, I would need to write separate functions for each, which is a bad approach.
2) Algolia Official Docs for Node.js : This cannot be deployed as a cloud function, but it does what I intend to do.
How can I write a function that can be deployed on Firebase and gets the whole object indexed with its key in Algolia?
Okay so I went ahead to create a Firebase Cloud function in order to index all objects in the Algolia index. This is the solution:
What you were doing is something like this:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}/text').onWrite(event => {
What you should do is the following:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}').onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
var firebaseObject = event.data.val();
firebaseObject.objectID = event.params.blogid;
return index.saveObject(firebaseObject).then(
() => event.data.adminRef.parent.child('last_index_timestamp').set(
Date.parse(event.timestamp)));
});
The difference is in the first line: In the first case, you only listen to text changes, hence you only get the data containing the text change.
In the second case, you get the whole object since you listen to changes in all of the blog object (notice how /text was removed).
I tested it and it works for me: whole object including author was indexed in Algolia.