Looping with Firestore querying [Web/Javascript] - javascript

How would I go about setting up a loop to query through my Firestore database structure? I don't believe I'm allowed to post pictures or links yet, so I'll try to give some path examples here. Note that there is a lot more to it than these examples.
Rating->Arrow->Arrow-Rating->(user ID)->(data fields)show_name, user_id, rating
Rating->Arrow->Arrow-Rating->(different user ID)->(data fields)show_name, user_id, rating
......
Rating->Flash->Flash-Rating->(user ID)->((data fields)show_name, user_id, rating<br />
......
To give a quick rundown of what this is all about, I'm trying to develop a mock site (with functionality) for a project, where I can display user created information (user info is created from a concurrently developed Android application) such as ratings.
Basically what I'm trying to do is get and display the average rating for each show, by getting each 'rating' field from each user in the respective "Rating" paths... i.e., getting a user rating from each show (Show-Rating->User ID/(data fields)), going to the next user from the same show, and then going to the next show and doing the same.
I'm just not entirely sure how to start going about this.

I suggest you use a cloud function that listens for changes in the ratings node. Use OnUpdate listener
Store every movie rating in its own node, and only query that node for the rating
This will reduce a lot of computation on the client side and will also be more efficient
Use this guide that describes a distributed counter

Related

Firebase >> User Favorites List

I need a little assistance with my app. I'm not asking for a handout, just some guidance as to where to begin. The basic idea is for logged in users to be able to favorite videos, and to have them persist when logged in.
My app uses the YouTube API (Playlist Items) to display videos from my public playlists within my app. I'm using firebase auth to register and login users, but I have yet to implement the RTD. This is where I need some assistance in structuring my data & organizing my app.
I don't want favorite lists created for every user by default, nor do I want to store false values and have to loop through them. I'd only like to set a favorites list if the user requests to do so, and the values are true. I'm open to suggestions regarding structuring my data, but I was thinking something simple like this:
"favorites": {
"John Doe": {
"video1ID": true,
"video2ID": true,
}
}
Videos are contained within cards using a .each function from within the API response. Included in these cards are "favorite" toggle switches that I'd like a user to be able to toggle and add a favorite video to their list.
YouTube provides Video ID's from within their JSON response. I was thinking that assigning a boolean to that video ID would get the job done, but I have no idea where to begin.
Something like:
function writeFavoritesList (name, videoID, toggleValue) {
firebase.database().ref('favorites/' + userId).set({
name: displayName,
videoID: videoID,
toggleValue: true
});
}
I'm very much a newb to anything outside of WordPress, so I hope I'm on the right track. Any help appreciated. Thanks! :)
Looks great. If this were another database, you could consider storing the video IDs in an array, but this being the firebase RealTime Database, you're much better off with objects, which you've already got.
You could modify your structure slightly to take advantage of RTDs push() key generation if you ever intend on sorting your favourite videos. To do so, instead of making the key the videoID and the value the boolean status, you could generate a key using firebase's push() key generation and make the value the videoID. "The unique key generated by push() are ordered by the current time, so the resulting list of items will be chronologically sorted. The keys are also designed to be unguessable (they contain 72 random bits of entropy)."
"favorites": {
"uid1": {
"uniqueKey1": videoID1,
"uniqueKey2": videoID2,
}
}
To generate a push() key, use: const key = firebase.database().ref().push().key.
More info: https://firebase.google.com/docs/reference/js/firebase.database.Reference#push
Saw your Guru post. I think the best way for you to learn is to delve deep into the documentation and figure this out for yourself. If you're truly committed to learning this stuff you'd be doing yourself a disservice to have someone else write the code for you.
I'd start with the GCP(Google Cloud Platform) cloud firestore docs and read through the Concepts section in its entirety:
https://cloud.google.com/firestore/docs/concepts
The firebase site mirrors parts of the GCP documenation, but also covers client implementations:
https://firebase.google.com/docs/firestore/
To get the most out of these docs use the nav sidebar on the left to drill down into all the various Cloud Firestore topics. They go into how to structure your database and provide sample code for you to analyse and play with.
You'll see the terms Documents and Collections thrown around a lot. A Document is somewhat equivalent to a JSON Object. A Collection is a list of documents; similar to an array of JSON objects. But here's where things get interesting; Documents can reference Collections (aka Subcollections):
So I would structure your database as follows:
Create a Users collection
Whenever a new user signs into your app, create a user document and add it to the Users collection.
The first time a user selects a favorite video create a Favorites collection and add it to the user document; then add favorite documents to the Favorites collection for this user
There is a Javascript/Web client (you've seem to already have it loaded from what I've seen in the repo link you provided on Guru). Here's the reference documentation for it:
https://firebase.google.com/docs/reference/js/firebase.firestore
The classes, methods and properties defined in those reference docs are what you'll be calling from within your jquery code blocks.
Good luck and stick with it.

