GIỚI THIỆU VÀ SỬ DỤNG GEM META WHERE TRONG RAILS
**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 ...
**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