Question

Web application will serve to user different posts from different categories.
For example category may be "Humor", "Sport", etc.

So for this i will have simple table : Categories(id, category_name).

I want to keep track how many times user visit specific category, so next time when user logs in, his default page will be page with posts from two different categories which user visited most.

Basic idea for this(i will use Ruby on Rails), before controller serve some page, counter for that category will be incremented and saved into DB. My problem is that i dont know where to keep this counter for categories?

First idea(table Counters) :

Counters(id, category_name1, category_name2, ..., category_nameN, user_id)

where category_name is integer field represent number of user visit(to spefic category). This idea is not good because when i add new category i must alter this table to add new column also.

Second idea :

Counters(id, count, category_id, user_id)

for me better idea but still not good performance i guess, because per ONE user i will have N rows, where N is number of categories. So if i have 1000 users and 10 categories that will be over-kill ?

Was it helpful?

Solution

Use the second approach. 1000 users * 10 categories = 10000 rows, which is by no means considered "large" in the database world.

Unless your client library forces you otherwise, you should use a natural key design:

Counter(user_id, category_id, count, PRIMARY KEY (user_id, category_id))

If your DBMS supports clustering, this whole table can be physically represented as a single B-Tree, which is efficient to query, modify and cache.

That being said, are you sure you need the count for eternity? Perhaps it would be better to keep the count only for the last 30 days1? That would require: 1000 users * 10 categories * 30 days = 300000 rows, which is still not particularly "large".

Alternatively, you might run a periodic batch job that multiplies all counts by some factor less than 1 (say 0.9), which would make old visits less "important" than the new ones. You'd probably want to use some floating-point type (as opposed to integer) for the counter in that scenario.


1 Or 90 or whatever...

OTHER TIPS

For that volume I would use the second approach -

Counters(id, count, category_id, user_id)

unless you encounter performance issues and only then switch to the other approach.

You can also use a counter-cache to help with this:

http://guides.rubyonrails.org/association_basics.html#detailed-association-reference

e.g.

class Counter < ActiveRecord::Base
  belongs_to :category, dependent: :destroy,
    counter_cache: true
end

As always with Rails it's best to start on the rails before going off them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top