Query and Update deep child - Firebase

I am currently stuck on how to query for deep nested child and then removing those children. A little bit of background:
In my app, users post jobs. The newly created job is posted to the jobs creator under: jobs/serviceUsers/activeJobs/usersUID
and also to the workers under: jobs/workerUsers/workersUID
Now posting the jobs is no problem, but now I am stuck on what if the user wants to delete it? I have to delete it under serviceUsers (which is no problem) but also delete the job within workerUsers (Keep in mind there will be many workerUsers with the same jobID). So I need to query, find and delete those jobs.
I currently have this code to find the workers and expected it to work but nothing happened:
// jobID is the job to be deleted
let queryRef = firebase.database().ref(`/jobs/workerUsers`);
queryRef.orderByChild(jobID).equalTo(true).once('value', snap => {
console.log('successful Query: ', snap.val);
});
Any help is appreciated!
(Note: this is my first time working with firebase)
Based on your comments what I understand is: when a user deletes a job, you want to delete it from ALL worker users. If that is the case you will need to add 1 more node under jobs, something like jobs/jobWorkers which will have worker IDs mapped to job ID (like Haressh has suggested).
So your final structure will be like:
jobs/serviceUsers/activeJobs/usersUID/jobId/{job details} - listing of active job (if I understand correctly it will have the jobs details created by specific user)
jobs/workerUsers/workersUID/jobId - this will map different jobs a specific user (worker) is working on (No job details stored here)
jobs/jobWorkers/jobId/workersUID - this will map different workers working on a specific job (No job details stored here)
When a user tries to delete a job (created by him) first get the workers working on that job from jobs/jobWorkers/jobId, iterate on those worker IDs and remove the job ID from jobs/workerUsers/<workerUID>/jobId. Then delete the complete node # jobs/jobWorkers/jobId. And then finally delete the complete node # jobs/serviceUsers/activeJobs/usersUID/jobId that will delete job details as well.
Yes, there is some data duplication here. But that's normal. (Welcome to Firebase, and for that matter welcome to NoSql DB :) )
Read this if you haven't done it already: https://firebase.google.com/docs/database/ios/structure-data
I didn't get your problem correctly, from what i understood users create jobs under jobs/serviceUsers/activeJobs/usersUID which looks ok,
but when you create the same under jobs/workerUsers/workersUID you instead need something like this jobs/workerUsers/jobID/usersUID
Then you can remove it like so
firebase.database().ref("/jobs/workerUsers/"+jobID+"/"+usersUID).remove();

How could I manage permissions of jhipster's authorities?

