ruby on rails - उन सभी रिकॉर्ड्स का चयन करें जिनके पास कुछ ऐसी स्थिति है जो कि है_माइंड एसोसिएशन- रूबी ऑन रेल में है




ruby-on-rails postgresql (4)

मेरे पास निम्नलिखित एसोसिएशन के साथ एक model profile.rb I

class User < ActiveRecord::Base
   has_one :profile
end

class Profile < ActiveRecord::Base
    has_many :skills
    belongs_to :user
end

मेरे पास निम्नलिखित model skills.rb साथ एक model skills.rb

class Skill < ActiveRecord::Base
    belongs_to :profile
end

मेरे पास कौशल तालिका में प्रविष्टियां हैं I

id:         name:           profile_id:
====================================================
1           accounting          1
2           martial arts        2
3           law                 1
4           accounting          2
5           journalist          3
6           administration      1

और इतने पर, मैं कैसे सभी प्रोफाइल के साथ क्वेरी कर सकते हैं, कहते हैं, "लेखा" और "प्रशासन" कौशल जो आईडी 1 के साथ प्रोफाइल हो जाएगा पर ऊपर ध्यान देने योग्य विचार। अब तक मैंने निम्नलिखित कोशिश की है

Profile.includes(:skills).where(skills: {name: ["accounting" , "administration"]} )

लेकिन id 1 साथ प्रोफाइल को खोजने के बजाय - यह मुझे [ 1, 2 ] क्योंकि आईडी 2 के साथ प्रोफाइल "accounting" skills रखता "accounting" skills और यह डाटाबेस में "IN" operation कर रहा है

नोट: मैं postgresql का उपयोग कर रहा हूँ और प्रश्न केवल वर्णन के रूप में निर्दिष्ट विशिष्ट आईडी प्रोफ़ाइल के बारे में नहीं है (जो मैंने केवल एक उदाहरण के रूप में प्रयोग किया था) - मूल प्रश्न यह है कि सभी प्रोफाइल प्राप्त करना है जिसमें ये दो उल्लेखनीय कौशल शामिल हैं।

मेरी सक्रियरेकॉर्ड पोस्टग्रेज़ में निम्नलिखित क्वेरी को शामिल करता है

SELECT FROM "profiles" LEFT OUTER JOIN "skills" ON "skills"."profile_id" = "profiles"."id" WHERE "skills"."name" IN ('Accounting', 'Administration')

विजय अग्रवाल के जवाब में मेरे पास पहले से ही मेरे आवेदन में कुछ और है, उसका और मेरा प्रश्न, वाइल्डकार्ड IN उपयोग करते हैं, जिसके परिणामस्वरूप प्रोफ़ाइल आईडी होते हैं, जिनमें कौशल का कोई भी हिस्सा होता है, जबकि मेरा प्रश्न प्रोफ़ाइल आईडी प्राप्त करना है जिसमें कौशल दोनों होते हैं । मुझे यकीन है कि इस प्रश्न को उसी प्रश्न में तय करने का एक तरीका होना चाहिए जो मूल प्रश्न में सूचीबद्ध है और मैं इस तरीके से सीखने के लिए उत्सुक हूं। मुझे आशा है कि मुझे आपके साथ कुछ और मदद मिलेगी- धन्यवाद

स्पष्टता के लिए, मैं सभी प्रोफाइल को एक मॉडल में कई कौशल के साथ क्वेरी करना चाहता हूं जिसके साथ प्रोफ़ाइल मॉडल के साथ कई संबंध हैं- प्राथमिक तालिका के रूप में Profile का उपयोग skills नहीं

प्रोफाइल को प्राथमिक तालिका के रूप में उपयोग करने का कारण यह है कि पृष्ठ पर अंक लगने पर मैं संबंधित तालिका से सभी कौशल प्राप्त नहीं करना चाहता, ऐसा कहना कि 20_000 या अधिक पंक्तियां और फिर profile.state अनुसार फ़िल्टर करें। इसके बजाय किसी को भी केवल 5 records जो profile.state , profile.user.is_active and other columns condition मिलते profile.state , profile.user.is_active and other columns condition और हजारों अप्रासंगिक रिकॉर्ड प्राप्त किए बिना कौशल से मेल खाते का चयन करना चाहते हैं और फिर उन्हें फिर से फ़िल्टर करें।


