2017-06-29 14 views
6
class GenericFormatter < Formatter 
attr_accessor :tag_name,:objects 

def generate_xml 
    builder = Nokogiri::XML::Builder.new do |xml| 
    xml.send(tag_name.pluralize) { 
    objects.each do |obj| 
     xml.send(tag_name.singularize){ 

      self.generate_obj_row obj,xml 
     }     
    end 
    } 
    end 
    builder.to_xml 
end 


def initialize tag_name,objects 
    self.tag_name = tag_name 
    self.objects = objects 
end 


def generate_obj_row obj,xml 
    obj.attributes.except("updated_at").map do |key,value| 
    xml.send(key, value) 
    end 
    xml.updated_at obj.updated_at.try(:strftime,"%m/%d/%Y %H:%M:%S") if obj.attributes.key?('updated_at') 
end 
end 

का गुच्छा उपरोक्त कोड में से XML पैदा करने, मैं फ़ॉर्मेटर जहाँ मैं वस्तुओं code.It के अंदर बाहर निकल जाते जोड़ तोड़ द्वारा nokogiri एक्सएमएल बिल्डर का इस्तेमाल किया है एक्सएमएल उत्पन्न करने के लिए लागू कर दिया है, जबकि जब डेटा 10,000 से अधिक रिकॉर्ड की तरह बड़ा होता है तो डेटा अधिक तेज़ नहीं होता है, तो यह उत्पन्न होने के लिए एक्सएमएल धीमा हो जाता है और कम से कम 50-60 सेकंड लेता है।धीमी मॉडल वस्तु

समस्या: क्या एक्सएमएल तेजी से उत्पन्न करने का कोई तरीका है, मैंने एक्सएमएल बिल्डर्स को भी देखने की कोशिश की है लेकिन काम नहीं किया। मैं एक्सएमएल तेज़ कैसे उत्पन्न कर सकता हूं? समाधान समाधान 3 और कोड के ऊपर अनुकूलित करने के सुझावों पर एक आवेदन होना चाहिए?

+0

