यह लेख कोरियाई में भी उपलब्ध है, सूनसांग हॉन्ग का धन्यवाद!
Rails के scopes आपके द्वारा चाहने वाले रिकॉर्ड्स को ढूंढना आसान बनाते हैं:
class Review < ActiveRecord::Base belongs_to :restaurant scope :positive, -> { where("rating > 3.0") } endpre>irb(main):001:0> Restaurant.first.reviews.positive.count Restaurant Load (0.4ms) SELECT `restaurants`.* FROM `restaurants` ORDER BY `restaurants`.`id` ASC LIMIT 1 (0.6ms) SELECT COUNT(*) FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0) => 5pre>लेकिन अगर आप उनके साथ सावधान नहीं हैं, तो आप अपने ऐप के प्रदर्शन को गंभीर रूप से नुकसान पहुंचाएंगे।
क्यों? आप वास्तव में एक scope को प्रीलोड नहीं कर सकते। तो अगर आपने कुछ रेस्तरां को उनकी सकारात्मक समीक्षाओं के साथ दिखाने की कोशिश की:
irb(main):001:0> restauraunts = Restaurant.first(5) irb(main):002:0> restauraunts.map do |restaurant| irb(main):003:1* "#{restaurant.name}: #{restaurant.reviews.positive.length} positive reviews." irb(main):004:1> end Review Load (0.6ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0) Review Load (0.5ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 2 AND (rating > 3.0) Review Load (0.7ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 3 AND (rating > 3.0) Review Load (0.7ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 4 AND (rating > 3.0) Review Load (0.7ms) SELECT `reviews`.* FROM `reviews` WHERE `reviews`.`restaurant_id` = 5 AND (rating > 3.0) => ["Judd's Pub: 5 positive reviews.", "Felix's Nightclub: 6 positive reviews.", "Mabel's Burrito Shack: 7 positive reviews.", "Kendall's Burrito Shack: 2 positive reviews.", "Elisabeth's Deli: 15 positive reviews."]pre>हां, यह एक N+1 क्वेरी है। धीमे Rails ऐप्स का सबसे बड़ा कारण।
आप इसे बहुत आसानी से ठीक कर सकते हैं, हालांकि, अगर आप रिश्ते को एक अलग तरीके से सोचते हैं।
Scopes को Associations में बदलें
जब आप Rails एसोसिएशन मेथड्स का उपयोग करते हैं, जैसे
belongs_toऔरhas_many, तो आपका मॉडल आमतौर पर ऐसा दिखता है:app/models/restaurant.rb class Restaurant < ActiveRecord::Base has_many :reviews endpre>लेकिन अगर आप दस्तावेज़ीकरण देखते हैं, तो आप देखेंगे कि वे अधिक कर सकते हैं। आप उन मेथड्स में अन्य पैरामीटर पास कर सकते हैं और बदल सकते हैं कि वे कैसे काम करते हैं।
scopeसबसे उपयोगी में से एक है। यह पहले वालेscopeकी तरह ही काम करता है:app/models/restaurant.rb class Restaurant < ActiveRecord::Base has_many :reviews has_many :positive_reviews, -> { where("rating > 3.0") }, class_name: "Review" endpre>irb(main):001:0> Restaurant.first.positive_reviews.count Restaurant Load (0.2ms) SELECT `restaurants`.* FROM `restaurants` ORDER BY `restaurants`.`id` ASC LIMIT 1 (0.4ms) SELECT COUNT(*) FROM `reviews` WHERE `reviews`.`restaurant_id` = 1 AND (rating > 3.0) => 5pre>अब, आप
includesके साथ अपने नए एसोसिएशन को प्रीलोड कर सकते हैं:irb(main):001:0> restauraunts = Restaurant.includes(:positive_reviews).first(5) Restaurant Load (0.3ms) SELECT `restaurants`.* FROM `restaurants` ORDER BY `restaurants`.`id` ASC LIMIT 5 Review Load (1.2ms) SELECT `reviews`.* FROM `reviews` WHERE (rating > 3.0) AND `reviews`.`restaurant_id` IN (1, 2, 3, 4, 5) irb(main):002:0> restauraunts.map do |restaurant| irb(main):003:1* "#{restaurant.name}: #{restaurant.positive_reviews.length} positive reviews." irb(main):004:1> end => ["Judd's Pub: 5 positive reviews.", "Felix's Nightclub: 6 positive reviews.", "Mabel's Burrito Shack: 7 positive reviews.", "Kendall's Burrito Shack: 2 positive reviews.", "Elisabeth's Deli: 15 positive reviews."]pre>6 SQL कॉल के बजाय, हमने केवल दो किए।
(
class_nameका उपयोग करके, आप एक ही ऑब्जेक्ट के लिए कई एसोसिएशन रख सकते हैं। यह बहुत काम आता है।)डुप्लीकेशन के बारे में क्या?
यहां अभी भी एक समस्या हो सकती है।
where("rating > 3.0")अब आपके Restaurant क्लास पर है। अगर आप बाद में सकारात्मक समीक्षाओं कोrating > 3.5में बदलते, तो आपको इसे दो बार अपडेट करना पड़ता!यह और खराब हो जाता है: यदि आप उन सभी सकारात्मक समीक्षाओं को भी प्राप्त करना चाहते हैं जो किसी व्यक्ति ने कभी छोड़ी हैं, तो आपको उस scope को User क्लास पर भी डुप्लिकेट करना होगा:
app/models/user.rb class User < ActiveRecord::Base has_many :reviews has_many :positive_reviews, -> { where("rating > 3.0") }, class_name: "Review" endpre>यह बहुत DRY नहीं है।
इसके आसपास एक आसान तरीका है, हालांकि।
whereके अंदर, आप Review क्लास में जोड़े गएpositivescope का उपयोग कर सकते हैं:app/models/restaurant.rb class Restaurant < ActiveRecord::Base has_many :reviews has_many :positive_reviews, -> { positive }, class_name: "Review" endpre>इस तरह, विचार कि क्या एक समीक्षा को सकारात्मक समीक्षा बनाता है, अभी भी केवल एक जगह पर है।
Scopes महान हैं। सही जगह पर, वे आपके डेटा को क्वेरी करना आसान और मजेदार बना सकते हैं। लेकिन अगर आप N+1 क्वेरी से बचना चाहते हैं, तो आपको उनके साथ सावधान रहना होगा।
तो, अगर एक scope आपको परेशानी देना शुरू कर देता है, तो इसे एक एसोसिएशन में लपेटें और इसे प्रीलोड करें। यह बहुत अधिक काम नहीं है, और यह आपको SQL कॉल का एक गुच्छा बचाएगा।