How to update a field inside a list in firebase? - javascript

I have this collection in Firebase
I have tried to modify the value within the map notes of a specific subject, I could not do it and I have tried in several ways without success
I would really appreciate a little help.
I get the data in that way, but I can not update that data.
this.firestore.collection('school').doc('254')
.get().toPromise().then(doc => {
if (doc.exists) {
const student = doc.data().class.find(value => {
return value.name === 'john';
});
console.log('student', student);
/*
Here edit student.notes.math = newValue
*/
}
});
PD: I'm currently work with Angular 7.

I don't see that your code attempts to update the document that you found. Changing the data in memory doesn't change the contents of the document. You will have to write additional code to update the document with your changes to the class field.
See:
Firestore: How to update specific field of document?
The documentation
this.firestore
.collection('school')
.doc('254')
.update("class", NEW_FIELD_CONTENTS)
You will need to provide the entire contents of the updated class field. Firestore doesn't have any operations that let you update an array item at a specific index.

Related

Error when querying for specific field inside an array inside a document in Firebase V9

I posted a question earlier about how to query for a specific value inside an array. I was told that there was a similar question already asked. I have looked at the solution for that question however i am getting an error. This is my code currently to check to see if the field mal_id inside the favourites array contains an id passed by me.
const docRef = collection(db, 'users')
const q = query(docRef, where('favourites', 'array-contains', {mal_id: id}))
I then attached an onSnapshot method to see if the data exists which is like this
onSnapshot(q, (snapshot) => {
if(snapshot.data()){
console.log(true)
}
else{
console.log(false)
}
})
But i get an error saying snapshot.data is not a function. idk if you are not supposed to use an onSnapshot method on this kind of query or my code is wrong. I have attached a picture of my database structure. Thanks.
*Edit
So, i have changed my data structure. Now i am creating a new field called animeID and then storing the id into it. I then do the array contains onto that but the snapshot still gives me the same error. Here is the updated code along with a new picture of my database structure.
const docRef = collection(db, 'users')
const q = query(docRef, where('animeID', 'array-contains', `mal_id: ${id}`))
onSnapshot(q, (snapshot) => {
if(snapshot.data()){
console.log(true)
}
else{
console.log(false)
}
})
Updated firestore structure
As Doug commented, Firestore's array-contains operator checks for equivalence between the value you supply and an entire array item. It can't find a subset of an item.
The common workaround is to add an additional field to your document with just the mal_id values you want query on. So something like:
Favourites_mal_ids: [21]
And then you can perform an array-contains on that field.

Pass a variable into a query (Firebase)

this is really frustrating me I hope you kind people can assist me.
I have a firebase firestore db that stores info about properties and cities. All properties contain a key:value pair called location_city. It is a string that stores the City that the property resides in.
I have made a dropdown box on a webpage with a list of available cities. The idea is to dynamically display properties from the selected city, by means of the .where() method. The third parameter accepted by the method is the string you want to query against.
I want to pass the text value of the dropdown list into the query.
Here's my code:
let selectedCityViewHomes;
function getDropdownSelection(sel) {
selectedCityViewHomes = sel.options[sel.selectedIndex].text;
selectedCityViewHomes.toString();
}
db.collection('Homes')
.where('location_city', '==', selectedCityViewHomes)
.get()
.then(snapshot => {
snapshot.docs.forEach(doc => {
renderHome(doc);
});
});
The query method only seems to work if you manually enter a string into the parameter and does not seem to allow you to pass a variable (which is a string) into the method.
I have tried concatenating "'" either side to mimic 'Manchester', for example and this did not work either.
TIA
Charlie
EDIT: I found where i had gone wrong, it was an issue of scope i think. I fixed it by creating a function which wrapped the db.collection code and passed in the variable as an argument.
const updateHomesViewHomes = (input) => {
db.collection('Homes').where('location_city', '==', input).get().then((snapshot) => {
homesViewHomes.innerHTML = "";
snapshot.docs.forEach(doc => {
renderHome(doc);
});
});
};
Thanks for the replies, hope this helps someone.

Mongoose save() not saving changes

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');

Firebase Realtime Database listener inside a loop is not working properly

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.

Determining what has been added/deleted/changed in a Firestore list

Firestore as the backend. I've managed to get through by simply using basic crud methods. However, I wanted to find out how do I determine the changes to a list of items that are returned after the initial subscription.
What I'm ultimately looking to do is :
- miminise the amount of documents that are read each time
- animate a list of items (entry animation, exit animation, change animamtion)
In the following example I have the basic crud method along with the initial subscription:
posts:post [] = [];
constructor(private db: AngularFirestore){}
ngOnInit(){
//The initial subscription to the posts
this.db.collection("Posts").valuechanges().subscribe( _posts => {
this.posts = _posts;
});
async addItem(_post:post)
{
_post.id = this.db.createId();
await this.db.collection("Posts").doc(_post.id).set(_post);
}
async update(_post:post)
{
await this.db.collection("Posts").doc(_post.id).update(_post);
}
delete (_post:post)
{
await this.db.collection("Posts").doc(_post.id).delete();
}
With the above methods, I'm subscribing to the documents in the Posts collection. Initially I'm receiving an arrray of type Post, and whenever another item is added, updated, removed i'm receiving an updated array of of type post.
How do I differentiate what has happened to the item so I can animate the changes (i.e animate the entry of the item etc...) ?
It would really help me out if you could show a sample code ?
Thanks
The valueChanges observable only exposes the actual data in the document. It has no other metadata about the document, nor the kind of change.
If you need more information, listen for documentChanges instead. That exposes a stream of DocumentChangeAction objects, which amongst others contain a type property that is the DocumentChangeType.
See https://github.com/angular/angularfire2/blob/master/docs/firestore/documents.md#the-documentchangeaction-type

Categories