2016-12-13 7 views
11

मुझे रेल में पॉलिमॉर्फिक एसोसिएशन के लिए स्कॉप्स लिखने के बारे में बहुत कम पता चला है, अकेले पॉलिमॉर्फिक एसोसिएशन पर प्रश्न कैसे लिखना है।पॉलिमॉर्फिक एसोसिएशन के साथ स्कॉप्स और क्वेरीज़

रेल दस्तावेज़ीकरण में, मैंने Polymorphic Associations section, Joining Tables section, और Scopes section पर देखा है। मैंने गुगलिंग का अपना उचित हिस्सा भी किया है।

उदाहरण के लिए इस स्थापना लें:

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 
end 

class Dog < ActiveRecord::Base 
    has_many :pets, as: :animal 
end 

class Cat < ActiveRecord::Base 
    has_many :pets, as: :animal 
end 

class Bird < ActiveRecord::Base 
    has_many :pets, as: :animal 
end 

तो एक Petanimal_type "डॉग", "कैट" के हो सकते हैं, या "बर्ड"।

सभी तालिका संरचनाओं दिखाने के लिए: यहाँ मेरी schema.rb है:

create_table "birds", force: :cascade do |t| 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
end 

create_table "cats", force: :cascade do |t| 
    t.integer "killed_mice" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
end 

create_table "dogs", force: :cascade do |t| 
    t.boolean "sits" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
end 

create_table "pets", force: :cascade do |t| 
    t.string "name" 
    t.integer "animal_id" 
    t.string "animal_type" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
end 

मैं तो आगे चला गया और कुछ रिकॉर्ड बनाया:

Dog.create(sits: false) 
Dog.create(sits: true) 
Dog.create(sits: true) #Dog record that will not be tied to a pet 
Cat.create(killed_mice: 2) 
Cat.create(killed_mice: 15) 
Cat.create(killed_mice: 15) #Cat record that will not be tied to a pet 
Bird.create 

और फिर मैं चला गया और कुछ बनाया pet रिकॉर्ड:

Pet.create(name: 'dog1', animal_id: 1, animal_type: "Dog") 
Pet.create(name: 'dog2', animal_id: 2, animal_type: "Dog") 
Pet.create(name: 'cat1', animal_id: 1, animal_type: "Cat") 
Pet.create(name: 'cat2', animal_id: 2, animal_type: "Cat") 
Pet.create(name: 'bird1', animal_id: 1, animal_type: "Bird") 

और वह सेटअप है! अब कठिन हिस्सा: मैं Pet मॉडल पर कुछ स्कोप बनाना चाहता हूं जो पॉलीमोर्फिक संघों में खोदता है।

यहाँ कुछ स्कोप मैं लिखने के लिए करना चाहते हैं कर रहे हैं:

  • मुझे दो सब animal_type == "डॉग" कि बैठ सकते हैं
  • मुझे animal_type के सभी Pets दें की Pets == "कैट" जिसने कम से कम 10 चूहों को मार दिया है
  • मुझे सभी Pets दें जो जानवरों के प्रकार "कुत्ते" नहीं हैं और बैठ नहीं सकते हैं।

