firebase database store list of items - javascript

HERE IS MY CODE
//1. Add menu items to the globalMenu and to restaurant, and the restaurantId of the restaurant adding menu.
this.writeNewMenuItem = function(itemName, restaurantId) {
// A post entry.
var menuItem = {
itemName: [itemName]
};
var ref = firebase.database().ref();
// Get a key for a new Post.
var newItem = ref
.child('globalMenu')
.push(menuItem);
ref
.child('restaurants')
.child(restaurantId)
//.child(newItem.key)
.update(menuItem);
}
Also let me know the way we can push and pop these items.
and if duplicate found this should not insert.
I want to fetch matched characters items, like if I pass 'app' in query this should return 'apple, application, appengine' etc. How could I do this?

You should try to avoid "arrays" in a Firebase Database, because:
... for distributed data, they aren't reliable because they lack a unique, permanent way to access each record.
https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html
Regarding your third question: querying the the database this way is not possible, you need to receive the whole list of all child nodes and do the filtering on the client side. I could recommend reading best practices for structuring the data: https://firebase.google.com/docs/database/admin/structure-data

Related

How to query the list in a better way using AngularFireDatabase?

"#angular/fire": "^5.2.3",
I am using AngularFireDatabase to manipulate data in firebase. I need to retrieve and update the table below in Firebase. I am currently doing it in the below method. Is there any way which I can use it to query better? Because, I am downloading the entire table and then uploading it again. I don't want to download the entire list of submissions instead just want to push an object to the submission array directly. Thanks in advance.
// Get form submissions for a specific form
getFormSubmissions(key: string) {
this.userSubmissionsList = this.db.list('/form-submissions', ref => ref.orderByChild('formId').equalTo(key));
return this.userSubmissionsList;
}
getSingleFormForSubmission(key: string) {
this.submission = this.db.object('/form-submissions/' + key);
return this.submission;
}
this.formService.getFormSubmissions(this.formId).snapshotChanges().subscribe(response => {
let key = null;
response.forEach(item => {
const a: any = item.payload.toJSON();
key = item.key;
});
this.formService.getSingleFormForSubmission(key).valueChanges().subscribe(resp => {
if (resp.submission === undefined) { resp.submission = []; }
this.formSubs = resp;
});
});
Pushing the data be like:
this.formSubs.submission.push(data);
this.formService.submitUserValues(this.formSubs);
You're storing a JavaScript array in Firebase, which unfortunately means it becomes hard to manipulate. To add an item to an array, you must know how many items already exist in that array, which requires that you first read it.
This is one of the many reasons why the Firebase documentation recommends against using arrays, but instead uses so-called push IDs to add items to lists. From that documentation:
Use the push() method to append data to a list in multiuser applications. The push() method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by push() is based on a timestamp, so list items are automatically ordered chronologically.
I also recommend checking out this old-but-still-very-true blog post Best Practices: Arrays in Firebase.

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.

Angular2/4 - Creating an array of data to share across multiple components via service

Overview:
I have a UI that allows a user to select one or more employees based on various search criteria. When they select them, I need to store the selected employees in an array, within my shared service.
Before any of this data is sent to the server, the array could be modified by adding more employees or removing some that exist in the array.
I need to be able to create and subscribe to an array of data in this shared service.
My Approach:
My initial approach was to use a BehaviorSubject so that I could call next and pass the data along when needed. This became an issue though because I didn't have a way to see all of the stored/selected users, only the last one that was passed through the BehaviorSubject.
Psuedo Code:
shared.service.ts
public selectedUsers = []; //<- How do I store stuff in here?
private selectedUsersSub = new BehaviorSubject<any>(null);
selectedUsers$ = this.selectedUsersSub.asObservable();
setSelectedUsers(data) {
this.selectedUsersSub.next(data);
}
get selectedUsers(){
return this.selectedUsers;
}
component.ts:
this._reqService.selectedUsers$.subscribe(
data => {
if (data) {
console.log('Observable Stream', data)
}
}
)
My goal here is to be able to store my selected employees in this selectedUsers array. My other components need to be able to subscribe so that they are always up-to-date with the current value of selectedUsers.
I also need to be able to access the current array of selected users at any time, not just the last value.
Delete public selectedUsers = [];
delete get selectedUsers(){
return this.selectedUsers;
}
And in any component you want to fetch the selectedUsers just subscribe to the public observable selectedUsers$
in a component
this.subscription = this.yourService.selectedUser$.subscribe((users)=>//do stuff here like push theusersto the users array of the component)
The service needs to be inject to a shared module in order all the components to get the same state (data).
More details: https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service
Your approach is wrong here. You have 2 basic options in a shared service pattern. 1 is to use a store pattern where you have a predefined set of data manipulations and use the scan operator, this is more complex, the simpler is to pass the entire list every time you want to update the list.
So your components will not only send the update, they'll first get the entire list and then manipulate and then send it.

Match value of array from database object in Firebase Cloud Functions

This is my first app project using Google Cloud Functions & Firebase. I'm trying to find away to get a single value of the array that I'm returning and compare it to a set variable and if it matches, update another child's value in that same account.
My App users can add records to the database under their login/user_id that is stored in the database. I'm trying to get a list of the "RecordName" that is a child under that login/user_id that every user has stored in their account.
So basically every "RecordName" in the entire database. When I want to run specials for those records, I need to match the name of that record to the name of the record I have on special and if there is a match, update another child value under that user's account ("special" = true.). This way, when they load their app next time, I have it highlighting that record so they know it's on special.
When I use..
const ref = admin.database().ref(`/store`);
...with the following code...
ref.on('value', function(snapshot) {
// puts ALL items of the object into array using function ..
console.log(snapshotToArray(snapshot));
});
... and the function...
function snapshotToArray(snapshot) {
var returnArr = [];
snapshot.forEach(function(childSnapshot) {
var item = childSnapshot.val();
item.key = childSnapshot.key;
returnArr.push(item);
});
return returnArr;
};
... I get the entire array just as it is in the database:
-store
-{ones_users_id}
-recordname: value1
-special: false
-{anothers_users_id}
-recordname: value2
-special: false
ect. ect.
If my record on special is called, "Newbie Record", what would be the best way to take out every individual value for the key: "recordname" from the array, compare each one to var = "Newbie Record" and if they match, update the value of the key: "special" to be true?
I'm new to JSON and NodeJS, I've been searching on here for answers and can't find exactly what I'm looking for. Your feedback would be very helpful.
It sounds like you're looking to query your database for nodes that have "recordname": "Newbie Record" and update them.
An easy way to do this:
const ref = admin.database().ref(`/store`);
const query = ref.orderByChild("recordname").equalTo("Newbie Record");
query.once('value', function(snapshot) {
snapshot.forEach(function(child) {
child.ref.update({ special: true })
});
});
Main differences with your code:
We now use a query to read just the nodes that we want to modify.
We now use once() to read the data only once.
We loop over the children of the snapshot, since a query may result in multiple nodes.
We use the reference of each child and then update its special property.
I recommend reading a bit more about Firebase queries in the documentation.

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