आपको ऐसा सभी profile_id प्राप्त करने के लिए करना चाहिए, जिसमें दोनों अकाउंटिंग और प्रशासन कौशल हैं:

Skill.where(name: ["accounting", "administration"]).group(:profile_id).having("count('id') = 2").pluck(:profile_id)

यदि आपको प्रोफाइल विवरण की आवश्यकता है, तो आप इस क्वेरी को id लिए Profile खंड कहां रख सकते हैं।

क्वेरी में नंबर 2 ध्यान दें, यह आपके सरणी की लंबाई है जहां क्लॉज में उपयोग किया जाता है। इस मामले में ["accounting", "administration"].length

अद्यतन करें::

अद्यतित प्रश्न विवरण के आधार पर, pluck बजाय आप एक क्वेरी में यह सुनिश्चित करने के लिए select सकते हैं और उपकुंजी जोड़ सकते हैं।

Profile.where(id: Skill.where(name: ["accounting", "administration"]).group(:profile_id).having("count('id') = 2").select(:profile_id))

आपके और अधिक पर छंटनी, पृष्ठ पर अंक लगाना और अतिरिक्त पर नियंत्रण है जहां खंड उस प्रश्न के बारे में मत देखें जो प्रश्न में संपादित किए गए हैं

2 अद्यतन करें ::

दोनों कौशल (ऊपर से समाधान की तुलना में कम कुशल होने की संभावना) दोनों के साथ प्रोफाइल का प्रतिच्छेदन करने का दूसरा तरीका:

profiles = Profile

["accounting", "administration"].each do |name|
  profiles = profiles.where(id: Skill.where(name: name).select(:profile_id))
end

PostgreSQL आश्रित समाधान:

where_clause = <<~SQL
  ARRAY(
    SELECT name FROM skills WHERE profile_id = profiles.id
  ) @> ARRAY[?]
SQL
Profile.where(where_clause, %w[skill1 skill2])

यह काम करता है, लेकिन गति को बढ़ाने के लिए डीबी संरचना बदलने के लिए समझ में आता है। दो विकल्प हैं:

  • has_and_belongs_to_many तरह स्थिरता ( skills तालिकाओं शब्दकोश में बदल जाता है) और अनुक्रमणिकाओं का उपयोग करने की क्षमता
  • array रूप में skills | profile jsonb कॉलम - उप-चयन या जुड़ने के बिना सूचकांक द्वारा त्वरित खोज जोड़ता है

निम्नलिखित प्रश्न के साथ समस्या

Profile.includes(:skills).where(skills: { name: ["accounting" , "administration"] } ) यह है कि इसमें IN ऑपरेटर जैसे IN ('Accounting', 'Administration')

अब एसक्यूए मानक के अनुसार, यह सभी अभिलेखों से मेल खाएगा जो किसी भी मान से मेल खाते हैं और सरणी से सभी मूल्यों को नहीं।

यहाँ एक सरल समाधान है

skills = ["accounting" , "administration"]

Profile.includes(:skills).where(skills: { name: skills }).group(:profile_id).having("count(*) = #{skills.length}")

पी एस यह मानता है कि आपके पास कम से कम एक कौशल होगा। अपने प्रयोग के अनुसार की स्थिति को समायोजित करें


मैं एक सहसंबद्ध उप-क्वेरी के साथ EXISTS उपयोग करेगा, इस तरह:

required_skills = %w{accounting administration}
q = Profile.where("1=1")
required_skills.each do |sk|
  q = q.where(<<-EOQ, sk)
    EXISTS (SELECT 1
            FROM   skills s
            WHERE  s.profile_id = profiles.id
            AND    s.name = ?)
  EOQ
end

इस तरह के इसी प्रश्न पर कुछ अन्य विचार हैं लेकिन मुझे लगता है कि आपके मामले में कई EXISTS नियम सबसे सरल और सबसे तेज़ हैं।

(रेल 4 + में जिस तरह से आप Profile.where("1=1") बजाय Profile.all साथ शुरू कर सकते हैं, क्योंकि all Relation रिटर्न देता है, लेकिन पुराने दिनों में यह सरणी वापस लौटता था।)







activerecord