Gem counter_culture trong rails giúp việc counter cache và tính tổng giữa bảng liên kết

Giới thiệu

counter_culture giúp việc counter cache trong Rails app với cải tiến nhiều hơn trên counter cache chuẩn của Rails:

  • Cập nhật counter cache khi các giá trị thay đổi không chỉ lúc tạo và xóa
  • Hỗ trợ counter cache thông qua các mối quan hệ bảng nhiều cấp
  • Hỗ trợ tên cột động để có thể tách counter cache cho từng đối tượng khác nhau.
  • Có thể đếm liên tục hoặc tính tổng.

Với counter_culture đã test với Ruby 2.2.5 và 2.3.1, Rails 3.2, 4.0, 4.1, 4.2, 5.0 và 5.1.

Cài đặt

Thêm counter_culture vào trong Gemfile:

gem "counter_culture", "~> 1.0"

Sau đó chạy bundle install

Database schema

Chúng ta phải tạo các cột cần thiết cho tất cả counter cache và cũng có thể dùng counter_culture generator để tạo skeleton migration:

rails generate counter_culture Post votes_count

Sau đó trong file migration sẽ có code như dưới:

add_column :posts, :votes_count, :integer, null: false, default: 0

Chú ý: Cột phải NOT NULL và có giá trị mặc định là 0 để gem làm việc đúng. Nếu chúng ta thêm counter cache vào dữ liệu có sẵn phải thêm code trong file migration đã tạo.

Cách sử dụng

counter-cache thông thường

class Post < ApplicationRecord
  belongs_to :user
  counter_culture :user
end

class User < ApplicationRecord
  has_many :posts
end

Bây giờ model User sẽ cập nhật counter-cache trong cột posts_count của bảng users.

Tính tổng thay vì đếm

Đôi khi chúng ta muốn thực hiện tính tổng thay vì đếm trong trường hợp này đối tượng counter sẽ thay đổi giá trị theo giá trị trường cần tính tổng thay vì tăng giá trị 1 mỗi lần. Tùy chọn delta_column để xác định rằng counter sẽ thay đổi giá trị theo một trường cụ thể với đối tượng đếm. Cụ thể hơn bảng votes có một trường là point và muốn tính tổng của point cho tất cả vote trong model Post với trường vote_points:

class Vote < ApplicationRecord
  belongs_to :post
  counter_culture :post, column_name: "vote_points", delta_column: "point"
end

class Post < ApplicationRecord
  has_many :votes
end

Bây giờ model Post sẽ cập nhật counter cache với trường vote_points. Giá trị counter cache sẽ là tổng của các giá trị point với mỗi liên kết bản ghi Vote. Tùy chọn delta_column hỗ trợ tất cả cột kiểu numeric không chỉ :integer. Cụ thể :float cũng được hỗ trợ và đã được test.

Trên này là chỉ là những cách sử dụng cơ bản còn dưới đây là phần chính trong bài viết này. Hãy tưởng tượng xem có yêu cầu là hãy tính tổng point cho một user mà các user khác đã vote mà phần trên chỉ đề cư đến cách tính tổng cho mỗi post mà thôi. Trong trường hợp này counter_culture vẫn có thể hỗ trợ để biết rõ hơn bạn có thể tham khảo thêm link này.

Để thực hiện chúng ta tạo các file migration như sau:

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :posts_count, null: false, default: 0
      t.integer :post_vote_count, null: false, default: 0

      t.timestamps
    end
  end
end

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :description
      t.integer :votes_count, null: false, default: 0
      t.integer :vote_points, null: false, default: 0
      t.references :user, foreign_key: true, index: true

      t.timestamps
    end
  end
end

class CreateVotes < ActiveRecord::Migration[5.0]
  def change
    create_table :votes do |t|
      t.integer :point, default: 1
      t.references :post, foreign_key: true, index: true

      t.timestamps
    end
  end