I am coding a webapp using JHipster code generator.
I created 2 extra roles, now I have 5 in total:
ROLE_USER, ROLE_ADMIN, ROLE_ANONYMOUS, ROLE_PRESIDENT, ROLE_VICE_PRESIDENT
I was wondering how could I manage their permissions to show some RESTs.
For example, I would like to let the PRESIDENT add new users to database, other simple users should not see the web service that do the work.
Is there a file that I'm ignoring by mistake that could help me with this feature?
Giving thanks in advice for your precious time,
Manuel
Adding new roles to JHipster needs to be added in 2 places.
The obvious one is the AngularJS frontend. To add your new role to user edit view, you add them to the select options in "user-management.controller.js" at vm.authorities = ['ROLE_USER', 'ROLE_ADMIN']
To enforce the role at different places in UI, you either add your roles to the state JS files. Just add them to data.authorities (check user-management.state.js)
If you like to have a template block visible only if a user has the proper role, check out the hasAnyAuthority directive.
The other Part is to secure the backend. If using a current JHipster version and SQL database, authorities are stored in the database. Add your custom roles to authorities.csv of your liquibase migrations.
Last but not least, you can enforce roles in WebSecurityConfiguration or MicroserviceSecurityConfiguration (just look at the existing antMatchers)

Adding a user to PFRelation using Parse Cloud Code

I am using Parse.com with my iPhone app.
I ran into a problem earlier where I was trying to add the currently logged in user to another user's PFRelation key/column called "friendsRelation" which is basically the friends list.
The only problem, is that you are not allowed to save changes to any other users besides the one that is currently logged in.
I then learned, that there is a workaround you can use, using the "master key" with Parse Cloud Code.
I ended up adding the code here to my Parse Cloud Code: https://stackoverflow.com/a/18651564/3344977
This works great and I can successfully test this and add an NSString to a string column/key in the Parse database.
However, I do not know how to modify the Parse Cloud Code to let me add a user to another user's PFRelation column/key.
I have been trying everything for the past 2 hours with the above Parse Cloud Code I linked to and could not get anything to work, and then I realized that my problem is with the actual cloud code, not with how I'm trying to use it in xcode, because like I said I can get it to successfully add an NSString object for testing purposes.
My problem is that I do not know javascript and don't understand the syntax, so I don't know how to change the Cloud Code which is written in javascript.
I need to edit the Parse Cloud Code that I linked to above, which I will also paste below at the end of this question, so that I can add the currently logged in PFUser object to another user's PFRelation key/column.
The code that I would use to do this in objective-c would be:
[friendsRelation addObject:user];
So I am pretty sure it is the same as just adding an object to an array, but like I said I don't know how to modify the Parse Cloud Code because it's in javascript.
Here is the Parse Cloud Code:
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId,
newColText = request.params.newColText;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
user.set('new_col', newColText);
Parse.Cloud.useMasterKey();
user.save().then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
And then here is how I would use it in xcode using objective-c:
[PFCloud callFunction:#"editUser" withParameters:#{
#"userId": #"someuseridhere",
#"newColText": #"new text!"
}];
Now it just needs to be modified for adding the current PFUser to another user's PFRelation column/key, which I am pretty sure is technically just adding an object to an array.
This should be fairly simple for someone familiar with javascript, so I really appreciate the help.
Thank you.
I would recommend that you rethink your data model, and extract the followings out of the user table. When you plan a data model, especially for a NoSQL database, you should think about your queries first and plan your structure around that. This is especially true for mobile applications, as server connections are costly and often introduces latency issues if your app performs lots of connections.
Storing followings in the user class makes it easy to find who a person is following. But how would you solve the task of finding all users who follow YOU? You would have to check all users if you are in their followings relation. That would not be an efficient query, and it does not scale well.
When planning a social application, you should build for scalabilty. I don't know what kind of social app you are building, but imagine if the app went ballistic and became a rapidly growing success. If you didn't build for scalability, it would quickly fall apart, and you stood the chance of losing everything because the app suddenly became sluggish and therefore unusable (people have almost zero tolerance for waiting on mobile apps).
Forget all previous prioities about consistency and normalization, and design for scalability.
For storing followings and followers, use a separate "table" (Parse class) for each of those two. For each user, store an array of all usernames (or their objectId) they follow. Do the same for followers. This means that when YOU choose to follow someone, TWO tables need to be updated: you add the other user's username to the array of who you follow (in the followings table), and you also add YOUR username to the array of the other user's followers table.
Using this method, getting a list of followers and followings is extremely fast.
Have a look at this example implementation of Twitter for the Cassandra NoSQL database:
https://github.com/twissandra/twissandra

