Updating Array of Objects in Firebase Realtime Database in React js - reactjs

I have a problem with updating certain properties of an array of objects in a real-time database in Firebase.
My object looks like the following (see picture).
Now I want to update the IsComing property of the second participant object.
At the moment I use the updateIsComming() function, but this is not very convincing, because I have to rewrite the whole object.
function updateIsComming() {
const db = getDatabase();
update(ref(db, " your_path/EventModel/" + modelkey ), {
Location: "London",
Participants: [
{ Name: "Bella2", IsComming: "true" },
{ Name: "Tom", IsComing: "true" },
],
});
Instead, I just want to reference the specific prop from that array. For example
Participant[1].IsComming = false;
Is there any way I can access a specific array of an object directly.

Arrays as a data structure are not recommended in Firebase Realtime Database. To learn why, I recommend reading Best Practices: Arrays in Firebase.
One of the reasons for this is that you need to read the array to determine the index of the item to update. The pattern is:
Read the array from the database.
Update the necessary item(s) in the array in your application code.
Write the updated array back to the database.
As you already discovered, this is not ideal. As is common on NoSQL databases, consider an alternative data structure that better suits the use-case.
In this case, an alternative data structure to consider is:
Participants: {
"Bella2": true,
"Tom": true
}
In there, we use the name of the participant as the key which means:
Each participant can be present in the object only once, because keys in an object are by definition unique.
You can now update a user's status by their name with: update(db, "your_path/EventModel/" + modelkey + "/Participants/Tom", false).

Related

react facing issues to find out specific data from an array of objects

I have a few collections of data that are in JSON format and want to access specific data conditional data, an example I want to access which one has teacher:true and put it in a state.
I have tried it with conditions inside the loop and filter function.
[
0:{name:"Rou",id:"121"}
1:{name:"Sou",id:"122",teacher:"true"}
2:{name:"Tou",id:"123"}
3:{name:"Vou",id:"124",teacher:"true"}
4:{name:"Kou",id:"125",teacher:"false"}
5:{name:"Wou",id:"126"}
]
here I want to get only
1:{name:"Sou",id:"122",teacher:"true"}
3:{name:"Vou",id:"124",teacher:"true"}
which means only where the teacher is true.
This has nothing to do with react. It's a javascript array problem.
let people = [
{name:"Rou",id:"121"},
{name:"Sou",id:"122",teacher:"true"},
{name:"Tou",id:"123"},
{name:"Vou",id:"124",teacher:"true"},
{name:"Kou",id:"125",teacher:"false"},
{name:"Wou",id:"126"}
];
let teachers = people.filter((person) => person.teacher == "true");
Note that you need it's strongly recommended to use a boolean type instead of a string:
// Strongly discouraged
{name:"Sou",id:"122",teacher:"true"};
// Better
{name:"Sou",id:"122",teacher: true};

Setting arrays in Firebase using Firebase console

I am using Firebase console for preparing data for a demo app. One of the data item is attendees. Attendees is an array. I want to add a few attendees as an array in Firebase. I understand Firebase does not have arrays, but object with keys (in chronological order). How do I do that for preparing sample data? My current Firebase data looks like the below.
The Firebase Database doesn't store arrays. It stores dictionaries/associate arrays. So the closest you can get is:
attendees: {
0: "Bill Gates",
1: "Larry Page",
2: "James Tamplin"
}
You can build this structure in the Firebase Console. And then when you read it with one of the Firebase SDKs, it will be translated into an array.
firebase.database().ref('attendees').once('value', function(snapshot) {
console.log(snapshot.val());
// ["Bill Gates", "Larry Page", "James Tamplin"]
});
So this may be the result that you're look for. But I recommend reading this blog post on why Firebase prefers it if you don't store arrays: https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html.
Don't use an array, when you actually need a set
Most developers are not actually trying to store an array and I think your case might be one of those. For example: can "Bill Gates" be an attendee twice?
attendees: {
0: "Bill Gates",
1: "Larry Page",
2: "James Tamplin",
3: "Bill Gates"
}
If not, you're going to have to check whether he's already in the array before you add him.
if (!attendees.contains("Bill Gates")) {
attendees.push("Bill Gates");
}
This is a clear sign that your data structure is sub-optimal for the use-case. Having to check all existing children before adding a new one is going to limit scalability.
In this case, what you really want is a set: a data structure where each child can be present only once. In Firebase you model sets like this:
attendees: {
"Bill Gates": true,
"Larry Page": true,
"James Tamplin": true
}
And now whenever you try to add Bill Gates a second time, it's a no-op:
attendees["Bill Gates"] = true;
So instead of having to code for the uniqueness requirement, the data structure implicitly solves it.
To add arrays manually using Firebase Realtime DB console:
Use double " " instead of single ' ' quotes
Which provides this structure:
After writing my other answer I realized that you might simply be looking how to add push IDs in the console.
That's not a feature at the moment. Most of is either use different types of keys when entering test data or have a little JavaScript snippet in another tab to generate the keys and copy/paste them over.
Please do request the feature here, since you're definitely not the first one to ask.
firebase array ?yeah, i have same problem with you few weeks ago, but i found it in here. Finally i can use it with my ChartJS.
function jamToArray(snapshot) {
const returnArr = [];
snapshot.forEach(function(childSnapshot) {
const item = childSnapshot.val().time;
returnArr.push(item);
});
return returnArr;
};
firebase.database().ref('sensor').limitToLast(10).on('value', function(snapshot) {
const jam = jamToArray(snapshot);
});

Can I check if a value is only pushed if a certain field value is not filled already?

I am trying to make a Meteor app to let users push a value to the database. It works ok, but there a small issue. As soon a certain user has pushed his information, i don't want to let the same user create another entry. Or this must be blocked, or the value the user is pushing must be overwritten for the value he is posting the second time. Now I get multiple entry's of the same user.
Here is my code. Hope you can help me here. Thanks in advance.
Estimations.update(userstory._id, {
$addToSet: {
estimations: [
{name: Meteor.user().username, estimation: this.value}
]
}
});
From the mongo docs
The $addToSet operator adds a value to an array unless the value is
already present, in which case $addToSet does nothing to that array.
Since your array elements are objects the value is the entire object, not just the username key. This means a single user can create multiple name, estimation pairs as long as the estimation value is different.
What you can do is remove any value for the user first, then reinsert:
var username = Meteor.user().username;
Estimations.update({ userstory._id },
{ $pull: { estimations: { name: username }}}); // if it doesn't exist this will no-op
Estimations.update({userstory._id },
{ $push: { estimations: { name: username, estimation: this.value }}});
By way of commentary, you've got a collection called Estimations that contains an array called estimations that contains objects with keys estimation. This might confuse future developers on the project ;) Also if your Estimations collection is 1:1 with UserStorys then perhaps the array could just be a key inside the UserStory document?

AngularFire - How do I query denormalised data?

Ok Im starting out fresh with Firebase. I've read this: https://www.firebase.com/docs/data-structure.html and I've read this: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
So I'm suitably confused as one seems to contradict the other. You can structure your data hierarchically, but if you want it to be scalable then don't. However that's not the actual problem.
I have the following structure (please correct me if this is wrong) for a blog engine:
"authors" : {
"-JHvwkE8jHuhevZYrj3O" : {
"userUid" : "simplelogin:7",
"email" : "myemail#domain.com"
}
},
"posts" : {
"-JHvwkJ3ZOZAnTenIQFy" : {
"state" : "draft",
"body" : "This is my first post",
"title" : "My first blog",
"authorId" : "-JHvwkE8jHuhevZYrj3O"
}
}
A list of authors and a list of posts. First of all I want to get the Author where the userUid equals my current user's uid. Then I want to get the posts where the authorId is the one provided to the query.
But I have no idea how to do this. Any help would be appreciated! I'm using AngularFire if that makes a difference.
Firebase is a NoSQL data store. It's a JSON hierarchy and does not have SQL queries in the traditional sense (these aren't really compatible with lightning-fast real-time ops; they tend to be slow and expensive). There are plans for some map reduce style functionality (merged views and tools to assist with this) but your primary weapon at present is proper data structure.
First of all, let's tackle the tree hierarchy vs denormalized data. Here's a few things you should denormalize:
lists you want to be able to iterate quickly (a list of user names without having to download every message that user ever wrote or all the other meta info about a user)
large data sets that you view portions of, such as a list of rooms/groups a user belongs to (you should be able to fetch the list of rooms for a given user without downloading all groups/rooms in the system, so put the index one place, the master room data somewhere else)
anything with more than 1,000 records (keep it lean for speed)
children under a path that contain 1..n (i.e. possibly infinite) records (example chat messages from the chat room meta data, that way you can fetch info about the chat room without grabbing all messages)
Here's a few things it may not make sense to denormalize:
data you always fetch en toto and never iterate (if you always use .child(...).on('value', ...) to fetch some record and you display everything in that record, never referring to the parent list, there's no reason to optimize for iterability)
lists shorter than a hundred or so records that you always as a whole (e.g. the list of groups a user belongs to might always be fetched with that user and would average 5-10 items; probably no reason to keep it split apart)
Fetching the author is as simple as just adding the id to the URL:
var userId = 123;
new Firebase('https://INSTANCE.firebaseio.com/users/'+userId);
To fetch a list of posts belonging to a certain user, either maintain an index of that users' posts:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
fb.child('/my_posts/'+userId).on('child_added', function(indexSnap) {
fb.child('posts/'+indexSnap.name()).once('value', function(dataSnap) {
console.log('fetched post', indexSnap.name(), dataSnap.val());
});
});
A tool like Firebase.util can assist with normalizing data that has been split for storage until Firebase's views and advanced querying utils are released:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
var ref = Firebase.util.intersection( fb.child('my_posts/'+userId), fb.child('posts') );
ref.on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val();
});
Or simply store the posts by user id (depending on your use case for how that data is fetched later):
/posts/$user_id/$post_id/...
new Firebase('https://INSTANCE.firebaseio.com/posts/'+userId).on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val());
});

