I'm new to Firestore and trying to develop a data model for my app.
Background: I have a dating type of app with 3 primary ways that users will communicate with one another. Liking, dismissing, and commenting user profiles. Users likes & comments are private. In other words, only I can see who's liked or commented my profile (it's not like Social media where everyone can see who's liked a post). I'll need to be able to query users to know who's dismissed their profile so I won't show it to those users again. I'll also need to know who's liked/commented a users profile so I can query which users have liked/commented each other (they've matched)
Users can like many profiles and vice versa
Users can dismiss/skip many profiles and vice versa
Users can comment on many profiles and vice versa
I believe this means I'll need a root collection for likedUsers, dismissedUsers, and commentedUsers
Problem:
For dismissed users, I thought I'd store every single user as a Document of the dismissedUsers root collection and store every user they've skipped as a field/value pair like so...
dismissedUsers/User/user1, user2, user3, etc
The above would create the many-many relationship I want where dismissedUsers can have many users and users can have many dismissedUsers. However, I don't believe it would be scalable as the User Document would grow too large.
Question: How do I create this many-many relationship where dismissedUsers can have many users and users can have many dismissedUsers so that it's scalable and least expensive? And query it?
First of all I would ask myself why I am using Firestore, being a document database, instead of choosing a relational database. I personally love Firestore and highly recommend it. We pick a document database because it is faster and easier to use in many ways. In other ways it is a drawback because you have very limited query power. It sounds to me like your brain is working towards a relational database implementation.
Here is one solution
First of all I would try to avoid storing user data in more than one location to avoid anomalies (of course right). I would have one collection of users where I stored all user data with a unique id (best to use the one that Firestore assigns so I don't have collisions). Within each users document I would link a subcollection for dismissed, liked, been dismissed by someone else, liked by someone else etc.. I would keep a record of all users (just the user id) that they have dismissed, liked, been dismissed by, been liked by etc.. This way I can look up all data for who that user has liked or disliked and display whatever I want to that user accordingly.
Drawbacks
You will have to write twice per like, dismiss etc. Use a batch write to update both the liked and likee data at the same time.
You don't need a collection of users who liked, dismissed, or commented on another users profile. You can have one user collection which stores all users. Inside each user document you can have three array of the user ids of the users that liked, commented, and/or dismissed a user profile. Just make sure that the document ID of the documents inside the users collection matches the user id of the corresponding user.
Related
This is how my Firestore DB is structured
For example, now I want userA to share the Recipe2 with userB.
I want to list the Recipe2 in UserB UI as well when I fetch his/her recipes.
The way I am fetching the recipes in my Swift code is that a user can only view his/her docs or collections, i.e. userB can never query/read the userA's recipes.
Keeping in view that I might allow userB to modify shared recipes in future, how can I achieve sharing feature? I don't want to create multiple copies, my Firestore rules are set to not allow any user to read other user's data, with all this what is best possible solution?
There are a bunch of ways to do it handle this, and providing the best possible solution would be impossible without understanding the entire use case.
Let me suggest this:
When you have a situation where there's my data, your data and OUR data, it's often best to put OUR data in a separate collection, using uid's and rules to determine who can access what data. For example
users
UserA
...
UserB
...
recipes
recipe0
owner: UserA
name: "pasta fazul"
ingredients
...
visible_to
UserB
Then a rule that would allow a user to read recipes where their uid is listed in visible_to, and only the owner matching the owner uid can make changes.
Like I said in the comments, you must either grant users access to each other's documents or denormalize the documents themselves. Denormalizing data in NoSQL is not only standard operating procedure but a highly-effective optimization tool and one that should be used when needed.
Firstly, Firestore documents cannot be larger than 1 MB which means that with a relatively small userbase you can opt for a different architecture (that would save you money on reads and writes). However, I would advise against architecting data that doesn't scale with the userbase because userbase size can be the most difficult metric to predict. This solution can scale with a userbase of practically any size. Secondly, this solution is designed so that when the owner of a recipe modifies that recipe, their shared instances can easily be found and also be updated (if needed). Thirdly, this solution is designed so that when a user who is displaying a shared recipe modifies that recipe, the original author can easily be found and that recipe can be updated or notated (if needed). Finally, this solution makes displaying a user's recipes (that the user both owns and borrows) possible with just two simple queries that don't require accessing another user's data.
[collection]
<document>
[users]
<userA>
[my-recipes]
<recipe1>
<recipe2>
<recipe3>
[sharing-with]
<userB> (acts as a bookmark with the user's info)
<userB>
[my-recipes]
<recipe4>
<recipe5>
<recipe6>
[shared-recipes]
<recipe3> (contains an owner field)
I am using three models:
- users
- events
- user_events (the join table)
I feel like I do not need to save every search result because it would be unnecessary. The search will query two APIs and display maybe the top 10 results and display them to the user. The user then might be interested in one or two of these events and add them to a list of things that interest them, a bookmark, if you may. Bookmarks that are happening on the same day should then be grouped together for the users organizational purposes.
Should I make another search model along with a search controller? I'm fairly new to Rails and need some advice on this topic.
I would advise you make a bookmark or search class to store a search when a user wants to save it. Then you can construct a has_many relationship between a user and a bookmark. So a user will have many bookmarks. See this for more documentation: http://guides.rubyonrails.org/association_basics.html#the-has-many-association
I am creating a photo sharing app for the iPhone using Parse.com and so far everything has been pretty easy and I just finished writing the code for uploading photos to the Parse server.
So far, I have been using the default "_User" class. Here are some of the key types of data that are being stored for each user: objectId, username, email, and an array object called "friends" that contains the usernames of other users in the database that the user has added to their friends list.
Now, I need to setup the actual sharing functionality between users. I have been going through the Parse documentation and I need to decide whether to use a one-to-many, many-to-many, or one-to-one relationship. Depending on which one I end up choosing, I then will need to decide between using pointers, arrays, parse relations, or join tables.
I am going to quickly explain the "sharing" functionality that I am trying to achieve:
For my photo app, user1 starts the "relationship" by taking a photo and choosing to send it to a friend on their friends list named user2 .
The photo is then uploaded to Parse and sent to user2.
user2 receives the photo from user1.
user2 opens the photo and it is their job to "finish" the "relationship" by using the app's editing tools and drawing something random on the original photo. Example: user2 draws a heart next to user1's face in the photo.
When user2 is finished, the final photo is saved to their profile and also sent back to the original sender, user1, and the "relationship" is now finished and completed/closed.
After reading through the docs and doing my own research, I have come to the conclusion that I need to use a Many-To-Many relationship using Join Tables so that I can include metadata.
However, I am new to objective-c and programming in general and it would be lazy of me if I did not ask for input and advice before getting started when it is possible that I have chosen the wrong solution or an unnecessary solution.
Your question is more about your DB management than anything to do with Obj-C.
Think about using a new class in your DB called SharedImage which hosts the image(s), a link to the originator and the recipient (1 to 1, so could us a pointer or a relationship) and any other data you need.
Now you can create these objects are you require and you can fetch them based on queries using the current user.
Also, for your existing friends array which you said holds the usernames of the friends - it would probably be better to store the objectIds of the friends instead of the usernames (because the username can usually be changed, but the objectId is static).
Overview
I'm creating a Ruby on Rails website which uses Facebook to login.
For each user I have a database entry which stores their Facebook User ID along with other basic information.
I'm also using the Koala gem in order to retrieve a user's friendlist from Facebook, but I'm unsure as to how I should store this data...
Option 1
I could store the user's friends as a serialized hash in the User table, then if I wanted to display a list of all the current user's friends, I could grab this hash and do something along the lines of SELECT FROM Users WHERE facebook_user_id IN hash
Each time the user logs in I could update this field to store the latest friends list.
Option 2
I could create a Friend table and store friendship information in here, where a User has many Friends. So there would be a row for each friendship, (User1 and User2 columns). Then to display a list of the current user's friends I could do something like SELECT User2 FROM Friends WHERE User1 = current_user
This seems like the better option to me, but...
It has the disadvantage that there would be many rows... If there were 100,000 users, each with 100 friends, that's now 10,000,000 rows in the Friends table.
It also means each time the user logs in, I'd need to loop over their Facebook friends list returned using Koala and create a Friend record if someone on their friendlist is in my User table and there isn't a corresponding entry in the Friends table. This seems like it'd be slow if a user has 1000 Facebook friends?
I'd appreciate any guidance on how it would be best to achieve this.
Apologies for the badly worded question, I'll try and reword/organise it shortly.
Thanks for any help in advance.
If you need to store a lot of data, then you need to store a lot of data. If you are like most, you probably won't run into that problem sooner than you have the cash to solve it. In other words, you are probably assuming you'll have more traffic and data than you'll get, at least in the short-term. So I doubt this is an issue, even though it is a good sign that you are thinking about it now rather than later.
As I mentioned in my comment below, the easiest solution is to have a tie table with a row for each side of the friend relationship (a has_many :friends, through: :facebook_friend_relationships, class_name: 'FacebookFriend' on FacebookFriend, per the design mentioned below). But your question seemed to be about how to reduce the number of records, so that is what the remainder of the answer will address.
If you have to store in the DB and you know for sure that you will absolutely have every FB user on the planet hitting your site because it is so awesome, but they won't all hit at once, then if you are limited in storage, you may want to use a LRU algorithm (remove the least recently used records) possibly with timed expiration also. You could just have a cron job that does a query on the DB then deletes old/unused records to do this. Wouldn't be perfect, but it would be a simple solution.
You could also archive older data rather than throw it away. So, frequently used data could stay in the table of active users, and then you might offload older data to another table or even another database (and you might see the apartment and second_base gems for that). However, once you get to the size, you're probably looking at a number of other architectural solutions that have much less to do with ActiveRecord models/associations or schema design. Though it pays to plan ahead, I wouldn't worry about that excessively until you are sure that the application will get enough users to invest the time in that.
Even though ActiveRecord has some caching, you could just avoid the DB and cache friends in memory yourself in the beginning for speed, especially if you don't yet have many users, which you probably don't yet. If you think you'll run out of memory because of the high number of users, LRU might be a good option here also, and lru_redux looks interesting. Again, you might want to time the cache also so expires and re-gets friends when the cache expires. Even just storing the results in the user session may be adequate, i.e. in the controller action method, just do #friends ||= Something.find_friends(fb_user_id), and the latter is what most might do as a first shot at it while you're getting started.
If you use ActiveRecord, in your query in the controller (or on the association in the model) consider using include: to avoid n+1 queries. That will speed up things.
For the schema design, maybe:
User - users table with email and authN info. Look at the Devise gem.
FacebookUser - info about the Facebook user.
FacebookFriendRelationship - a tie model with (id and) two columns, one for one FacebookUser id and one for the other.
By separating the authN info (User) from the FB data (FacebookUser and FacebookFriendRelationship), you make it easier to have other social media accounts, etc. each with information specific to those accounts in other tables.
The complexity comes in FacebookUser's relationship with friends if the goal is to minimize rows in the relationship table. To half the number of rows, you'd have a single row for a relationship where the id of FacebookUser could be in either foreign key column. Either the user has a friend or is a friend, so you could have two has_many :through associations on FacebookFriend that each use a different foreign key in FacebookFriendRelationship. Or you could do HABTM without the model and use foreign_key and association_foreign_key options in each association. Either way, you could add a method to add both associations together (because they are arrays). Instead, you could use custom SQL in a single has_many if you didn't care about having to use ActiveRecord to remove associations the normal way. However, per your comments, I think you want to avoid this complexity, and I agree with you, unless you really must limit the number of relationship rows. However, it isn't the number of tie table rows that will eat the data, it is going to be all of the user info you keep in the FacebookFriends table.
I'm learning Rails by building a simple site where users can create articles and comment on those articles. I have a view which lists a user's most recent articles and comments. Now I'd like to add user 'profiles' where users can enter information like their location, age and a short biography. I'm wondering if this profile should be a separate model/resource (I already have quite a lot of fields in my user model because I'm using Authlogic and most of it's optional fields).
What are the pros and cons of using a separate resource?
I'd recommend keeping profile columns in the User model for clarity and simplicity. If you find that you're only using certain fields, only select the columns you need using :select.
If you later find that you need a separate table for some reason (e.g. one user can have multiple profiles) it shouldn't be a lot of work to split them out.
I've made the mistake of having two tables and it didn't buy me anything but additional complexity.
Pros: It simplifies each model
Cons: Managing 2 at once is slightly harder
It basically comes down to how big the user and profile are. If the user is 5 fields, and the profile 3, there is no point. But if the user is 12 fields, and the profile 20, then you definitely should.
I think you'd be best served putting in a separate model. Think about how the models correspond to database tables, and then how you read those for the various use cases your app supports.
If a user only dips in to his actual profile once in a while but the User model is accessed frequently, you should definitely make it a separate object with a one-to-one relationship. If the profile data is needed every time the User data is needed, you might want to stick them in the same table.
Maybe the location is needed every time you display the user (say on a comment they left), but the biography should be a different model? You'll have to figure out the right breakdown, but the general rule is to structure things so you don't have to pull data that isn't being used right away.
A user "owns" various resources on your site, such as comments, etc. If you separate the profile from the user then it's just one more resource. The user is static, while the profile will change from time to time.
Separating it out would also allow you to easily maintain a profile history.
I would keep it separate. Not all your users would want to fill out a profile, so those would be empty fields sitting in your user table. It also means you can change the profile fields without changing any of the logic of your user model.
Depends on the width of the existing user table. Databases typically havea limit to the number of bytes a recird can contain. I fyou are close to (or over which you can usually do if you have lots of fields with null values) the limit, I would add a table with a one-to-one relationship for better performance and less of a likelihood of a record that suddenly can't be inserted as there is too much data for the row size. If you are nowhere near the limit, the add to the exisiting table.