Dropping a Mongo Database Collection in Meteor

Is there any way to drop a Mongo Database Collection from within the server side JavaScript code with Meteor? (really drop the whole thing, not just Meteor.Collection.remove({}); it's contents)
In addition, is there also a way to drop a Meteor.Collection from within the server side JavaScript code without dropping the corresponding database collection?
Why do that?
Searching in the subdocuments (subdocuments of the user-document, e.g. userdoc.mailbox[12345]) with underscore or similar turns out quiet slow (e.g. for large mailboxes).
On the other hand, putting all messages (in context of the mailbox-example) of all users in one big DB and then searching* all messages for one or more particular messages turns out to be very, very slow (for many users with large mailboxes), too.
There is also the size limit for Mongo documents, so if I store all messages of a user in his/her user-document, the mailbox's maximum size is < 16 MB together with all other user-data.
So I want to have a database for each of my user to use it as a mailbox, then the maximum size for one message is 16 MB (very acceptable) and I can search a mailbox using mongo queries.
Furthemore, since I'm using Meteor, it would be nice to then have this mongo db collection be loaded as Meteor.Collection whenever a user logs in. When a user deactivates his/her account, the db should of course be dropped, if the user just logs out, only the Meteor.Collection should be dropped (and restored when he/she logs in again).
To some extent, I got this working already, each user has a own db for the mailbox, but if anybody cancels his/her account, I have to delete this particular Mongo Collection manually. Also, I have do keep all mongo db collections alive as Meteor.Collections at all times because I cannot drop them.
This is a well working server-side code snippet for one-collection-per-user mailboxes:
var mailboxes = {};
Meteor.users.find({}, {fields: {_id: 1}}).forEach(function(user) {
mailboxes[user._id] = new Meteor.Collection("Mailbox_" + user._id);
});
Meteor.publish("myMailbox", function(_query,_options) {
if (this.userId) {
return mailboxes[this.userId].find(_query, _options);
};
});
while a client just subscribes with a certain query with this piece of client-code:
myMailbox = new Meteor.Collection("Mailbox_"+Meteor.userId());
Deps.autorun(function(){
var filter=Session.get("mailboxFilter");
if(_.isObject(filter) && filter.query && filter.options)
Meteor.subscribe("myMailbox",filter.query,filter.options);
});
So if a client manipulates the session variable "mailboxFilter", the subscription is updated and the user gets a new bunch of messages in the minimongo.
It works very nice, the only thing missing is db collection dropping.
Thanks for any hint already!
*I previeously wrote "dropping" here, which was a total mistake. I meant searching.
A solution that doesn't use a private method is:
myMailbox.rawCollection().drop();
This is better in my opinion because Meteor could randomly drop or rename the private method without any warning.
You can completely drop the collection myMailbox with myMailbox._dropCollection(), directly from meteor.
I know the question is old, but it was the first hit when I searched for how to do this
Searching in the subdocuments...
Why use subdocuments? A document per user I suppose?
each message must be it's own document
That's a better way, a collection of messages, each is id'ed to the user. That way, you can filter what a user sees when doing publish subscribe.
dropping all messages in one db turns out to be very slow for many users with large mailboxes
That's because most NoSQL DBs (if not all) are geared towards read-intensive operations and not much with write-intensive. So writing (updating, inserting, removing, wiping) will take more time.
Also, some online services (I think it was Twitter or Yahoo) will tell you when deactivating the account: "Your data will be deleted within the next N days." or something that resembles that. One reason is that your data takes time to delete.
The user is leaving anyway, so you can just tell the user that your account has been deactivated, and your data will be deleted from our databases in the following days. To add to that, so you can respond to the user immediately, do the remove operation asynchronously by sending it a blank callback.

Categories

Resources