MongoDB: Query and retrieve objects inside embedded array?

Let's say I have the following document schema in a collection called 'users':
{
name: 'John',
items: [ {}, {}, {}, ... ]
}
The 'items' array contains objects in the following format:
{
item_id: "1234",
name: "some item"
}
Each user can have multiple items embedded in the 'items' array.
Now, I want to be able to fetch an item by an item_id for a given user.
For example, I want to get the item with id "1234" that belong to the user with name "John".
Can I do this with mongoDB? I'd like to utilize its powerful array indexing, but I'm not sure if you can run queries on embedded arrays and return objects from the array instead of the document that contains it.
I know I can fetch users that have a certain item using {users.items.item_id: "1234"}. But I want to fetch the actual item from the array, not the user.
Alternatively, is there maybe a better way to organize this data so that I can easily get what I want? I'm still fairly new to mongodb.
Thanks for any help or advice you can provide.
The question is old, but the response has changed since the time. With MongoDB >= 2.2, you can do :
db.users.find( { name: "John"}, { items: { $elemMatch: { item_id: "1234" } } })
You will have :
{
name: "John",
items:
[
{
item_id: "1234",
name: "some item"
}
]
}
See Documentation of $elemMatch
There are a couple of things to note about this:
1) I find that the hardest thing for folks learning MongoDB is UN-learning the relational thinking that they're used to. Your data model looks to be the right one.
2) Normally, what you do with MongoDB is return the entire document into the client program, and then search for the portion of the document that you want on the client side using your client programming language.
In your example, you'd fetch the entire 'user' document and then iterate through the 'items[]' array on the client side.
3) If you want to return just the 'items[]' array, you can do so by using the 'Field Selection' syntax. See http://www.mongodb.org/display/DOCS/Querying#Querying-FieldSelection for details. Unfortunately, it will return the entire 'items[]' array, and not just one element of the array.
4) There is an existing Jira ticket to add this functionality: it is https://jira.mongodb.org/browse/SERVER-828 SERVER-828. It looks like it's been added to the latest 2.1 (development) branch: that means it will be available for production use when release 2.2 ships.
If this is an embedded array, then you can't retrieve its elements directly. The retrieved document will have form of a user (root document), although not all fields may be filled (depending on your query).
If you want to retrieve just that element, then you have to store it as a separate document in a separate collection. It will have one additional field, user_id (can be part of _id). Then it's trivial to do what you want.
A sample document might look like this:
{
_id: {user_id: ObjectId, item_id: "1234"},
name: "some item"
}
Note that this structure ensures uniqueness of item_id per user (I'm not sure you want this or not).

Resources