Address Firebase object by child value, and then modify that object? - javascript

I'm facing a little difficulty finding information about how to modify objects in Firebase Realtime Database. I'm adding items into my database in real-time, so I won't know ahead of time what the object key is. As you know, the database structure looks like this:
Say I want to address a child of "testing", whose category is "social", what I do is this:
firebase.database().ref("testing").orderByChild("category").equalTo("social")
But how exactly can I then address this child so as to update this entire child (including all the fields - "category", "date", etc.) or even delete it? Thanks so much for any help!

To update (or delete) a node in Firebase you must know the complete path to that node. If you don't know the complete path, you can use a query to determine the node(s) matching a certain condition.
So in your case you'll need to execute the query, loop over the results, and update each child node in turn. In code:
let query = firebase.database().ref("testing").orderByChild("category").equalTo("Social");
query.once("value").then((results) => {
results.forEach((snapshot) => {
snapshot.ref.update({ propertyToUpdate: "new value" });
});
});
If you want to delete the matching node(s), the innermost line would be: snapshot.ref.remove().

Related

How can I access the child of a unique key in Firebase?

I am trying to access the child value of a unique key value (that had been "pushed") within Firebase. Currently, my database looks like this: I want to access the value of "emailOfUser"
I am very new to Firebase so I am not familiar with the functions. Currently, this is my method of obtaining other values for a different section of the database:
Thank you so much for any feedback!
I've tried different methods to accessing this data within the Firebase, but I cannot get it to work/the methods I were using were outdated. I also tried to "update" the Firebase instead of "pushing" the values to prevent a unique key from generating, but it simply overwrote my current data rather than appending something new.
If you want to load all the users who voted and print their emails, you can do that with:
get(child(dbref, 'usersWhoVoted')).then((snapshot) => {
snapshot.forEach((childSnapshot) => {
console.log(childSnapshot.key, childSnapshot.val().emailOfUser);
});
})
Note that your current structure allows a user to vote multiple times. If you want to only allow them to vote once, use some identifier of the user as the key in your database structure:
userVotes: {
"uniqueIdOfUser1": "valueTheyVotedOn",
"uniqueIdOfUser1": "valueTheyVotedOn",
...
}
Now each user can by definition only vote once, If they vote again (assuming your security rules allow that), their new vote will simply replace the existing vote.

orderByValue of subkey

I'm new to firebase and wanted to ask how I can retrieve only the latest data e.g. the last 5 activities added.
I structured my data the following way: activities have the userID as a key and the timestamp as a subkey, see:
activities
|-uid
|-timestamp
|-data: {}
so for example:
activities
|-4zDhgv1UEjb3EfZ9XhO2PAHdHYg9
|-1621608449507
|-distance: 12.5
|-1621608957090
|-distance: 9.75
I think of some query like this, which of course doesn't work atm
firebase.database.ref(`activities`).SUBLEVEL().orderByValue().limitToLast(5);
Thanks in advance for any help!
Firebase queries work on a flat list of the child nodes directly under the path you query. So you you run a query of activities, it will consider each node directly under it as a possible result.
The value to order/filter on must be at a known location under each direct child node. That means that in your current model you can only order/filter,
either if you know the entire activities/4zDhgv1UEjb3EfZ9XhO2PAHdHYg9 path already,
or only want to order/filter on the 1621608449507/distance subpath under there.
Also see:
Firebase Query Double Nested
Firebase query if child of child contains a value

Firestore data modeling and angularFire

