Class | IHelp::IHelpIndex |
In: |
lib/ihelp.rb
|
Parent: | Object |
IHelpIndex uses Ferret to index all available RI documentation and lets you do full text searches over the index.
E.g. IHelpIndex.new.search("domain name lookup")
See Ferret::QueryParser for query string format.
GLOBAL_INDEX_PATH | = | File.join(File.dirname(__FILE__), "ihelp.index") | Default place to save and load the index from. | |
LOCAL_INDEX_PATH | = | File.join(ENV["HOME"], ".ihelp.index") | ||
DEFAULT_LOG_LEVEL | = | 2 |
index | [RW] | The search index, is a Ferret::Index::Index |
log_level | [RW] | |
reindex_when_needed | [RW] |
# File lib/ihelp.rb, line 601 601: def initialize(use_global_index = (ENV["USER"] == "root")) 602: @log_level = DEFAULT_LOG_LEVEL 603: if use_global_index 604: @index_path = GLOBAL_INDEX_PATH 605: else 606: @index_path = LOCAL_INDEX_PATH 607: if not File.exist?(@index_path) and File.exist?(GLOBAL_INDEX_PATH) 608: FileUtils.cp_r(GLOBAL_INDEX_PATH, @index_path) rescue nil 609: elsif not File.exist?(@index_path) 610: self.class.reindex_when_needed = true 611: end 612: end 613: analyzer = IHelpAnalyzer.new 614: have_index = File.exist? @index_path 615: if have_index 616: log "Loading existing index from #{@index_path}", 1 617: @index = Ferret::I.new( 618: :key => :full_name, 619: :path => @index_path, 620: :analyzer => analyzer 621: ) 622: else 623: log "Creating new index in #{@index_path}", 2 624: field_infos = Ferret::Index::FieldInfos.new(:term_vector => :no) 625: field_infos.add_field(:name, 626: :store => :yes, 627: :index => :yes, 628: :boost => 10.0) 629: field_infos.add_field(:full_name, 630: :store => :yes, 631: :index => :untokenized, 632: :boost => 0.0) 633: field_infos.add_field(:content, 634: :boost => 1.0, 635: :store => :yes, 636: :index => :yes, 637: :term_vector => :with_positions_offsets) 638: @index = Ferret::I.new( 639: :key => :full_name, 640: :field_infos => field_infos, 641: :analyzer => analyzer 642: ) 643: end 644: if self.class.reindex_when_needed and need_reindexing? 645: reindex! 646: end 647: unless have_index 648: @index.persist(@index_path) 649: log "\nSaved index.", 2 650: end 651: end
# File lib/ihelp.rb, line 684 684: def all_names 685: IHelp.ri_driver.ri_reader.all_names.uniq 686: end
Returns true if there already is an object named full_name
# File lib/ihelp.rb, line 670 670: def already_indexed? full_name 671: @index[full_name] 672: end
# File lib/ihelp.rb, line 742 742: def display 743: return @display if @display 744: @display = IHelp.ri_driver.display 745: if ENV["PAGER"].to_s.size > 0 746: unless ENV["PAGER"] =~ /(^|\/)less\b.* -[a-zA-Z]*[rR]/ 747: IHelp.no_colors = true 748: end 749: else 750: ENV["PAGER"] = "less -R" 751: end 752: class << @display 753: public :page 754: attr_accessor :formatter 755: end 756: @display 757: end
# File lib/ihelp.rb, line 674 674: def format_time(t) 675: sec = t % 60 676: min = (t / 60) % 60 677: hour = (t / 3600) 678: str = sec.to_i.to_s.rjust(2,'0') 679: str = min.to_i.to_s.rjust(2,'0') + ":" + str 680: str = hour.to_i.to_s.rjust(2,'0') + ":" + str if hour >= 1 681: str 682: end
# File lib/ihelp.rb, line 653 653: def log str, level = 1 654: STDERR.puts str if level >= @log_level 655: end
Reindexes any non-indexed help documents.
# File lib/ihelp.rb, line 694 694: def reindex! 695: start_size = @index.size 696: names = all_names 697: log "Indexing...", 2 698: nsz = names.size.to_s 699: i = 0 700: names.each{|n| 701: i += 1 702: next if (start_size > 0) and (already_indexed?(n)) 703: if @log_level == DEFAULT_LOG_LEVEL 704: amt_done = (i.to_f / names.size) 705: pct = ("%.1f" % [100*amt_done]).rjust(5) 706: STDERR.write "\r #{pct}% (#{i.to_s.rjust(nsz.size)}/#{nsz}) #{n}".ljust(80)[0,80] 707: STDERR.flush 708: end 709: hd = if n.include? "#" 710: IHelp.ri_driver.get_info_str(*(n.split("#") + [true])) 711: elsif n.include? "::" 712: IHelp.ri_driver.get_info_str(n) or 713: IHelp.ri_driver.get_info_str(*n.reverse.split("::",2).reverse.map{|r| r.reverse }) 714: else 715: IHelp.ri_driver.get_info_str n 716: end 717: if hd.nil? 718: log "skipping #{n} (not found)" 719: next 720: else 721: log "indexing #{n}" 722: end 723: @index << { 724: :name => hd.full_name, 725: :full_name => n, 726: :content => wrap_str(hd.to_text) 727: } 728: } 729: if @log_level == DEFAULT_LOG_LEVEL 730: amt_done = (i.to_f / names.size) 731: pct = ("%.1f" % [100*amt_done]).rjust(5) 732: STDERR.write "\r #{pct}% (#{i.to_s.rjust(nsz.size)}/#{nsz}) #{names.last}".ljust(80)[0,80] 733: STDERR.flush 734: STDERR.puts 735: end 736: if start_size != @index.size 737: log "Optimizing index... (old size #{start_size}, current size #{@index.size})" 738: @index.optimize 739: end 740: end
Searches for the terms in query and prints out first ten hits
See Ferret::QueryParser for query string format.
# File lib/ihelp.rb, line 784 784: def search(query) 785: result = @index.search(query, :limit => 1000) 786: init_display 787: if IHelp.no_colors or `which less`.strip.empty? 788: pre_tag = "<<" 789: post_tag = ">>" 790: else 791: name_start = "\033[32m" 792: name_end = "\033[0m" 793: pre_tag = "\033[36m" 794: post_tag = "\033[0m" 795: end 796: page do 797: puts 798: puts "=== #{result.total_hits} hits#{", showing first 1000" if result.total_hits > 1000} ".ljust(72,"=") 799: puts 800: result.hits.each_with_index do |hit, i| 801: id, score = hit.doc, hit.score 802: #puts hit.score 803: puts "#{name_start}#{@index[id][:full_name]}#{name_end}" 804: puts 805: puts @index.highlight(query, id, :field => :content, 806: :pre_tag => pre_tag, 807: :post_tag => post_tag, 808: :ellipsis => "...\n").to_s 809: puts 810: display.formatter.draw_line 811: puts 812: end 813: puts "#{result.total_hits} hits" 814: end 815: p result.hits.map{|hit| @index[hit.doc][:full_name] } 816: result 817: end
# File lib/ihelp.rb, line 661 661: def time 662: t = Time.now 663: rv = yield 664: log "Took #{Time.now - t}" 665: rv 666: end