GIỚI THIỆU VÀ SỬ DỤNG GEM META WHERE TRONG RAILS
Bài đăng này đã không được cập nhật trong 9 năm
**I. Giới thiệu Gem Meta Where **:
-
Khi xây dựng ứng dụng Rails với MySQL chắc hẳn bạn sẽ gặp một vài những khó khăn với những câu lệnh truy vấn phức tạp.
-
MetaWhere sẽ giúp các nhà phát triển thao tác với ActiveRecord khá thuận tiện và dễ dàng bằng việc sử dụng sức mạnh của Arel Predications(phương pháp so sánh).
-
MetaWhere giúp bạn xác định được những điều kiện logic boolean phức tạp, thậm chí cả trong các bảng phụ.
II. Sử dụng Meta Where trong ứng dụng Rails :
1 . ** Setup** :
- Thêm MetaWhere vào Gemfile của bạn :
gem “meta_where”
- Cài đặt Gem : ``` “bundle install” ```
2 . **Sử dụng với Rails** :
- **WHERE**
Sử dụng MetaWhere như một chuỗi phương thức thông thường :
```Ruby
Article.where(:title.matches => 'Hello%', :created_at.gt => 3.days.ago)
=> SELECT "articles".* FROM "articles" WHERE ("articles"."title" LIKE 'Hello%')
AND ("articles"."created_at" > '2010-03-26 18:39:32.592087')
- **Tìm với điều kiện HASH**
Cú pháp tương tự ActiveRecord :: Base # find:
```Ruby
Article.find(:all,
:conditions => {
:title.matches => 'Hello%',
:created_at.gt => 3.days.ago
}
)
- **SCOPE**
```Ruby
class Article
scope :recent, lambda {|v| where(:created_at.gt => v.days.ago)}
end
Article.recent(14).to_sql
=> SELECT "articles".* FROM "articles"
WHERE ("articles"."created_at" > '2015-03-26 18:54:37.030951')
-
Các Toán tử
MetaWhere hỗ trợ các toán tử mặc định như các method Arel , để sử dụng bạn cần gọi : MetaWhere.operator_overload! khi khởi tạo ứng dụng của mình.
Article.where(:created_at > 100.days.ago, :title =~ 'Hi%').to_sql
=> SELECT "articles".* FROM "articles"
WHERE ("articles"."created_at" > '2015-03-26 20:11:44.997446')
AND ("articles"."title" LIKE 'Hi%')
>> (equal)
^ (not equal)
+ (in array/range)
!~ (not matching, only available under Ruby 1.9)
> (greater than)
>= (greater than or equal to)
< (less than)
<= (less than or equal to)
-
Cấu trúc & and |
- Với các toán tử :
Article.where((:title =~ 'Hello%') | (:title =~ 'Goodbye%')).to_sql
=> SELECT "articles".* FROM "articles" WHERE
(("articles"."title" LIKE 'Hello%'
OR "articles"."title" LIKE 'Goodbye%'))```
- **Thay thế:**
Article.where(:title.matches % 'Hello%' | :title.matches % 'Goodbye%').to_sql
=> SELECT "articles".* FROM "articles" WHERE (("articles"."title" LIKE 'Hello%' OR "articles"."title" LIKE 'Goodbye%'))
- **Với Hashes :**
```Ruby
Article.where({:created_at.lt => Time.now} &
{:created_at.gt => 1.year.ago}).to_sql
=> SELECT "articles".* FROM "articles" WHERE
((("articles"."created_at" < '2015-03-27 00:26:30.629467')AND
("articles"."created_at" > '2015-03-27 00:26:30.629526')))
- **Với Hashes và Thay thế**
```Ruby
Article.where( :title.matches % 'Hello%' &{:created_at.lt =>
Time.now, :created_at.gt => 1.year.ago}).to_sql
=> SELECT "articles".* FROM "articles" WHERE
(("articles"."title" LIKE 'Hello%' AND
("articles"."created_at" < '2015-03-27 01:04:38.023615' AND
"articles"."created_at" > '2015-03-27 01:04:38.023720')))
- **MetaWhere có thể xử lý một cấu trúc phức tạp hơn : **
```Ruby
Article.joins(:comments).where( {:title=> 'Greetings'} |
((:created_at.gt % 21.days.ago & :created_at.lt % 7.days.ago) &
:body.matches % '%from the past%' )
{:comments => [:body =~ '%first post!%']}).to_sql
=> SELECT "articles".*FROM "articles"INNER JOIN "comments"
ON "comments"."article_id" = "articles"."id"
WHERE
(("articles"."title" = 'Greetings' OR(((
"articles"."created_at" > '2010-03-26 05:57:57.924258'
AND "articles"."created_at" < '2010-04-09 05:57:57.924984')
AND "articles"."body" LIKE '%from the past%')
AND "comments"."body" LIKE '%first post!%')))
- **Join() một thuộc tính :**
```Ruby
Article.joins(:comments => :moderations.outer).to_sql
=> SELECT "articles".* FROM "articles"
INNER JOIN "comments"
ON "comments"."article_id" = "articles"."id"
LEFT OUTER JOIN "moderations"
ON "moderations"."comment_id" = "comments"."id"
- **Các hàm SQL :**
```Ruby
Manager.joins(:employees.outer).group('managers.id').
having(:employees => (:count.func(:id) < 3))
=> SELECT "managers".* FROM "managers"
LEFT OUTER JOIN "employees"
ON "employees"."manager_id" = "managers"."id"
GROUP BY managers.id HAVING count("employees"."id") < 3
Nếu bạn cho phép dùng các toán tử hỗ trợ thì có thể dùng :count[:id] thay cho việc gọi function như trên.
Ngoài các hàm thông thường trong SQL : SELECT, WHERE, HAVING, bạn cũng có thể đặt thêm “bí danh” với “:as” :
Manager.select('managers.*').
select(:find_in_set[:id, '3,2,1'].as('position'))
=> SELECT managers.*, find_in_set("managers"."id",'3,2,1') AS
position FROM "managers"
- **Xử lý thông minh với điều kiện Hash : **
PredicateBuilder (một phần của ActiveRecord) sẽ truyền một Hash điều kiện vào một truy vấn SQL hợp lệ :
```Ruby
Article.joins(:comments).where(:comments => {:body => 'hey'}).to_sql
=> SELECT "articles".* FROM "articles" INNER JOIN "comments"
ON "comments"."article_id" = "articles"."id"
WHERE ("comments"."body" = 'hey')
- Hash lồng nhau :
Article.where(
:comments => {
:body => 'yo',
:moderations => [:value < 0]
},
:other_comments => {:body => 'hey'}
).joins(
{:comments => :moderations},
:other_comments
).to_sql
=> SELECT "articles".* FROM "articles"
INNER JOIN "comments" ON
"comments"."article_id" = "articles"."id"
INNER JOIN "moderations" ON
"moderations"."comment_id" = "comments"."id"
INNER JOIN "comments" "other_comments_articles" ON
"other_comments_articles"."article_id" = "articles"."id"
WHERE (("comments"."body" = 'yo' AND "moderations"."value" < 0
AND "other_comments_articles"."body" = 'hey'))
-
Cải tiến hợp nhất mối quan hệ :
Một trong những thay đổi mà MetaWhere tạo ra đối với ActiveRecord là sẽ trì hoãn việc “compiling” các “where_values” vào thực tế Arel Predicates cho đến khi hoàn toàn cần thiết.
Trong quá trình này, MetaWher cung cấp cho bạn một phương pháp tuyệt vời để hợp nhất, thay đổi parameter của mình :
Article.where(:id < 2).merge(Comment.where(:id < 7), :lame_comments)
.to_sql
=> "SELECT "articles".* FROM "articles"
INNER JOIN "comments" ON "comments"."article_id" = "articles"."id"
AND "comments"."body" = 'first post!'
WHERE ("articles"."id" < 2) AND ("comments"."id" < 7)"
hay :
(Comment.where(:id < 7) & Article.where(:title =~ '%blah%')).to_sql
=> SELECT "comments".* FROM "comments" INNER JOIN "articles"
ON "articles"."id" = "comments"."article_id"
WHERE ("comments"."id" < 7) AND ("articles"."title" LIKE '%blah%')"
- Tính đa hình với "belongs_to" :
MetaWhere cho phép bạn truy vấn thông “belongs_to” theo cách sau :
```Ruby
Note.joins(:notable.type(Developer)).
where(:notable.type(Developer) => {:name.matches => 'Ernie%'})
=> SELECT "notes".* FROM "notes"
INNER JOIN "developers" ON "developers"."id" = "notes"."notable_id"
AND "notes"."notable_type" = 'Developer'
WHERE "developers"."name" LIKE 'Ernie%'
- **Sử dụng giá trị điều kiện với object ActiveRecord :**
```Ruby
# Developer belongs_to Company
company = Company.find(123)
Developer.where(:company => company)
# Developer HABTM Projects
projects = [Project.first, Project.last]
Developer.joins(:projects).where(:projects => projects)
# Note belongs_to :notable, :polymorphic => true
dev1 = Developer.first
dev2 = Developer.last
project = Project.first
company = Company.first
Note.where(:notable => [dev1, dev2, project, company]).to_sql
=> SELECT "notes".* FROM "notes" WHERE (((("notes"."notable_id" IN (1, 8)
AND "notes"."notable_type" = 'Developer') OR ("notes"."notable_id" = 1
AND "notes"."notable_type" = 'Project')) OR ("notes"."notable_id" = 1
AND "notes"."notable_type" = 'Company')))
Nguồn tham khảo : - http://erniemiller.org/ - http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html - https://github.com/activerecord-hackery/meta_where - https://github.com/brynary/arel
All rights reserved