end

Tiếp đến rake db:migrate và vào các file model sửa thành như dưới:

class Vote < ApplicationRecord
  belongs_to :post
  counter_culture :post
  counter_culture :post, column_name: "vote_points", delta_column: "point"
  counter_culture [:post, :user], column_name: "post_vote_count", delta_column: "point"
end

class Post < ApplicationRecord
  belongs_to :user
  has_many :votes

  counter_culture :user
end

class User < ApplicationRecord
  has_many :posts
end

Để tiện trong việc test data thì viết thêm một rake file để chạy bạn có thể tham khảo link này.

Đầu tiên chạy rake db:create_user_posts sẽ tạo 1 user và 10 post cho user vừa tạo.
D, [2017-08-27T17:08:05.005481 #27308] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:08:05.012802 #27308] DEBUG -- :   SQL (0.3ms)  INSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES ('Mrs. Kay Pfeffer', '2017-08-27 10:08:05', '2017-08-27 10:08:05')
D, [2017-08-27T17:08:05.013904 #27308] DEBUG -- :    (0.8ms)  COMMIT
D, [2017-08-27T17:08:05.027330 #27308] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-08-27T17:08:05.029033 #27308] DEBUG -- :   SQL (0.3ms)  INSERT INTO `posts` (`title`, `description`, `user_id`, `created_at`, `updated_at`) VALUES ('Repudiandae id debitis aspernatur et aut in quisquam eum.', 'Ut et est non eum sit. Fugit atque nihil assumenda quis. Magni est tempore consequatur facilis suscipit.', 1, '2017-08-27 10:08:05', '2017-08-27 10:08:05')
D, [2017-08-27T17:08:05.030089 #27308] DEBUG -- :   SQL (0.3ms)  UPDATE `users` SET `posts_count` = COALESCE(`posts_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:08:05.030755 #27308] DEBUG -- :    (0.3ms)  COMMIT
D, [2017-08-27T17:08:05.031517 #27308] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:08:05.032652 #27308] DEBUG -- :   SQL (0.2ms)  INSERT INTO `posts` (`title`, `description`, `user_id`, `created_at`, `updated_at`) VALUES ('Ut iure officia sit est rem tempora.', 'Harum ducimus nemo et. Quia eum repellat autem ratione quis. Quis quia laborum numquam repudiandae accusamus iure pariatur. Optio nulla voluptatem maiores.', 1, '2017-08-27 10:08:05', '2017-08-27 10:08:05')
D, [2017-08-27T17:08:05.033422 #27308] DEBUG -- :   SQL (0.2ms)  UPDATE `users` SET `posts_count` = COALESCE(`posts_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:08:05.033893 #27308] DEBUG -- :    (0.2ms)  COMMIT
D, [2017-08-27T17:08:05.034400 #27308] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:08:05.035252 #27308] DEBUG -- :   SQL (0.2ms)  INSERT INTO `posts` (`title`, `description`, `user_id`, `created_at`, `updated_at`) VALUES ('Atque ut nam est voluptates temporibus.', 'Eum sapiente exercitationem eos omnis ipsum. Voluptates veniam eos. Aliquid distinctio consequatur molestias autem. Voluptates velit qui est reiciendis ut.', 1, '2017-08-27 10:08:05', '2017-08-27 10:08:05')
D, [2017-08-27T17:08:05.035833 #27308] DEBUG -- :   SQL (0.2ms)  UPDATE `users` SET `posts_count` = COALESCE(`posts_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:08:05.036236 #27308] DEBUG -- :    (0.2ms)  COMMIT
D, [2017-08-27T17:08:05.036706 #27308] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:08:05.037493 #27308] DEBUG -- :   SQL (0.1ms)  INSERT INTO `posts` (`title`, `description`, `user_id`, `created_at`, `updated_at`) VALUES ('Nihil culpa illum repudiandae doloribus quisquam velit et.', 'Nemo consectetur accusamus unde et excepturi esse. Placeat minima debitis rerum odio quas facilis reprehenderit. Nemo nesciunt inventore totam natus dolorum qui. Odio eius voluptatum unde sit praesentium occaecati deserunt.', 1, '2017-08-27 10:08:05', '2017-08-27 10:08:05')
D, [2017-08-27T17:08:05.038040 #27308] DEBUG -- :   SQL (0.1ms)  UPDATE `users` SET `posts_count` = COALESCE(`posts_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:08:05.038547 #27308] DEBUG -- :    (0.3ms)  COMMIT
.....

Theo log cho thấy mỗi khi post được tạo sẽ update cột posts_count trong bảng user và cuối cùng thì check lại trong rails console.

irb(main):003:0> User.count
   (1.2ms)  SELECT COUNT(*) FROM `users`
=> 1
irb(main):004:0> Post.count
   (0.2ms)  SELECT COUNT(*) FROM `posts`
=> 10
irb(main):005:0> User.first
  User Load (0.3ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, name: "Mrs. Kay Pfeffer", posts_count: 10, post_vote_count: 0, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
irb(main):006:0> Post.all
  Post Load (0.4ms)  SELECT `posts`.* FROM `posts`
=> #<ActiveRecord::Relation [#<Post id: 1, title: "Repudiandae id debitis aspernatur et aut in quisqu...", description: "Ut et est non eum sit. Fugit atque nihil assumenda...", votes_count: 0, vote_points: 0, user_id: 1, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">, #<Post id: 2, title: "Ut iure officia sit est rem tempora.", description: "Harum ducimus nemo et. Quia eum repellat autem rat...", votes_count: 0, vote_points: 0, user_id: 1, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">, ...]>
Tiếp theo chạy rake db:create_vote[5] sẽ tạo 5 vote cho post có id = 1 mỗi vote 1 point
D, [2017-08-27T17:16:48.892639 #28904] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:16:48.911643 #28904] DEBUG -- :   Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.920039 #28904] DEBUG -- :   SQL (0.3ms)  INSERT INTO `votes` (`post_id`, `created_at`, `updated_at`) VALUES (1, '2017-08-27 10:16:48', '2017-08-27 10:16:48')
D, [2017-08-27T17:16:48.921062 #28904] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.921695 #28904] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.927873 #28904] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.931802 #28904] DEBUG -- :   SQL (0.3ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:16:48.932896 #28904] DEBUG -- :    (0.7ms)  COMMIT
D, [2017-08-27T17:16:48.933286 #28904] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:16:48.934277 #28904] DEBUG -- :   Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.935257 #28904] DEBUG -- :   SQL (0.2ms)  INSERT INTO `votes` (`post_id`, `created_at`, `updated_at`) VALUES (1, '2017-08-27 10:16:48', '2017-08-27 10:16:48')
D, [2017-08-27T17:16:48.935890 #28904] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.936363 #28904] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.937087 #28904] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.937579 #28904] DEBUG -- :   SQL (0.1ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:16:48.938008 #28904] DEBUG -- :    (0.2ms)  COMMIT
D, [2017-08-27T17:16:48.938262 #28904] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:16:48.938965 #28904] DEBUG -- :   Post Load (0.1ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.939807 #28904] DEBUG -- :   SQL (0.1ms)  INSERT INTO `votes` (`post_id`, `created_at`, `updated_at`) VALUES (1, '2017-08-27 10:16:48', '2017-08-27 10:16:48')
D, [2017-08-27T17:16:48.940326 #28904] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.940723 #28904] DEBUG -- :   SQL (0.1ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:16:48.941408 #28904] DEBUG -- :   User Load (0.1ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:16:48.941905 #28904] DEBUG -- :   SQL (0.1ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 1 WHERE `users`.`id` = 1
D, [2017-08-27T17:16:48.942312 #28904] DEBUG -- :    (0.2ms)  COMMIT
...
Chúng ta kiểm tra lại trong rails console thấy rằng post_vote_count trong bảng user đã update thành 5 trong khi có 5 vote mỗi vote 1 point.
irb(main):007:0> User.find(1)
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, name: "Mrs. Kay Pfeffer", posts_count: 10, post_vote_count: 5, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
irb(main):008:0> Post.find(1)
  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
=> #<Post id: 1, title: "Repudiandae id debitis aspernatur et aut in quisqu...", description: "Ut et est non eum sit. Fugit atque nihil assumenda...", votes_count: 5, vote_points: 5, user_id: 1, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
Đến đây chắc bạn vẫn không tin vì nó chỉ tăng 1 khi tạo 1 vote, để biết cụ thể hơn chúng ta sẽ chạy rake khác tạo 3 vote cho post có id=1 vơi point random từ 2-10 đó là rake db:create_post_vote_sp[3,1]
D, [2017-08-27T17:30:07.034534 #31364] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-08-27T17:30:07.053906 #31364] DEBUG -- :   Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.060036 #31364] DEBUG -- :   SQL (0.2ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (6, 1, '2017-08-27 10:30:07', '2017-08-27 10:30:07')
D, [2017-08-27T17:30:07.061167 #31364] DEBUG -- :   SQL (0.5ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.061615 #31364] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 6 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.066427 #31364] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.071232 #31364] DEBUG -- :   SQL (0.4ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 6 WHERE `users`.`id` = 1
D, [2017-08-27T17:30:07.072126 #31364] DEBUG -- :    (0.4ms)  COMMIT
D, [2017-08-27T17:30:07.072590 #31364] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:30:07.073671 #31364] DEBUG -- :   Post Load (0.3ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.074947 #31364] DEBUG -- :   SQL (0.2ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (8, 1, '2017-08-27 10:30:07', '2017-08-27 10:30:07')
D, [2017-08-27T17:30:07.075642 #31364] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.076123 #31364] DEBUG -- :   SQL (0.1ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 8 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.076874 #31364] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.077409 #31364] DEBUG -- :   SQL (0.1ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 8 WHERE `users`.`id` = 1
D, [2017-08-27T17:30:07.077867 #31364] DEBUG -- :    (0.2ms)  COMMIT
D, [2017-08-27T17:30:07.078167 #31364] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:30:07.078971 #31364] DEBUG -- :   Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.079785 #31364] DEBUG -- :   SQL (0.1ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (4, 1, '2017-08-27 10:30:07', '2017-08-27 10:30:07')
D, [2017-08-27T17:30:07.080314 #31364] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.080727 #31364] DEBUG -- :   SQL (0.1ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 4 WHERE `posts`.`id` = 1
D, [2017-08-27T17:30:07.081391 #31364] DEBUG -- :   User Load (0.1ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:30:07.081896 #31364] DEBUG -- :   SQL (0.1ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 4 WHERE `users`.`id` = 1
D, [2017-08-27T17:30:07.082307 #31364] DEBUG -- :    (0.2ms)  COMMIT
Ở đây cả bảng users và posts được cập nhật counter cache hãy xem cụ thể dưới
Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
SQL (0.2ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (6, 1, '2017-08-27 10:30:07', '2017-08-27 10:30:07')
SQL (0.5ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 1
SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 6 WHERE `posts`.`id` = 1
User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
SQL (0.4ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 6 WHERE `users`.`id` = 1
irb(main):010:0> User.find(1)
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, name: "Mrs. Kay Pfeffer", posts_count: 10, post_vote_count: 23, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
irb(main):011:0> Post.find(1)
  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
=> #<Post id: 1, title: "Repudiandae id debitis aspernatur et aut in quisqu...", description: "Ut et est non eum sit. Fugit atque nihil assumenda...", votes_count: 8, vote_points: 23, user_id: 1, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
Chạy thêm rake db:create_post_vote_sp[3,2]
D, [2017-08-27T17:32:07.352387 #1512] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-08-27T17:32:07.395909 #1512] DEBUG -- :   Post Load (6.5ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
D, [2017-08-27T17:32:07.411572 #1512] DEBUG -- :   SQL (6.7ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (3, 2, '2017-08-28 12:50:42', '2017-08-28 12:50:42')
D, [2017-08-27T17:32:07.425396 #1512] DEBUG -- :   SQL (12.4ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.426805 #1512] DEBUG -- :   SQL (0.4ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 3 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.435669 #1512] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:32:07.439936 #1512] DEBUG -- :   SQL (0.3ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 3 WHERE `users`.`id` = 1
D, [2017-08-27T17:32:07.446417 #1512] DEBUG -- :    (6.1ms)  COMMIT
D, [2017-08-27T17:32:07.447060 #1512] DEBUG -- :    (0.1ms)  BEGIN
D, [2017-08-27T17:32:07.448370 #1512] DEBUG -- :   Post Load (0.3ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
D, [2017-08-27T17:32:07.449656 #1512] DEBUG -- :   SQL (0.2ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (8, 2, '2017-08-28 12:50:42', '2017-08-28 12:50:42')
D, [2017-08-27T17:32:07.450330 #1512] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.450851 #1512] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 8 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.451641 #1512] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:32:07.452272 #1512] DEBUG -- :   SQL (0.2ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 8 WHERE `users`.`id` = 1
D, [2017-08-27T17:32:07.457759 #1512] DEBUG -- :    (5.2ms)  COMMIT
D, [2017-08-27T17:32:07.458513 #1512] DEBUG -- :    (0.2ms)  BEGIN
D, [2017-08-27T17:32:07.460503 #1512] DEBUG -- :   Post Load (0.5ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
D, [2017-08-27T17:32:07.462103 #1512] DEBUG -- :   SQL (0.2ms)  INSERT INTO `votes` (`point`, `post_id`, `created_at`, `updated_at`) VALUES (8, 2, '2017-08-28 12:50:42', '2017-08-28 12:50:42')
D, [2017-08-27T17:32:07.463216 #1512] DEBUG -- :   SQL (0.5ms)  UPDATE `posts` SET `votes_count` = COALESCE(`votes_count`, 0) + 1 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.463838 #1512] DEBUG -- :   SQL (0.2ms)  UPDATE `posts` SET `vote_points` = COALESCE(`vote_points`, 0) + 8 WHERE `posts`.`id` = 2
D, [2017-08-27T17:32:07.464818 #1512] DEBUG -- :   User Load (0.2ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
D, [2017-08-27T17:32:07.465491 #1512] DEBUG -- :   SQL (0.2ms)  UPDATE `users` SET `post_vote_count` = COALESCE(`post_vote_count`, 0) + 8 WHERE `users`.`id` = 1
D, [2017-08-27T17:32:07.466030 #1512] DEBUG -- :    (0.3ms)  COMMIT
irb(main):001:0>User.find(1)
  User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, name: "Mrs. Kay Pfeffer", posts_count: 10, post_vote_count: 42, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">
irb(main):002:0> Post.find(2)
  Post Load (0.2ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
=> #<Post id: 2, title: "Ut iure officia sit est rem tempora.", description: "Harum ducimus nemo et. Quia eum repellat autem rat...", votes_count: 3, vote_points: 19, user_id: 1, created_at: "2017-08-27 10:08:05", updated_at: "2017-08-27 10:08:05">

Kết quả cho thấy

Post id = 1 có 8 vote và 23 point, Post id = 2 có 3 vote và 19 point, User tạo post có tổng 42 point.

Hy vọng bài viết này có thể giúp giải quyết vấn đề và cũng như tăng thêm kiến thức mới.

Tham khảo

Source code counter culture


All Rights Reserved