2009-09-03 7 views
8

मैं अपने रेल प्रोजेक्ट में awesome_nested_set प्लगइन का उपयोग कर रहा हूं।किसी नेस्टेड सेट से सभी रिकॉर्ड को वास्तविक HTML पेड़ में कैसे प्रस्तुत करें

class Customer < ActiveRecord::Base 
    has_many :categories 
end 

class Category < ActiveRecord::Base 
    belongs_to :customer 

    # Columns in the categories table: lft, rgt and parent_id 
    acts_as_nested_set :scope => :customer_id 

    validates_presence_of :name 
    # Further validations... 
end 

डेटाबेस में पेड़ के रूप में उम्मीद का निर्माण किया है: मैं दो मॉडल है कि इस (सरलीकृत) की तरह लग रही है। parent_id, lft और rgt के सभी मान सही हैं। पेड़ में कई रूट नोड्स हैं (जिन्हें निश्चित रूप से awesome_nested_set में अनुमति दी गई है)।

अब, मैं किसी दिए गए ग्राहक की सभी श्रेणियों को संरचना जैसे सही ढंग से सॉर्ट किए गए पेड़ में प्रस्तुत करना चाहता हूं: उदाहरण के लिए नेस्टेड <ul> टैग। यह बहुत मुश्किल नहीं होगा लेकिन मुझे इसे कुशल होने की आवश्यकता है (कम एसक्यूएल प्रश्न बेहतर)।

अद्यतन: यह पता लगाया गया है कि बिना किसी एसक्यूएल प्रश्नों के पेड़ में दिए गए नोड के लिए बच्चों की संख्या की गणना करना संभव है: number_of_children = (node.rgt - node.lft - 1)/2। यह समस्या का समाधान नहीं करता है लेकिन यह सहायक साबित हो सकता है।

उत्तर

7

यह अच्छा होगा अगर नेस्टेड सेट में बॉक्स से बेहतर सुविधाएं होंगी।

चाल के रूप में आप की खोज की है एक फ्लैट सेट से पेड़ का निर्माण करना है:

एलएफटी के अनुसार क्रमबद्ध सभी नोड के एक सेट के साथ
  • शुरू
  • प्रथम नोड एक रूट यह रूट के रूप में जोड़ने है पेड़ के अगले नोड
  • यदि यह पिछले नोड का बच्चा है (prev.lft और prev.rht के बीच lft) एक बच्चे को पेड़ में जोड़ें और आगे एक नोड
  • अन्यथा पेड़ को ऊपर ले जाएं स्तर और दोहराना परीक्षण

नीचे देखें:

def tree_from_set(set) #set must be in order 
    buf = START_TAG(set[0]) 
    stack = [] 
    stack.push set[0] 
    set[1..-1].each do |node| 
    if stack.last.lft < node.lft < stack.last.rgt 
     if node.leaf? #(node.rgt - node.lft == 1) 
     buf << NODE_TAG(node) 
     else 
     buf << START_TAG(node) 
     stack.push(node) 
     end 
    else# 
     buf << END_TAG 
     stack.pop 
     retry 
    end 
    end 
    buf <<END_TAG 
end 

def START_TAG(node) #for example 
    "<li><p>#{node.name}</p><ul>" 
end 

def NODE_TAG(node) 
    "<li><p>#{node.name}</p></li>" 
end 

def END_TAG 
    "</li></ul>" 
end 
+0

यह काम करता है। आप भी awesome_nested_set के बारे में सही हैं। मैं आश्चर्यचकित नहीं कर सकता कि यह प्लगइन में पहली जगह क्यों नहीं बनाया गया है। धन्यवाद! –

+0

उल्लेख करने के लिए भूल गए: आपके समाधान के बारे में आवश्यक बिंदु यह है कि इसे केवल एक एकल SQL क्वेरी की आवश्यकता है! –

+3

http://gist.github.com/460814 –

3

आपको एक आंशिक रूप से प्रस्तुत करना होगा जो स्वयं को कॉल करेगा। कुछ इस तरह:

# customers/show.html.erb 
<p>Name: <%= @customer.name %></p> 
<h3>Categories</h3> 
<ul> 
    <%= render :partial => @customer.categories %> 
</ul> 

# categories/_category.html.erb 
<li> 
    <%= link_to category.name, category %> 
    <ul> 
    <%= render :partial => category.children %> 
    </ul> 
</li> 

यह रेल 2.3 कोड है। आपको मार्गों को कॉल करना होगा और इससे पहले आंशिक रूप से आंशिक नाम देना होगा।

+1

