12/08/2018, 14:38

Using PostgreSQL and jsonb with Ruby on Rails

Trong một ứng dụng Rails, chúng ta thường xuyên có các chức năng thiết lập riêng của từng ngường dùng ví dụ city, tài khoản mạng xã hội, blog, theme v.v... Hiện nay, đã có rất nhiều phương pháp để thực hiện lưu trữ những thông tin này vào trong cơ sở dữ liệu quan hệ. Phương pháp phổ biến nhất mà ...

Trong một ứng dụng Rails, chúng ta thường xuyên có các chức năng thiết lập riêng của từng ngường dùng ví dụ city, tài khoản mạng xã hội, blog, theme v.v... Hiện nay, đã có rất nhiều phương pháp để thực hiện lưu trữ những thông tin này vào trong cơ sở dữ liệu quan hệ. Phương pháp phổ biến nhất mà mọi lập trình viên đều biết đó là ứng với mỗi thiết lập sẽ tạo một cột trong bảng User của cơ sở dữ liệu hay thậm chí là có một bảng riêng để lưu trữ thông tin trên... Tuy nhiên, với việc phiên bản Postgres 9.4 bổ sung thêm kiểu dữ liệu jsonb, ta có thêm một phương pháp khác đó là lưu trữ tất cả các thông tin user setting chỉ vào một cột trong bảng. Kết hợp với việc sử dụng gem Storext, ta sẽ có được một cách lưu trữ dữ liệu thiết lập của người dùng một cách đơn giản. Trong bài viết này, tôi xin trình bày về cách thực hiện của phương pháp này

  • Postgres 9.4+
  • Rails 4.x+

Ta tạo một cột có tên là preferences có kiểu là jsonb trong bảng users bằng một trong hai cách sau:

# db/migrate/*_create_users.rb
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.text :name, null: false
      t.jsonb :preferences, null: false, default: '{}'
    end

    add_index  :users, :preferences, using: :gin
  end
end

# db/migrate/*_add_jsonb_column_to_users.rb
class AddJsonbColumnToUsers < ActiveRecord::Migration
  def change
    add_column :users, :preferences, :jsonb, null: false, default: {}
    add_index  :users, :preferences, using: :gin
  end
end

Sau đó chúng ta phải chạy lệnh rake db:migrate để có thể tạo cột trong cơ sở dữ liệu

Đến đây, cột preferences của bạn sẽ lưu trữ dữ liệu giống json trong Postgres, nhưng chúng ta muốn cột này được xử lý giống như những cột bình thường khác (integer, text) trong ActiveRecord::Base thì gem storetext là một lựa chọn trên cả tuyệt vời. Việc cài đặt gem này hết sức đơn giản. Ta chỉ cần add gem 'storext' vào Gemfile rồi chạy bundle install.

Đây là một phần thú vị, tất cả mọi cấu hình đều được định nghĩa qua phương thức store_attributes của gem Storext. Để cấu hình cho cột preferences, ta có thể thực hiện giống như sau:

class User < ActiveRecord::Base
  include Storext.model
  # "preferences" matches what we named the database column
  store_attributes : preferences do
    twitter String
    github String
    blog String
    city String
    newsletter Boolean, default: true
  end
end

Đến đây, ta có thể sử dụng các biến twitter, blog ... như một attribute bình thường trong ActiveRecord::Base của Rails. Ví dụ:

User.new blog: "http://example.com", github: "luongvietdung"

Một điều hết sức thú vị khác đó là chúng ta vẫn sử dụng được các validate chuẩn của Rails với các attributes blog, github ... ngoại trừ validate unique. Điều đó có nghĩa là ta hoàn toàn có thể khai báo như sau trong model User:

class User < ActiveRecord::Base
[...]
validates :github, presence: true
[...]
end

Một trong những tác vụ quan trọng nhất của cơ sở dữ liệu là truy vấn. Vậy đối với kiểu jsonb thì ta sẽ phải thực hện truy vấn ra sao?. Các bạn không nên lo lắng, PostgreSQL đã cung cấp một số toán tử cơ bản phục vụ cho việc viết truy vấn tại đây. Sau đây tôi xin đưa ra một vài ví dụ truy vấn đơn giản đối với cột jsonb :

  1. Lấy danh sách những user có newsletter = true:
User.where('preferences @> ?', {newsletter: true}.to_json)
  1. Lấy danh sách những user đã có thông tin tài khoản Github và Twitter
User.where('preferences ?& array[:keys]', keys: ['twitter', 'github'])
  1. Lấy danh sách những user đã có thông tin tài khoản Github hoặc Twitter
User.where('preferences ?| array[:keys]', keys: ['twitter', 'github'])
  1. Lấy danh sách những user sống tại thành phố Hà Nội
User.where('preferences @> ?', {city: 'Hà Nội'}.to_json)

Như vậy, trên đây tôi đã giới thiệu một kiểu dữ liệu đặc biệt trong PostgreSQL 9.4+ đó là jsonb để sử dụng trong trường hợp lưu trữ user settings vào cùng một trường. Đây có thể nói là một phương pháp đơn giản, dễ xử lý và tường minh.

0