2

I'm building an activity log of users actions. But the main objective is to get all activities not per user, but per page (scope) its inside. I will explain more after.

The main thing I'm concern about and making this question is to avoid joins to perform this and walk to the right way with scalability

The well known designs approaches for activity logs:

So, to get all user activities its all about fetching the table with the column "user_id = ?". Ok.

Well. Now suppose my app has many pages about celebrities. User can add topics, questions (inside topics) into a page. Also change page's name, photo etc.

An hypothetical example of a page: /stevejobs

Well, the activities are only relevant when grouped by the /stevejobs page, because they will need appear inside the page:


Page: Stevejobs

Actions:

  • User 1 add a question "When were Steve Jobs born?" on topic "About"
  • User 2 edited page name to "Steve Jobs"
  • User 1 added a topic "Life"

So far each activity has the object related to the activity and the parent object. E.g:

When add question: {object_type: Question, object_id: 1, parent_type: Topic, parent_id: 1}

When add topic: {object_type: Topic, object_id: 1, parent_type: Page, parent_id: 1}

Then the column parent can have many levels of depth until the page id to join if I want to get all activities that happened in /stevejobs

One solution is to add 2 extra polymorphic columns named "scope_type" and "scope_id".

When add question: {object_type: Question, object_id: 1, parent_type: Topic, parent_id: 1, scope_type: Page, scope_id: 1}

When add topic: {object_type: Topic, object_id: 1, parent_type: Page, parent_id: 1, scope_type: Page, scope_id: 1}

So its possible to get all activities per page more easily, I think

What can I deal with it?

The joins that I'm doing..

# All activities from Topics of page
list_activities = Activity.joins("JOIN lists ON activities.item_id = topics.id and activities.item_type = 'Topic'")
                          .joins("JOIN pages ON pages.id = topics.page_id")
                          .where("pages.id = #{self.id}")

# All activities from Question of Topic of Page
document_activities = Activity.joins("JOIN documents ON activities.item_id = questions.id and activities.item_type = 'Question'")
                              .joins("JOIN topics ON topics.id = questions.topic_id")
                              .joins("JOIN pages ON pages.id = topics.page_id")
                              .where("pages.id = #{self.id}")

And then make a union. Too ugly. And in the end the serialized columns too avoid joins are useless

Community
  • 1
  • 1
Luccas
  • 4,078
  • 6
  • 42
  • 72
  • You second solution seems to do what you want. What's wrong with it? Also, is it necessary to store the `scope_type`? That will always be `Page`, correct? – cdesrosiers Oct 15 '12 at 15:43
  • Thanks. I don't know what is wrong exactly. Its just appears to have too many columns and I haven't saw no one doing that. Just want some ideas. – Luccas Oct 15 '12 at 16:01
  • How do your models look and their associations? I was going to answer, but I think it would be good to know that first. Please add the relevant associations to your question. – GorrillaMcD Oct 15 '12 at 17:27
  • @GorrillaMcD Well, my models are just normal...For this example a Page has_many topics and a Topic has_many_questions. – Luccas Oct 16 '12 at 14:57
  • What version of rails are you using. Rails3 or 2? – GorrillaMcD Oct 16 '12 at 15:56

0 Answers0