हाँ, मैं एक ही समाधान अपने आप के साथ आया था। समस्या यह है कि 'बच्चों' को हर कॉल एक अतिरिक्त SQL क्वेरी निष्पादित करता है (100 subtrees = 100 SQL क्वेरी)। शास्त्रीय एन + 1 समस्या में परिणाम। यही वही है जो मैं टालने की कोशिश कर रहा हूं। इसके अलावा: पहला रेंडर आंशिक कॉल कुछ होना चाहिए <<% = रेंडर: आंशिक => @ ग्राहक.categories.roots%> ' –

5

मैंने हाल ही में similar question for php उत्तर दिया (नेस्टेड सेट == संशोधित प्रीऑर्डर पेड़ ट्रैवर्सल मॉडल)।

मूल अवधारणा पहले से आदेशित नोड्स प्राप्त करने और के माध्यम से एक गहराई सूचक के साथ एक SQL क्वेरी है। वहां से यह लूप या रिकर्सन के माध्यम से आउटपुट को प्रस्तुत करने का सिर्फ एक प्रश्न है, इसलिए इसे रूबी में परिवर्तित करना आसान होना चाहिए।

मैं awesome_nested_set प्लग से परिचित नहीं हूं, लेकिन इसमें पहले से ही गहराई से एनोटेटेड, आदेशित परिणाम प्राप्त करने का विकल्प हो सकता है, क्योंकि यह नेस्टेड सेट से निपटने के दौरान एक सुंदर मानक संचालन/आवश्यकता है।

3

_tree.html.eb

@set = Category.root.self_and_descendants 
<%= render :partial => 'item', :object => @set[0] %> 

_item.html।ERB

<% @set.shift %> 
<li><%= item.name %> 
<% unless item.leaf? %> 
<ul> 
    <%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id} %> 
</ul> 
<% end %> 
</li> 

भी कर सकते हैं प्रकार उनके:

<%= render :partial => 'item', :collection => @set.select{|i| i.parent_id == item.id}.sort_by(&:name) %> 

लेकिन इस मामले में आप इस लाइन को निकाल देना चाहिए:

<% @set.shift %> 
5

सितम्बर 2009 भयानक नेस्टेड सेट के बाद से ऐसा करने के लिए एक विशेष विधि भी शामिल है यह: https://github.com/collectiveidea/awesome_nested_set/commit/9fcaaff3d6b351b11c4b40dc1f3e37f33d0a8cbe

यह विधि बहुत अधिक है कॉलिंग स्तर की तुलना में फिर से प्रभावशाली है क्योंकि इसे किसी भी अतिरिक्त डेटाबेस प्रश्नों की आवश्यकता नहीं है।

उदाहरण: category.each_with_level (category.root.self_and_descendants) do | o, स्तर |

1

मुझे रूबी के पुराने संस्करण की वजह से स्वीकार्य उत्तर नहीं मिल सका, मुझे लगता है कि, मुझे लगता है।

def tree_from_set(set) 
    buf = '' 

    depth = -1 
    set.each do |node| 
     if node.depth > depth 
      buf << "<ul><li>#{node.title}" 
     else 
      buf << "</li></ul>" * (depth - node.depth) 
      buf << "</li><li>#{node.title}" 
     end 

     depth = node.depth 
    end 

    buf << "</li></ul>" * (depth + 1) 

    buf.html_safe 
end 

यह वैकल्पिक गहराई से जानकारी का उपयोग करके सरलीकृत है: यहाँ समाधान मेरे लिए काम कर रहा है। (इस दृष्टिकोण का लाभ इनपुट पत्तियों को पूरी संरचना बनाने के लिए सेट की कोई आवश्यकता नहीं है कि वहाँ है।) गहराई बिना

अधिक जटिल समाधान मणि के GitHub विकि पर पाया जा सकता है:

https://github.com/collectiveidea/awesome_nested_set/wiki/How-to-generate-nested-unordered-list-tags-with-one-DB-hit

0

हो सकता है कि थोड़ी देर हो चुकी है, लेकिन मैं awesome_nested_setclosure_tree मणि के आधार पर के लिए मेरे समाधान साझा करना चाहते हैं नेस्ट hash_tree विधि:

def build_hash_tree(tree_scope) 
    tree = ActiveSupport::OrderedHash.new 
    id_to_hash = {} 

    tree_scope.each do |ea| 
    h = id_to_hash[ea.id] = ActiveSupport::OrderedHash.new 
    (id_to_hash[ea.parent_id] || tree)[ea] = h 
    end 
    tree 
end 

यह किसी भी गुंजाइश यह रेंडर करने के लिए की तुलना में उपयोग सहायक द्वारा lft

आदेश दिया के साथ काम करेंगे:

def render_hash_tree(tree) 
    content_tag :ul do 
    tree.each_pair do |node, children| 
     content = node.name 
     content += render_hash_tree(children) if children.any? 
     concat content_tag(:li, content.html_safe) 
    end 
    end 
end 
संबंधित मुद्दे