FESS で収集した情報を Ruby で取り出す

FESS で収集した情報を Ruby で取り出す
=====================================

[機械学習関連情報の収集と分類(構想)](http://qiita.com/suchowan/items/459062590f7134dfc138)の [FESS](http://fess.codelibs.org/ja/) / [Elasticsearch]( https://www.elastic.co) 関連部分の詳細です。

実際に調べた時点から半年ほど時間が経っていますので、現状と若干ずれがあるかもしれません。

(1) 記事のスキーマ
------------------

FESS はクロールした記事の管理に Elasticsearch を使っています。そのスキーマ構造はドキュメントを探しても見つかりませんでした。

が…、FESS の管理画面( http://localhost:8090/admin/dashboard/ [^1] ) で、more -> analysis を選択すると ANALYZE BY FIELD TYPE というプルダウンが現れるので、index name / type /field name をプルダウンしてスキーマ構造を確認することができるようです。

結果、/fess/doc に記事本体があると推定できたので、FESS の管理画面( http://localhost:8090/admin/dashboard/  [^1]) で、rest を選択し、/fess/doc/_serach で GET すると、その内容を見ることができました。

JSON で表現して、下記の例のようになっています。

```
{
  "_index": "fess",
  "_type": "doc",
  "_id": "http://b.hatena.ne.jp/entry/www.publickey1.jp/blog/14/tamr.html;role=guest",
  "_score": 1,
  "_source": {
    "title": "Webページタイトル",
    "config_id": "WAVNT47Q1yptIFHWqnp2t",
    "expires": "2016-03-22T03:00:19.572Z",
    "lang": "ja",
    "content": "タグを除去したWebページの内容",
    "cash": "生のWebページの内容",
    "has_cache": "true",
    "digest": "検索結果一覧に表示する要約文",
    "segment": "20160322120000",
    "host": "news.nifty.com",
    "site": "news.nifty.com/cs/technology/techalldetail/yomi...",
    "url": "http://news.nifty.com/cs/technology/techalldetail/yomiuri-20160321-50078/1.htm",
    "created": "2016-03-22T03:10:08.561Z",
    "anchor":["このWebページからリンクしているWebページのURL"],
    "mimetype": "text/html",
    "filetype": "html",
    "content_length": "179232",
    "last_modified": "2016-03-08T02:48:36.000Z",
    "timestamp": "2016-03-08T02:48:36.000Z",
    "boost": "1.0",
    "label": [],
    "role": ["guest"],
    "parent_id": "http://ai.paint-ink.com/;role=guest",
    "click_count": 0,
    "favorite_count": 0,
    "doc_id": "e0d87bae582c4a8d84ff5270e593da0c"
  }
}, ...
```

(2) 新規検索結果の取得
----------------------

netstat で確認したところ Elasticsearch が port 9201[^1] で待ち受けていることが分かったので、localhost:9201 に対してREST request を行って、FESS 外からクロール結果を取得してみました。

過去4時間に更新されたWebページを全て取得する REST request は、

```
curl -XPOST 'http://localhost:9201/fess/doc/_search?pretty=true' -d '{
  "query": {
    "range": {
      "last_modified": {
        "gt": "now-4h"
      }
    }
  }
}'
```

となります[^2]。

(3) 形態素解析
--------------

前項で取得したクロール結果の _source/content を kuromoji で形態素に分解してみます[^3]。

(4) 取得スクリプト
------------------

過去4時間に更新されたWebページを全て取得して形態素解析をかけて、Fess::Docオブジェクトに格納する Ruby スクリプトは、


```rb
# -*- coding: utf-8 -*-

require 'rest-client'
require 'json'
require 'rjb'

Encoding.default_external = 'UTF-8'

module Kuromoji

  Rjb::load('kuromoji-0.7.7.jar')
  tokenizer = Rjb::import('org.atilika.kuromoji.Tokenizer')
  @@tknizer = tokenizer.builder.build

  def self.tokenize(sentence)
    list = []
    it   = @@tknizer.tokenize(sentence).iterator
    while it.has_next
      list << it.next
    end
    list
  end
end

module Fess

  URL    = 'http://localhost:9201'
  QUERY  = <<QUERY
{
  "query" : {
    "range" : {
       "last_modified" : {
         "gt" : "%s"
      }
    }
  }
}
QUERY

  @@client = RestClient::Resource.new(URL)

  class Doc

    attr_reader :tokens

    def initialize(doc)
      @doc = doc
      doc.keys.each do |key|
        instance_eval  %Q{
          def #{key.sub(/^_/,'')}
            @doc['#{key}']
          end
        }
      end
      @tokens = Kuromoji.tokenize(source['content'])
    end
  end

  def self.retrieve(edge)
    result = @@client['/fess/doc/_search?pretty=true'].post(QUERY % edge)
    json   = JSON.parse(result.body)
    json['hits']['hits'].map {|doc| Doc.new(doc)}
  end
end

Fess.retrieve('now-4h').each do |doc|
  print [doc.id, doc.source['last_modified'], doc.source['title']]
  doc.tokens[0..10].each do |x|
    print x.surface_form
    print " : "
    puts x.all_features
  end
end
```

といった感じです[^4]。

[^1]: localhost とポート 8090,9201 は適当に読み替えてください。

[^2]: →[Ranges](https://www.elastic.co/guide/en/elasticsearch/guide/current/_ranges.html)

[^3]: →[rubyから形態素解析ライブラリkuromojiを使う](http://qiita.com/k-shogo/items/61d76a9a590b65887f77)

[^4]: このスクリプトでは kuromoji-0.7.7.jar をカレントディレクトリに配置しています。実際の環境にあわせください。