I have data model like this
Players-->root collection
Sports--->root collection
Players_Sports---root collection
I want get all the sports(Multiple sport) details or document player belongs.
For this I am using angularFireStore5
First, I am getting
Player details like this
this.db.collection('players').doc(id).get().toPromise()
Second, I am getting Player(user) linked PlayerSport
db.collection<any>('PlayerSports',ref => ref.where('playerId', '==', id) ).get().toPromise()
Third, I am trying to get Sports details based on ID'S,
db.collection<any>('sportsType', ref => ref.where('sportsType' 'in' [sportsIDs])).get().toPromise()
where SportIDs is arrary of ID that are linked in player_sports
First and Second steps works fine, but third steps is not giving any data or response
Could you please let me know where is the problem,
is it in Data model or code? my guess is that data model is not correct. Please guide me on this.
I would suggest getting the data from firebase and storing it inside a list so the app can access it later.
void getDataF(){
databaseReference
.collection("TableName")
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f) {
iDFacList.add(f.documentID);
dbFacList.add(f.data["FieldName"]);
});
});
}
There is no sportsType field in the sportsType document as far as I can see.
If you're trying to find documents based on their SportsId field, you'll want ref.where('SportsId'....
Update
It seems that you're trying to find a document by its ID, which you can do with:
ref.doc(sportsIDs)
If you want to get multiple documents, or get a single document as a collection, you can use:
ref.where(firebase.firestore.FieldPath.documentId() 'in' [sportsIDs])

Firebase - remove something by value

I'm creating advanced chatbot and I'm using Firebase to store names, chat bans, messages.
I want to remove something by value, so, if I banned user "test", i want to remove that same user with "test".
Here is my Firebase ban structure:
So, i want to remove "-KCXvmm_M-Nd7sR724hJ" by value (name), is that even possible?
ref: var banRef = new Firebase('application.firebaseio.com/ban');
Push: banRef.push({name:'test1'});
It should be a simple matter of:
ref.orderByValue().equalTo('test1').on('child_added', function(snapshot) {
snapshot.ref().remove();
});
Note that the query may match multiple children, in which case the child_added event will fire for each child and they'll all get removed.

Query data in one collection based on data in another collection in MongoDB

I am trying to learn how to use MongoDB and am really confused how to do this. What I have are two collections, one which has a number of users and another collection which has a number of items. For example:
users:
{
"_id" : ObjectId("56dba03438e1a255b97e82b6"),
"name" : "john",
"age" : 25
}
items:
{
"_id" : ObjectId("56dba0db38e1a255b97e82b7"),
"name" : "pencil"
}
Now what I want to do in my app is to allow users to select an item but multiple users can select the same item. So I need to keep track of which users clicked which items. I thought about doing this using another collection which keeps track of the user id and item id (a user can only select an item once). Is this the correct approach? I created this collection:
useritems:
{
"_id" : ObjectId("56dba0db38e1a255b97e82b7"),
"userid" : "56db9fb038e1a255b97e82b5",
"itemid" : "56dba03438e1a255b97e82b6"
}
If this is the right approach, then I want to be able to click on an item in my app and for it to display a list of all the users who selected that item. How can I do this? I got as far as to display only the useritems collection documents where the itemid = itemid selected on the app...but now how would I display all of the users from the users collection based on the ones in the useritems collection?
router.get('/userlist/:id', function(req, res) {
var db = req.db;
var collection = db.get('useritems');
collection.find({'itemid' : '_id'},{},function(e,docs){
res.json(docs);
});
});
Thanks for the help, I'm really having a hard time understanding how this would work.
The idea of creating a third collection is a solution that mirrors how you would solve this problem in a relational database. With MongoDB, it often pays off to think about different patterns based on how you access your data.
In your case, I would not create another collection, but track which user has selected which item within the user document, within the item document, or within both documents. Which way you do this depends on your data access patterns.
Adding Selected Item to User Document
{
"_id": ObjectId("56dba03438e1a255b97e82b6"),
"name": "john",
"age": 25,
"selectedItemId": "56dba0db38e1a255b97e82b7"
}
If you will often want to see the item each user has selected, it makes sense to store the item inside the user document. When you retrieve a user, you would only have to do one extra call to the items collection to retrieve the item for that user. (If you decide to use Mongoose as an object-document mapper (ODM), then you can achieve this extra call by using Mongoose's populate method).
Adding User to the Item Document
{
"_id": ObjectId("56dba03438e1a255b97e82b7"),
"name": "pencil",
"selectedBy": [
"56dba0db38e1a255b97e82b4",
"56dba0db38e1a255b97e82b5",
"56dba0db38e1a255b97e82b6"
],
}
If you will often want to see which users have selected a given item, it makes sense to store an array of users inside the item document. When you retrieve an item, you would then have the IDs of the users that selected that item, which you could then retrieve from the database. (Again, if you decide to use Mongoose you can do this by using its populate method).
Adding Both Solutions
The reason why you would prefer one solution over another is that given your access pattern, you will be spared from iterating through the whole collection to get the data you need. For example, in the case were you add the array of users to an item, if you wanted to find the item a given user has selected, you would have to iterate though all the items and look in the array of user IDs until you found the user you wanted. Something similar would occur if you only stored the item ID inside a user document and suddenly needed to look at all the users for a given item. If both of these calls are made often, then it pays off having the data in both places. Indeed this "denormalises" your data and you will have to make sure that when you insert, update, and delete the data you do so in both places, but it's a far more scalable solution if you're making both types of queries often.
Embedding the Whole Item Document inside the User Document
{
"_id": ObjectId("56dba03438e1a255b97e82b6"),
"name": "john",
"age": 25,
"selectedItem": {
"name": "pencil"
}
}
Following the OP's comment, I'll address this scenario too. This is also a possible solution and can be very useful in simplifying the query needed to access the data. Just by querying the user document, you will be able to access what item he/she has selected without the extra query to a collection of items. The limitation of this approach is that if for whatever reason you want to update the name of the item from say "pencil" to "Pencil", you will have to ensure that you update it across all of the user documents, otherwise your data will be inconsistent. This gets more complicated when your embedded documents are more complex. Nonetheless, it is a widely used solution. If you're rarely updating your data, but reading it very often, especially if you are more interested in seeing the item picked by a given user, then it definitely speeds up your most frequent data access patterns.
You are right, only you need populate the userid to get all atributes of that collection. I suggest you use (if your are not) Mongoose
With mongoose:
UserItems
.find({'itemid' : '_id'})
.populate('userid')
.then( useritems => {
// here you have all users with their data for a specific item
return res.json(useritems);
});
You can add an array in the item document that keeps track of the IDs of the users who clicked that item.
This is assuming the ID is stored in a active session.
docs.user_who_clicked.push(req.user._id);
docs.save()
I wouldn't create a separate collection just for that unless you have a good reason. Just add a selectedBy to each Item document. Also, I find it simpler to just use my own unique names or IDs rather than looking things up with the internal Mongo IDs. Something like this:
var items = db.collection('items');
items.updateOne({itemname:'nuts'},{$push:{selectedBy:'johns'}});
//...
items.find({itemname:'nuts'}).toArray(function(err,items) {
console.log(items[0].selectedBy);
db.close();
});

Categories