Blog architecture design - ruby-on-rails

I'm teaching myself rails, and want to build a blog similar to tumblr. It will have a couple different post types, such as written text, photo posts, audio posts, and video posts.
My initial thought was to have different models for each type of post, since there will be different rules for each type of post. However, I'm still learning and don't know what I don't know, so maybe there is a better way to go about things (maybe only one model for posts, and a table for post types?).
Any feedback would be appreciated.

Probably a good relational database and object oriented design would be to have one main post model, which will probably share mostly same attributes and behaviors with all the types of posts. This could even act as your "text" type posts.
This could also simplify relationships with the posts also (eg. "users has many posts" vs "users has many text posts and/or video posts and/or etc").
Then have a sort of "attachments" join table, which determines the type of attachment (so you can have multiple attachments per post):
CREATE TABLE attachments (post_id, media_type, media_id)
Then have a table and model for each type for specific behaviors and handlers for the media types.
CREATE TABLE audios (id, transcription, storage);
CREATE TABLE videos (id, location, format, storage);
This will probably require some sort of polymorphic relationship, though, which could be a debatable DB design... you'll need views and triggers to query easily and maintain integrity... but Rails handles it quite well.
The post model would have
has_many :attachments
and attachments would have
belongs_to :post
belongs_to :media, :polymorphic => true
and each of the media model would have
has_one :attachment, :as => :media
then you can access your media via
post.attachments[0].media
You can skip the attachments table and merge the attributes with the posts table if you only need one type of media per post
Sorry i keep editing, i keep thinking of more things to to say :)

Here's a couple of options that could work.
First, you could just make one Model with columns for text_content, video_link, photo_link, etc.
Then in your view, you could render the post's view to the user (probably using a partial) with a different look depending on which attributes have values.
A second option would be to make a smaller Post table that just had key information and use a series of 'has_one' relationships to the other items.
The only advantage I see to the second option is that your DB table would be smaller since you don't have to represent the null cells over and over. Unless your worried about some huge scaling issues, I'd go with the 1st option though.

Related

Postgres HStore vs HABTM

I am building an app that has and model that can be tagged with entries from another model, similar to the tagging function of Stackoverflow.
For example:
class Question < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :questions
end
I am debating between just setting up a has_and_belongs_to_many relationship with a join table, or adding the tags to a hash using Postgres' hstore feature.
Looking for anyone that has had a similar experience that can speak to performance differences, obstacles, or anything else that should persuade me one way or another.
EDIT:
I think I should also mention that this will be a API that will be using an AngularJS frontend.
You are describing the topic of a great debate:) Normalization vs denormalization. Using many to many allows you to do nice queries such as "how many people use a certain tag" in a very simple way. HStore is very nice as well but you end up with thousands of the same tags everywhere. I use both approaches in different projects but the real problem comes when you decide one day to move your database. With Hstore you will be stuck to postgresql or have to rewrite your code. If super high speed is important as well as querying different ways and you often want to load a user record in one fellow swoop as fast as possible and show all used tags I normally do both: create a many to many relationship as tags are normally also connected to more objects like user has many tags from tags table and tags are connected to let's say brands which are connected to products and so on.
Then I create an additional field with hstore or json objects on the user table which adds every tag or removes it when the many to many relationship is destroyed.
To give you an example: in on of my projects I have companies (almost 10 million) who are interested in certain keywords and their ranking on google. This table has millions of rows but connected only to 2 million keywords which are connected to search results. This way I can quickly query which result is searched for by how many people and who they are.
If a customer opens their key word search page I load their keywords from a text column with json which is faster than going through the table.

Active Record: Parent Model w/ Multiple Child Model Types

I am trying to understand the best way to model some data for a rails app using active record.
Stack
Rails 4.1.0.beta1 & Ruby 2.0 & Postgres
Here is my scenario. I have 3 "Content Types" of Article, Video and Graphic. My goal is to be able to ask Active Record for all the Contents I have published and that are the newest ones in the Database. Such as give me the last 10 Content Items no matter what 'type' to display in an efficient way.
Here is my initial thoughts:
Content - has_many: articles, videos, graphics
published_date:datetime
Article - belongs_to: content
Video - belongs_to: content
Graphic - belongs_to: content
I am guessing its something like this but I am not sure if this is the most efficient way. All Articles, Videos and Graphics would inherit attributes from the Content model. But each Article, Video and Graphic would have their own attributes.
What is the best way to organize this data for speed and growth if a new Content Type is introduced? Thanks in advanced.
Firstly following the model relationships you laid out in your "Here is my initial thoughts":
If you were to add a new Content Type down the road I believe it should be as simple as adding the correct has_many and belongs_to tags in the model headers. Your Content model could have as many has_many's as you wanted aka as many content types as you want.
To retrieve the newest 10 content items you could do a join on the three content type tables that match the content id grabbing the 10 newest items in each table, then sort that in reverse chronological order and grab the first 10 items.

Best way to structure database objects with multiple features and attributes in Rails

