Nhóm điều kiện trong Ransack

Trong dự án trước của mình, mình đã gặp một trường hợp yêu cầu tìm kiếm theo điều kiện như sau:

  • trường date của bảng A (lấy phần ngày tháng) nhỏ hơn ngày tháng nào đó, hoặc
  • trường date của bảng A (lấy phần ngày tháng) bằng ngày tháng nào đó và trường time của bảng B (lấy phần giờ - phút) nhỏ hơn hoặc bằng thời gian nào đó (bảng A belongs_to B)

Đang sử dụng ransacker để trích xuất các phần ngày tháng của các trường nên mình quyết định tìm hiểu luôn xem ransack có hỗ trợ xây dựng query chứa điều kiện như yêu cầu trên không. Kết quả là mình tìm thấy trang demo của ransack. Vậy là mình thử xây dựng một truy vấn tương tự với cơ sở dữ liệu hiện có trên trang demo:

Truy vấn

Câu lệnh và kết quả truy vấn:

Câu lệnh và kết quả

Như vậy là có thể xây dựng truy vấn theo yêu cầu trên với ransack. Params truyền lên của form có dạng:

q[g][0][m]:"or"
q[g][0][c][0][a][0][name]:"first_name"
q[g][0][c][0][p]:"cont"
q[g][0][c][0][v][0][value]:"e"
q[g][0][g][0][m]:"and"
q[g][0][g][0][c][0][a][0][name]:"created_at"
q[g][0][g][0][c][0][p]:"gteq"
q[g][0][g][0][c][0][v][0][value]:"2016/08/01"
q[g][0][g][0][c][1][a][0][name]:"updated_at"
q[g][0][g][0][c][1][p]:"lteq"
q[g][0][g][0][c][1][v][0][value]:"2016/11/01"

Đổi qua dạng hash cho "dễ nhìn" thì như sau:

{
  q: {
    g: {
      "0": {
        m: "or",
        c: {
          "0": {
            a: {
              "0": {
                name: "first_name"
              }
            },
            p: "cont",
            v: {
              "0": {
                value: "e"
              }
            }
          }
        },
        g: {
          "0": {
            m: "and",
            c: {
              "0": {
                a: {
                  "0": {
                    name: "created_at"
                  },
                  p: "gteq",
                  v: {
                    "0": {
                      value: "2016/08/01"
                    }
                  }
                }
              },
              "1": {
                a: {
                  "0": {
                    name: "updated_at"
                  },
                  p: "lteq",
                  v: {
                    "0": {
                      value: "2016/11/01"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Như vậy có thể thấy key g có giá trị là một nhóm các điều kiện, key m là toán tử kết nối các điều kiện, điều kiện c chứa thuộc tính a, phép so sánh p và các giá trị v.

Đoạn params ở trên có thể viết trong một scope của bảng User để truy vấn như sau:

scope :sample_scope, ->first_name, desire_time do
  ransack(
    g: {
      "0": {
        m: "or",
        first_name_cont: first_name,
        g: {
          "0": {
            m: "and",
            created_at_gteq: desire_time,
            updated_at_lteq: desire_time
          }
        }
      }
    }
  ).result
end

như vậy là có thể tìm kiếm user theo các nhóm các điều kiện mong muốn.

Đối với yêu cầu mình nói thì scope sẽ có dạng:

scope :sample_scope, ->desire_time do
  ransack(
    g: {
      "0": {
        m: "or",
        date_lt: desire_time.to_date,
        g: {
          "0": {
            m: "and",
            date_eq: desire_time.to_date,
            b_time_lt: desire_time
          }
        }
      }
    }
  ).result
end

Đối với form trên view thì ta có thể xây dựng với dạng:

<%= hidden_field_tag "q[g][0][m]", value: "or" %>
<%= text_field_tag "q[g][0][c]", "date_lt" %>
<%= hidden_field_tag "q[g][0][g][0][m]", value: "and" %>
<%= text_field_tag "q[g][0][g][0][c][0]", "date_eq" %>
<%= text_field_tag "q[g][0][g][0][c][0]", "b_time_lt" %>

rồi trên controller ta có thể truy vấn bằng ransack với params[:q]

A.ransack(params[:q]).result

Tổng quát lại, ta có thể tạo ra truy vấn với nhiều nhóm với nhau và nhiều điều kiện với nhau:

g0:
  m1: ""
  g1:
    m: ""
    c1: ""
    c2: ""
    ...
    cn: ""
  g2:
    m2: ""
    c1: ""
    c2: ""
    ...
    cn: ""
  ...
  gn:
    mn: ""
    c1: ""
    c2: ""
    ...
    cn: ""

Kết luận

Trên đây mình đã giới thiệu về tính năng nhóm các điều kiện truy vấn của ransack, hy vọng có thể giúp ích cho mọi người, cảm ơn mọi người đã theo dõi 😃