तो मैं वहाँ में मेरे कार्यक्षेत्र डाल करने के लिए चाहते हो जाएगा मेरी Pet मॉडल में:

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 

    scope :sitting_dogs, -> {#query goes here} 
    scope :killer_cats, -> {#query goes here} 
    scope :remove_dogs_that_cannot_sit, -> {#query goes here} #only removes pet records of dogs that cannot sit. All other pet records are returned 
end 

(दूसरे शब्दों में: कुत्तों के लिए सिवाय इसके कि नहीं बैठ सकते मुझे सभी पालतू जानवरों को दे दो: उन सभी को) मुझे इन क्षेत्रों को लिखना बहुत मुश्किल लगता है।

कुछ चीज़ें जो मुझे ऑनलाइन मिलीं, ऐसा लगता है कि आप केवल इन स्कॉप्स को कच्चे एसक्यूएल के साथ लिख सकते हैं। मैं सोच रहा हूं कि इसके बजाय इन स्कॉप्स के लिए हैश सिंटैक्स का उपयोग करना संभव है या नहीं।

कोई सुझाव/सहायता की सराहना की जाएगी!

उत्तर

1

पिछले जवाब की समीक्षा करने और इसके साथ चारों ओर खेलने के बाद:

यहाँ मेरी संस्करण है यहाँ मैं क्या काम मिल गया है।

(कि Pet.remove_dogs_that_cannot_sit एक सरणी देता है ध्यान दें। इस वर्ग विधि पढ़ी जा सकती है लेकिन N + 1 की वजह से धीमी गति से किया जा रहा है की सबसे बड़ी खामी। फिक्सिंग के लिए कोई सुझाव है कि बहुत सराहना की जाएगी है।)

class Dog < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :sits, -> {where(sits: true)} 
end 

class Cat < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :killer, ->{ where("killed_mice >= ?", 10) } 
end 

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 

    scope :by_type, ->(type) {where(animal_type: type)} 
    scope :by_dogs, -> {by_type("Dog") } 
    scope :by_cats, -> {by_type("Cat") } 

    def self.sitting_dogs 
    all.by_dogs 
     .joins("INNER JOIN dogs on animal_type = 'Dog' and animal_id = dogs.id") 
     .merge(Dog.sits) 
    end 

    def self.killer_cats 
    all.by_cats 
     .joins("INNER JOIN cats on animal_type = 'Cat' and animal_id = cats.id") 
     .merge(Cat.killer) 
    end 

    # returns an Array not Pet::ActiveRecord_Relation 
    # slow due to N + 1 
    def self.remove_dogs_that_cannot_sit 
    all.reject{|pet| pet.animal_type == "Dog" && !pet.animal.sits} 
    end 
end 
+0

'self.remove_dogs_that_cannot_sit' को 'पशु' पर शामिल करने के द्वारा तेज़ी से बनाया जा सकता है। 'All.includes (: पशु) .reject {| pet | pet.animal_type == "कुत्ता" &&! pet.animal.sits} ' – chrismanderson

+0

इसके सूक्ष्म को नोट करें, लेकिन जब आप" .reject "भाग को दबाते हैं तो आप वास्तव में रूबी के लिए छोड़ रहे हैं और एसक्यूएल को पीछे छोड़ रहे हैं, इसलिए इसकी धीमी क्यों है। आप एरियल स्कॉप्स चेन कर सकते हैं और एसक्यूएल में रह सकते हैं, उदा।by_dogs.where (बैठता है: झूठा) बराबर होना चाहिए, मान लें कि आपके पास डीबी में बूलियन कॉलम पर पूर्ण और डिफ़ॉल्ट झूठा सेट नहीं है। आईआईआरसी, मैं गलत हो सकता हूं क्योंकि मैं अपने सिर के ऊपर से जा रहा हूं, लेकिन जब तक आपके पास कोई गुंजाइश है, तब तक ".all" निहित और अनावश्यक है जब आप एरियल स्कॉप्स को चेन कर रहे हैं। – engineerDave

0

मैं प्रासंगिक व्यक्तिगत मॉडल के लिए ये क्षेत्र जोड़ना होगा जैसे:

class Dog < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :sits, ->{ where(sits: true) } 
end 
class Cat < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :natural_born_killer, ->{ where("killed_mice >= ?", 10) } 
end 

अगर आप फिर उन्हें मुख्य पालतू पशु मॉडल पर की जरूरत है, तो आप सिर्फ उन्हें जोड़ सकते हैं तरीकों जैसे के रूप में:

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 

    def sitting_dogs 
    where(:animal => Dog.sits.all) 
    end 
    def killer_cats 
    where(:animal => Cat.natural_born_killer.all) 
    end 
end 

आदि

आपका जटिल मामला बस कुछ पालतू जानवरों से कम है जो कुछ भी कुत्ते बैठे हैं।

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 
    scope :sits, ->{ where(sits: true) } 

    def sitting_dogs 
    where(:animal => Dog.sits.all) 
    end 

    # There's probably a nicer way than this - but it'll be functional 
    def remove_dogs_that_cannot_sit 
    where.not(:id => sitting_dogs.pluck(:id)).all 
    end 
end 
+0

आपकी प्रतिक्रिया के लिए धन्यवाद एन 1 को हटाने का एक और तरीका है। कठिन हिस्सा वह है जिसे मैं वास्तव में 'पेट' रिकॉर्ड के बारे में परवाह करता हूं। मैं पालतू अभिलेखों का कुछ संग्रह लेना चाहता हूं और उन्हें स्कॉप्स के आधार पर फ़िल्टर करना चाहता हूं। – Neil

+0

हां - यही वह है जो मुझे अब कर रहा है ... आप स्कोप्स के आधार पर पालतू जानवरों को फ़िल्टर कर रहे हैं। –

+0

नोट: असली उदाहरणों पर परीक्षण नहीं किया गया है ... उन्हें काम करने के लिए उनके साथ परेशान करने की आवश्यकता हो सकती है। नोट: आप स्कोप-विलय का भी उपयोग कर सकते हैं। –

1

मैं बैठे कुत्तों और हत्यारा बिल्लियों के लिए अलग-अलग क्षेत्रों के बारे में सहमत हूं। पालतू जानवर के लिए animal_type द्वारा फ़िल्टर करने के लिए एक दायरा पेश किया जा सकता है।

class Dog < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :sits, ->{ where(sits: true) } 
end 

class Cat < ActiveRecord::Base 
    has_many :pets, as: :animal 
    scope :killer, ->{ where("killed_mice >= ?", 10) } 
end 

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 
    scope :by_type, -> { |type| where(animal_type: type) } 
    scope :sitting_dogs, -> { by_type("Dog").sits } 
    scope :killer_cats, -> { by_type("Cat").killer } 
    scope :remove_dogs_that_cannot_sit, -> { reject{|pet| pet.animal_type == "Dog" && !pet.animal.sits} } 
end 
+0

आपके उत्तर के लिए धन्यवाद! यह बहुत अच्छा लग रहा है, हालांकि दुर्भाग्यवश यह मेरे लिए काम नहीं कर रहा है। 'Remove_dogs_that_cannot_sit' स्कोप का उपयोग करते समय यह निम्न त्रुटि के साथ आता है: 'नाम त्रुटि: अपरिभाषित स्थानीय चर या विधि' अस्वीकार ' – Neil

+0

ऐसा लगता है कि इसके लिए किसी को' all.reject ... 'के दायरे में निर्दिष्ट करने की आवश्यकता है काम करने के लिए, हालांकि मुझे यकीन नहीं है क्यों। – Neil

+1

मैंने यह भी देखा कि आपके 'remove_dogs_that_cannot_sit' स्कोप एक ActiveRecord :: Relation ऑब्जेक्ट के विपरीत एक सरणी देता है। इस प्रकार: शायद यह एक दायरा नहीं होना चाहिए क्योंकि यह श्रृंखला योग्य नहीं है। यह शायद एक वर्ग विधि होना चाहिए। – Neil

1

नहीं पूरा जवाब है, लेकिन यहां remove_dogs_that_cannot_sit क्वेरी है कि एक एआर संबंध वापस आती है और निकालता है एन + 1.

class Pet < ActiveRecord::Base 
    belongs_to :animal, polymorphic: true 
    belongs_to :dog, -> { where(pets: { animal_type: 'Dog' }) }, foreign_key: :animal_id 

    def self.remove_dogs_that_cannot_sit 
    includes(:dog).where.not("pets.animal_type = 'Dog' AND dogs.sits = false").references(:dogs) 
    end 

    def self.old_remove_dogs_that_cannot_sit 
    all.reject{|pet| pet.animal_type == "Dog" && !pet.animal.sits} 
    end 
end 

एकका उपयोग को क्रियान्वित करने का एक तरीका है एक पॉलिमॉर्फिक मॉडल परकुछ प्रश्नों को तेज करने का एक शानदार तरीका है, खासकर यदि आपका पॉलिमॉर्फिक मॉडल विकल्पों की एक छोटी संख्या तक ही सीमित है। आप अपनी कुछ स्कोप्ड विधि को Pet पर भी साफ कर सकते हैं।

def self.sitting_dogs 
    includes(:dog).merge(Dog.sits).references(:dogs) 
    end 

तेज़ भी।

irb(main):085:0> puts Benchmark.measure { 1000.times { Pet.remove_dogs_that_cannot_sit } } 
    0.040000 0.000000 0.040000 ( 0.032890) 
=> nil 

irb(main):087:0> puts Benchmark.measure { 1000.times { Pet.old_remove_dogs_that_cannot_sit } } 
    1.610000 0.090000 1.700000 ( 1.923665) 
=> nil 
0

यहाँ पर remove_dogs_that_cannot_sit

scope :joins_all -> { 
    joins("left join cats on animal_type = 'Cat' and animal_id = cats.id") 
    .joins("left join dogs on animal_type = 'Dog' and animal_id = dogs.id") 
    .joins("left join birds on animal_type = 'Bird' and animal_id = birds.id") 
} 

Pet.join_all.where.not("animal_type = 'Dog' and sits = 'f'") 
संबंधित मुद्दे