I have a product model. Each product has a different feature set, and has many features.
Instead of creating a product model that lists all of it's features (since this would involve including a lot of features I do not need, and when I needed to add new features it would be difficult) my thought was to create one column that stores a "features array". So, be it this product is a laptop, and I wanted to know the screen size, I could call:
#laptop.features[:screen]
=> "15.6 inch"
The problem with this that I am not sure there is a simple and practical way to build a form that could accept various features, then map them to the array.
I found a railscast (#196) that explains there is accepts_nested_attributes_for built into rails that would basically have using both a Product model and Feature model and just associate the two records.
Which way would be better? Is there a common approach for this sort of problem? And is there a way to have a form in your view that would accept features? (even if they are not directly apart of the Product model's database structure)
I would definitely go with a more flexible solution of having a has_many relationship with features. Then you can easily call #product.features to get the products features and the flexibility really shines when you want to do something like assign multiple attributes to screen. If you are throwing hashes into your database you wouldn't be able to add two attributes (easily anyways) to screen.
Say you wanted #product.features[:screen] to show IPS of TFT in the future as well as size, then you would have to have nested hashes or something else that would be really ugly to process.
Perhaps a Features table that contains the features that you want to mention for the products, probably with a type attribute. Maybe "Type: Display, Key: Size, Value: 1920x1080", or "Type: HDD, Key: Capacity, Value: 2GB". You can use the type to create 'families' of keys. You can make your keys anything you want to track, and the value is just a string.
With that Feature list built, you create a joining table/model (assignments?) that tracks which product has which features.
Product (id, non-feature attributes)
has_many assignments
has_many features, through assignments
Feature (id, type, key, value)
has_many assignments
has_many products, through assignments
Assignments (id, product_id, feature_id, timestamps?)
belongs_to product
belongs_to feature
Given that you're linking a product id to a feature id, you can fiddle with your feature value text without breaking anything. Decide that "1920x1080" should be "1920px x 1080px" -- just change the feature record.

Rails Order by Contained Objects 2 Levels Deep?

How can I include ordering in an 'order' ActiveRelation call that's more than one level deep?
That is, I understand the answer when it's only one level deep (asked and answered at Rails order by associated data). However, I have a case where the data on which I want to sort is two levels deep.
Specifically, in my schema a SongbookEntry contains a Recording, which contains an Artist and a Song. I want to be able to sort SongbookEntry lists by song title.
I can go one level deep and sort Recordings by song title:
#recordings = Recording.includes(:song).order('songs.title')
...but don't know how to go two levels deep. In addition, it would be great if I could sort on the recording (that is, the song title and the artist name) -- is this possible without descending into SQL?
Thanks for any help,
Keith
If you model the association between SongbookEntry and Song as such:
class SongbookEntry < ActiveRecord::Base
# ...
has_one :song, through: :recording
end
you will be able to access #songbookentry.song and SongbookEntry.joins(:song) using your existing schema.
Edit:
Applying the same idea for Artist, a possible query would be:
SongbookEntry.joins(:song,:artist).order('songs.title','artists.name')
Note that this may not be the most efficient operation (multiple joins involved) even though it looks Rails-ish, so later on you may want to denormalize the tables as Ryan suggested, or find another way to model the data.
I would advise storing the artist name (and possibly the song title too) on the recording itself, so you don't have to "descend into SQL".
Try this
SongbookEntry.includes(:recording=>[:artist,:song]).order('songs.title, artists.name')
You can use joins in place of includes if you don't want to use associated tables fields in views

How to create a (high performance) activity feed from several models?

In a Rails app I want to combine several models into a single activity feed.
For example, I have Post, Comment, Photo, Status models. Each one belongs to a User, an Institution, and a City. On a User/Institution/City show page I want to display a chronological list of the latest activity.
From reading around and from other questions on SO, there appear to be two schools of thought on this.
Create a polymorphic ActivityFeed model where e.g. a User has_many :posts as: feedable. Then render the ActivityFeed collection on the respective show pages.
Create and render a combined array e.g. #user_feed = #user_photos + #user_comment +..
But I haven't seen much discussing the pros and cons of each approach.
Has any one else attempted something similar? What issues should I be thinking about or planning for in choosing an approach? How well do each perform (especially as the database gets larger)?
My requirements are:
To display all items that belong to the parent model.
To have several different types of parent model (User, Institution, City, etc).
To render a different view partial for each item in the activity feed, depending on its class.
Grateful for any thoughts, personal experiences, or pointers to further information on this. Should I be looking for a third option? I'm particularly interested in the performance of creating a combined array from several queries.
Thanks
Let me add a few more issues to your requirements
ease of display all items belonging to parent model
ease of allowing for different types of parent model
ease of class-dependent template rendering
ease of refactoring shared activity functionality
ease of integrating potential activity control functionality (mark as read, subscribe, follow, share etc)
ease of calculating polymorphic feed, say in reverse chronological order of activities of various types.
ease of calculating aggregated activity count on a parent
For me 5 and 6 and 7 defo tips it in favour of the first option, my code would be:
class Activity
belongs_to :activity, polymorphic: true
belongs_to :user, counter_cache: true
belongs_to :institution, counter_cache: true
belongs_to :city, counter_cache: true
end
your scope for city newsfeed (demonstrating 1,2,6)
city.activities.order('updated_at DESC').limit(20)
note that this is a cheap search on one table only that can already give you enough for a quick list since association loading (would be .includes(:activities)) is not necessary to display something like.
Joe from NYU, New York has posted a comment
Mel from UCL, London has added a new photo
4 is trivial if activity is a separate class and 5 also smooth with standard activity controller. for shared rendering partials like the quicklist views/activities will be a natural place.
7 is easily done with the counter_cache.
as a bonus, by allowing has_many :activities on each type, you could distinguish types of activities, like create, update, read, etc.
hope this helps

Resources