Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 2nd, 2012  |  syntax: None  |  size: 7.89 KB  |  hits: 15  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. # -*- coding: utf-8 -*-
  2.  
  3. require 'yaml'
  4. require 'pathname'
  5. require 'rubygems'
  6. require 'groonga'
  7. require 'fileutils'
  8. require 'pathname'
  9. require 'cdstk/cdstk_yaml'
  10. require 'common/grenfiletest'
  11. require 'common/util'
  12. include CodeStock
  13. require 'kconv'
  14.  
  15. module CodeStock
  16.   class Cdstk
  17.     DB_FILE_PATH = 'db/grendb.db'
  18.    
  19.     # バイグラムでトークナイズする。連続する記号・アルファベット・数字は一語として扱う。
  20.     # DEFAULT_TOKENIZER = "TokenBigram"
  21.  
  22.     # 記号・アルファベット・数字もバイグラムでトークナイズする。
  23.     DEFAULT_TOKENIZER = "TokenBigramSplitSymbolAlphaDigit"
  24.    
  25.     def initialize(io = $stdout, db_dir = ".")
  26.       @db_dir = db_dir
  27.       @out = io
  28.       @file_count = 0
  29.       @add_count = 0
  30.       @update_count = 0
  31.       @start_time = Time.now
  32.     end
  33.  
  34.     def init
  35.       if Dir.entries(@db_dir) == [".", ".."]
  36.         CdstkYaml.create(@db_dir)
  37.         @out.puts "create     : #{yaml_file}"
  38.         db_create(db_file)
  39.       else
  40.         @out.puts "Can't create Grendb Database (Not empty) in #{@db_dir}"
  41.       end
  42.     end
  43.  
  44.     def update
  45.       print_result do
  46.         yaml = yaml_load
  47.         db_open(db_file)
  48.  
  49.         yaml.contents.each do |content|
  50.           update_dir_in(content["directory"])
  51.         end
  52.       end
  53.     end
  54.  
  55.     def update_dir(dir)
  56.       print_result do
  57.         update_dir_in(dir)
  58.       end
  59.     end
  60.  
  61.     def add(*content)
  62.       # 絶対パスに変換
  63.       content.map!{|v|File.expand_path(v)}
  64.  
  65.       # YAML更新
  66.       yaml = yaml_load
  67.       yaml.add(*content)
  68.       yaml.save
  69.  
  70.       # 部分アップデート
  71.       db_open(db_file)
  72.       content.each do |dir|
  73.         update_dir(dir)
  74.       end
  75.     end
  76.  
  77.     def remove(*content)
  78.       # 絶対パスに変換
  79.       content.map!{|v|File.expand_path(v)}
  80.  
  81.       # YAML更新
  82.       yaml = yaml_load
  83.       yaml.remove(*content)
  84.       yaml.save
  85.  
  86.       # @todo 削除したコンテンツをインデックスから削除
  87.     end
  88.  
  89.     def list
  90.       @out.puts yaml_load.list
  91.     end
  92.  
  93.     def rebuild
  94.       db_delete(db_file)
  95.       db_create(db_file)
  96.       update
  97.     end
  98.  
  99.     def dump
  100.       db_open(db_file)
  101.      
  102.       documents = Groonga::Context.default["documents"]
  103.       records = documents.select
  104.       records.each do |record|
  105.         @out.puts record.inspect
  106.         @out.puts "path : #{record.path}"
  107.         @out.puts "shortpath : #{record.shortpath}"
  108.         @out.puts "suffix : #{record.suffix}"
  109.         @out.puts "timestamp : #{record.timestamp.strftime('%Y/%m/%d %H:%M:%S')}"
  110.         @out.puts "content :", record.content ? record.content[0..64] : nil
  111.         @out.puts
  112.       end
  113.     end
  114.  
  115.     private
  116.  
  117.     def db_file
  118.       (Pathname.new(@db_dir) + DB_FILE_PATH).to_s
  119.     end
  120.  
  121.     def yaml_file
  122.       CdstkYaml.yaml_file @db_dir
  123.     end
  124.  
  125.     def yaml_load
  126.       CdstkYaml.load(@db_dir)
  127.     end
  128.  
  129.     def update_dir_in(dir)
  130.       dir = File.expand_path(dir)
  131.  
  132.       if (!FileTest.exist?(dir))
  133.         @out.puts "[WARNING]  : #{dir} (Not found, skip)"
  134.       elsif (FileTest.directory? dir)
  135.         db_add_dir(dir)
  136.       else
  137.         db_add_file(STDOUT, dir, File.basename(dir))
  138.       end
  139.     end
  140.  
  141.     def time
  142.       @end_time - @start_time
  143.     end
  144.  
  145.     def print_result
  146.       yield
  147.      
  148.       @end_time = Time.now
  149.      
  150.       @out.puts
  151.       @out.puts "time       : #{Gren::Util::time_s(time)}"
  152.       @out.puts "files      : #{@file_count}"
  153.       @out.puts "add        : #{@add_count}"
  154.       @out.puts "update     : #{@update_count}"
  155.     end
  156.  
  157.     def db_create(filename)
  158.       dbfile = Pathname(File.expand_path(filename))
  159.       dbdir = dbfile.dirname
  160.       dbdir.mkpath unless dbdir.exist?
  161.      
  162.       unless dbfile.exist?
  163.         Groonga::Database.create(:path => dbfile.to_s)
  164.         Groonga::Schema.define do |schema|
  165.           schema.create_table("documents") do |table|
  166.             table.string("path")
  167.             table.string("shortpath")
  168.             table.text("content")
  169.             table.time("timestamp")
  170.             table.text("suffix")
  171.           end
  172.  
  173.           schema.create_table("terms",
  174.                               :type => :patricia_trie,
  175.                               :key_normalize => true,
  176.                               :default_tokenizer => DEFAULT_TOKENIZER) do |table|
  177.             table.index("documents.path", :with_position => true)
  178.             table.index("documents.shortpath", :with_position => true)
  179.             table.index("documents.content", :with_position => true)
  180.             table.index("documents.suffix", :with_position => true)
  181.           end
  182.         end
  183.         @out.puts "create     : #{filename} created."
  184.       else
  185.         @out.puts "message    : #{filename} already exist."
  186.       end
  187.     end
  188.  
  189.     def db_open(filename)
  190.       dbfile = Pathname(File.expand_path(filename))
  191.      
  192.       if dbfile.exist?
  193.         Groonga::Database.open(dbfile.to_s)
  194.         @out.puts  "open       : #{dbfile} open."
  195.       else
  196.         raise "error      : #{dbfile.to_s} not found!!"
  197.       end
  198.     end
  199.  
  200.     def db_delete(filename)
  201.       raise "Illegal file name : #{filename}." unless filename =~ /\.db$/
  202.       Dir.glob("#{filename}*").each do |f|
  203.         @out.puts "delete     : #{f}"
  204.         FileUtils.rm_r(f)
  205.       end
  206.     end
  207.     private :db_delete
  208.      
  209.     def db_add_dir(dirname)
  210.       searchDirectory(STDOUT, dirname, File.basename(dirname), 0)
  211.     end
  212.     private :db_add_dir
  213.  
  214.     def db_add_file(stdout, filename, shortpath)
  215.       # 格納するデータ
  216.       values = {
  217.         :path => filename,
  218.         :shortpath => shortpath,
  219.         :content => nil,
  220.         :timestamp => File.mtime(filename),
  221.         :suffix => File::extname(filename),
  222.       }
  223.      
  224.       # 検索するデータベース
  225.       documents = Groonga::Context.default["documents"]
  226.      
  227.       # 既に登録されているファイルならばそれを上書き、そうでなければ新規レコードを作成
  228.       _documents = documents.select do |record|
  229.         record["path"] == values[:path]
  230.       end
  231.      
  232.       isNewFile = false
  233.  
  234.       if _documents.size.zero?
  235.         document = documents.add
  236.         isNewFile = true
  237.       else
  238.         document = _documents.to_a[0].key
  239.       end
  240.      
  241.       # タイムスタンプが新しければデータベースに格納
  242.       if (document[:timestamp] < values[:timestamp])
  243.         # 実際に使うタイミングでファイルの内容を読み込み
  244.         # values[:content] = open(filename).read
  245.         values[:content] = File.read(filename).kconv(Kconv::UTF8)
  246.        
  247.         # データベースに格納
  248.         values.each do |key, value|
  249.           if (key == :path)
  250.             if (isNewFile)
  251.               @add_count += 1
  252.               @out.puts "add_file   : #{value}"
  253.             else
  254.               @update_count += 1
  255.               @out.puts "update     : #{value}"
  256.             end
  257.           end
  258.           document[key] = value
  259.         end
  260.       end
  261.  
  262.     end
  263.  
  264.     def searchDirectory(stdout, dir, shortdir, depth)
  265.       Dir.foreach(dir) do |name|
  266.         next if (name == '.' || name == '..')
  267.          
  268.         fpath = File.join(dir,name)
  269.         shortpath = File.join(shortdir,name)
  270.        
  271.         # 除外ディレクトリならばパス
  272.         next if ignoreDir?(fpath)
  273.  
  274.         # 読み込み不可ならばパス
  275.         next unless FileTest.readable?(fpath)
  276.  
  277.         # ファイルならば中身を探索、ディレクトリならば再帰
  278.         case File.ftype(fpath)
  279.         when "directory"
  280.           searchDirectory(stdout, fpath, shortpath, depth + 1)
  281.         when "file"
  282.           unless ignoreFile?(fpath)
  283.             db_add_file(stdout, fpath, shortpath)
  284.             @file_count += 1
  285.             @out.puts "file_count : #{@file_count}" if (@file_count % 100 == 0)
  286.           end
  287.         end          
  288.       end
  289.     end
  290.  
  291.     def ignoreDir?(fpath)
  292.       FileTest.directory?(fpath) &&
  293.       GrenFileTest::ignoreDir?(fpath)
  294.     end
  295.     private :ignoreDir?
  296.  
  297.     def ignoreFile?(fpath)
  298.       GrenFileTest::ignoreFile?(fpath) ||
  299.       GrenFileTest::binary?(fpath)
  300.     end
  301.     private :ignoreFile?
  302.  
  303.   end
  304. end