मुद्दा सिर्फ एक भंडारण कि है 'ऑब्जेक्ट्स' की विशाल सूची आपकी सभी रैम का उपयोग कर रही है? आप ऑब्जेक्ट [बैचों में] को संभालने के लिए कोड को दोबारा कर सकते हैं (http: //api.rubyonrails।org/वर्गों/ActiveRecord/Batches.html)। –

+0

इसके अलावा, यदि आप इसे एक असीमित कार्य के रूप में चलाते हैं तो प्रदर्शन किसी समस्या का * कम * हो जाता है। –

+0

नहीं, यह बड़ी सूची को संग्रहीत करने के बारे में नहीं है, यह ऑब्जेक्ट को xml में हेरफेर करने के बारे में है। अगर हम इसे बैच में विभाजित करते हैं तो यह प्रदर्शन –

उत्तर

3

आपकी मुख्य समस्या बैच में आपके डेटा को विभाजित करने के बजाय सब कुछ एक ही समय में संसाधित कर रही है। इसे सभी को बहुत सारी मेमोरी की आवश्यकता होती है, पहले उन सभी ActiveRecord मॉडल बनाने के लिए और फिर पूरे XML दस्तावेज़ का स्मृति प्रतिनिधित्व बनाने के लिए। मेटा प्रोग्रामिंग भी काफी महंगा है (मेरा मतलब है कि उन send विधियों)।

class XmlGenerator 
    attr_accessor :tag_name, :ar_relation 

    def initialize(tag_name, ar_relation) 
    @ar_relation = ar_relation 
    @tag_name = tag_name 
    end 

    def generate_xml 
    singular_tag_name = tag_name.singularize 
    plural_tag_name = tag_name.pluralize 

    xml = "" 
    xml << "<#{plural_tag_name}>" 

    ar_relation.find_in_batches(batch_size: 1000) do |batch| 
     batch.each do |obj| 
     xml << "<#{singular_tag_name}>" 

     obj.attributes.except("updated_at").each do |key, value| 
      xml << "<#{key}>#{value}</#{key}>" 
     end 

     if obj.attributes.key?("updated_at") 
      xml << "<updated_at>#{obj.updated_at.strftime('%m/%d/%Y %H:%M:%S')}</updated_at>" 
     end 

     xml << "</#{singular_tag_name}>" 
     end 
    end 

    xml << "</#{tag_name.pluralize}>" 
    xml 
    end 
end 

# example usage 
XmlGenerator.new("user", User.where("age < 21")).generate_xml 

मेजर सुधार कर रहे हैं::

इस कोड पर एक नजर डालें

  • बैचों में डेटाबेस से डेटा प्राप्त करने में, आप ActiveRecord संग्रह के बजाय ActiveRecord मॉडल की सरणी पास करनी होगी
  • तारों का निर्माण करके एक्सएमएल उत्पन्न करना, इसमें अमान्य एक्सएमएल का उत्पादन करने का जोखिम है, लेकिन यह बिल्डर
का उपयोग करने से बहुत तेज है

मैंने 60k से अधिक रिकॉर्डों पर इसका परीक्षण किया। इस तरह के एक्सएमएल दस्तावेज उत्पन्न करने में लगभग 40 सेकंड लग गए।

यह और भी बेहतर बनाने के लिए किया जा सकता है, लेकिन यह सब आपके आवेदन पर निर्भर करता है।

यहां कुछ सुझाव दिए गए हैं:

  • ActiveRecord का उपयोग नहीं करते डेटा लाने, बजाय हल्के पुस्तकालय या सादे डेटाबेस ड्राइवर का उपयोग करने के लिए
  • केवल डेटा है कि आप की जरूरत
  • ट्वीक बैच आकार
  • लिखने लाने स्मृति
+0

अरे माइकल मूल रूप से आप एक्सएमएल के रूप में स्ट्रिंग उत्पन्न कर रहे हैं तो मैं इसे सादा एक्सएमएल प्रारूप में कैसे प्राप्त कर सकता हूं क्योंकि मुझे लगता है कि अगर मैं विधि को _xml पर कॉल करूंगा तो स्ट्रिंग पर उत्पन्न करने के लिए स्ट्रिंग पर अधिक समय लगेगा । –

+0

मुझे नहीं पता कि आप इस वर्ग का उपयोग करने की योजना कैसे बनाते हैं। अगर कुछ कोड 'to_xml' को कॉल कर रहा है तो बस 'gener_xml'' से' to_xml' का नाम बदलें। –

+0

धन्यवाद मिचल लिटिल परफॉर्मेंस इसे लागू करके बढ़ाया गया है ताकि इसे आसानी से एकीकृत किया जा सके। जो अंक आपने इसके लिए दिए हैं, उनके लिए अच्छा है। –

1

को सहेजने के लिए सीधे फ़ाइल को उत्पन्न करें (यदि वह आपका उपयोग केस है)में स्क्रैच से एक्सएमएल बनाने के लिए एक अच्छा इंटरफेस है, नोकोगिरी libxml2 के चारों ओर एक रैपर है।

Gemfile मणि 'nokogiri' इस

xml = Nokogiri::XML::Builder.new { |xml| 
    xml.body do 
     xml.test1 "some string" 
     xml.test2 890 
     xml.test3 do 
      xml.test3_1 "some string" 
     end 
     xml.test4 "with attributes", :attribute => "some attribute" 
     xml.closing 
    end 
}.to_xml 

उत्पादन की तरह एक्सएमएल सरल उपयोग Nokogiri एक्सएमएल बिल्डर उत्पन्न करने के लिए

<?xml version="1.0"?> 
<body> 
    <test1>some string</test1> 
    <test2>890</test2> 
    <test3> 
    <test3_1>some string</test3_1> 
    </test3> 
    <test4 attribute="some attribute">with attributes</test4> 
    <closing/> 
</body> 

डेमो: http://www.jakobbeyer.de/xml-with-nokogiri

+0

अरे मायार, नोकोगिरी के डेमो के लिए धन्यवाद। यदि आप मेरे कोड के ऊपर देखते हैं तो यह नोकोगिरी बिल्डर में भी है, इसलिए आप मेरी तरफ से क्या उम्मीद कर रहे हैं। –

संबंधित मुद्दे