まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2016/07

(注 今回のエントリは以下の内容が古くなってしまったため、新たに書き直したものです)



全文検索エンジン ElasticSearch に日本語形態素解析ソフトウェア Kuromoji のプラグインを導入して日本語全文検索エンジンを作ります。なおプラットフォームは CentOS 6 を前提とします。


まず ElasticSearch の動作に必要な Java 環境を用意します。Oracle Java を導入しても構いませんが、Open Java であれば以下のコマンドで導入可能です(Java 7 の場合):
# yum install java-1.7.0-openjdk

次に検索エンジンである ElasticSearch 本体を導入します。公式サイトから最新版(2016/07/11時点では 2.3.4)のインストールモジュールをダウンロードします。インストールモジュールにはいくつかの種類はありますが、今回は rpm パッケージ版(elasticsearch-2.3.4.rpm)をダウンロードします:
https://www.elastic.co/downloads/elasticsearch

2016071101


ダウンロードできたら rpm コマンドでインストールして起動、および自動起動設定までを行います。ちなみにこの rpm 版をインストールした場合、ElasticSearch 本体は /usr/share/elasticsearch/ 以下に導入されます :
# rpm -ivh elasticsearch-2.3.4.rpm
# /etc/init.d/elasticsearch start
# chkconfig elasticsearch on

これで ElasticSearch 本体の導入は完了しました。続けて Kuromoji (とウェブ GUI である head)を導入します。いったん ElasticSearch を止めた上で本体のディレクトリに移動し、それぞれのプラグインを導入します:
# /etc/init.d/elasticsearch stop
# cd /usr/share/elasticsearch
# bin/plugin install analysis-kuromoji
# bin/plugin install mobz/elasticsearch-head

設定ファイル(/etc/elasticsearch/elasticsearch.yml)を編集します。まずデフォルトの状態だとウェブ GUI にはローカルホストからでないとアクセスできません。何かと不便なので外部からもアクセスできるように変更します(以下の例では全てのホストからのアクセスを許可しています)。また解析エンジンのデフォルトとして Kuromoji を利用するよう変更します:
(/etc/elasticsearch/elasticsearch.yml)

# allow http connection from every hosts
network.host: 0.0.0.0

# set kuromoji as default Tokenizer
index.analysis.analyzer.default.type: custom
index.analysis.analyzer.default.tokenizer: kuromoji_tokenizer

この状態で ElasticSearch を起動すれば日本語全文検索エンジンとしての ElasticSearch が、ウェブ GUI 付きで稼働します:
# /etc/init.d/elasticsearch start

ウェブ GUI にアクセスするにはウェブブラウザから http://(ElasticSearch のホスト):9200/_plugin/head/ にアクセスします:

2016071102


では実際に日本語データを挿入して検索してみましょう。以下の内容のテキストを manho.json という名前で保存します:
(manho.json)

{"create":{"_id":"4442072443477838363"}}
{"id": 4442072443477838363,"username": "michelle_yama","created": "2016/07/11 12:49:25","updated": "2016/07/11 12:49:25","tag": "","filename": "","type": "image/jpeg","address": "","text": "","lat": 35.6565093994141,"lng": 139.752868652344,"nice": 0,"niceby": "","width": 480,"height": 360,"imgkey": "","misc": "東京都港区芝大門2丁目1"}

{"create":{"_id":"5545583388040541151"}}
{"id": 5545583388040541151,"username": "42ER03","created": "2016/07/11 05:59:20","updated": "2016/07/11 05:59:20","tag": "市章と市の花「ツツジ」","filename": "20051119兵庫県加古川市山手2丁目29-12地先_ .jpg","type": "image/jpeg","address": "","text": "平成17年11月撮影","lat": 34.78293426048821,"lng": 134.88915952863033,"nice": 0,"niceby": "","width": 480,"height": 480,"imgkey": "","misc": "兵庫県加古川市山手2丁目27"}

{"create":{"_id":"1831807387175567807"}}
{"id": 1831807387175567807,"username": "ippei0605","created": "2016/07/10 23:07:24","updated": "2016/07/11 16:01:52","tag": "居合の稽古","filename": "image.jpeg","type": "image/jpeg","address": "","text": "","lat": 35.76108611111111,"lng": 139.5356611111111,"nice": 1,"niceby": "dotnsf","width": 480,"height": 360,"imgkey": "","misc": "東京都東久留米市新川町1丁目10 都道125号線"}

{"create":{"_id":"3546975096233083798"}}
{"id": 3546975096233083798,"username": "dotnsf","created": "2016/07/10 22:28:51","updated": "2016/07/10 22:28:51","tag": "hiroshima shobara","filename": "hilside.jpg","type": "image/jpeg","address": "","text": "国営備北丘陵公園内のヒルサイドパークマンホール","lat": 34.83977596170017,"lng": 132.99681892575074,"nice": 0,"niceby": "","width": 269,"height": 480,"imgkey": "","misc": "広島県庄原市上原町1300"}

{"create":{"_id":"4946513086810703103"}}
{"id": 4946513086810703103,"username": "minamu4545","created": "2016/07/10 21:06:11","updated": "2016/07/10 21:08:31","tag": "","filename": "image.jpeg","type": "image/jpeg","address": "","text": "某水泳アニメの聖地となった岩美町の色蓋(おすい)。<br/>浦富海岸が描かれて美しい蓋。此の蓋もかなり美しいですが、實際の海岸はもっと美しかったです。<br/>色蓋は浦富海岸沿いの縣道155號線にたくさん設置されてますが、路上なので状態は惡いものが多いです。<br/>此の蓋もガムが付いて無ければ完璧だったのですが殘念です。蓋にガム捨てるのほんとやめて欲しい。<br/>","lat": 35.59056896685199,"lng": 134.32106172622503,"nice": 0,"niceby": "","width": 480,"height": 480,"imgkey": "","misc": "鳥取県岩美郡岩美町大字浦富 県道155号線"}

{"create":{"_id":"7542390449304551253"}}
{"id": 7542390449304551253,"username": "42ER03","created": "2016/07/10 07:50:19","updated": "2016/07/10 07:50:19","tag": "ビルバオ市の市章","filename": "20140731③ビルバオ03_.jpg","type": "image/jpeg","address": "","text": "2014年07月撮影/グッゲンハイム美術館付近","lat": 43.26813637672919,"lng": -2.9336467575902736,"nice": 0,"niceby": "","width": 480,"height": 480,"imgkey": "","misc": "スペイン"}

{"create":{"_id":"8484297972018100687"}}
{"id": 8484297972018100687,"username": "42ER03","created": "2016/07/09 08:17:30","updated": "2016/07/09 08:17:30","tag": "高崎まつり","filename": "20160610,1156,03群馬県高崎市高崎駅東口_.jpg","type": "image/jpeg","address": "","text": "平成28年06月撮影","lat": 36.32132769063828,"lng": 139.01428243101884,"nice": 0,"niceby": "","width": 480,"height": 480,"imgkey": "","misc": "群馬県高崎市栄町16"}

{"create":{"_id":"4255462064362665608"}}
{"id": 4255462064362665608,"username": "minamu4545","created": "2016/07/08 19:42:02","updated": "2016/07/08 19:43:49","tag": "","filename": "image.jpeg","type": "image/jpeg","address": "","text": "にかほ市(旧象潟町)小滝地區の農業集落排水の色蓋。<br/>描かれているのは奈曽の白滝。<br/>奈曽の白滝の駐車場前の歩道に設置されていますが劣化が激しいです。<br/>ちなみに、駐車場の横には農業集落排水の処理場があります。","lat": 39.18138258105665,"lng": 139.94690930427362,"nice": 0,"niceby": "","width": 480,"height": 480,"imgkey": "","misc": "秋田県にかほ市象潟町小滝"}

{"create":{"_id":"5394969523646972723"}}
{"id": 5394969523646972723,"username": "ujiteaa","created": "2016/07/08 06:08:46","updated": "2016/07/08 06:08:46","tag": "埼玉県川越市旭町","filename": "_20160708_053352.JPG","type": "image/jpeg","address": "","text": "","lat": 35.690625,"lng": 139.699788,"nice": 0,"niceby": "","width": 473,"height": 480,"imgkey": "","misc": "東京都新宿区西新宿1丁目1"}

{"create":{"_id":"3662100910616235567"}}
{"id": 3662100910616235567,"username": "ujiteaa","created": "2016/07/08 06:07:30","updated": "2016/07/08 06:07:30","tag": "埼玉県川越市","filename": "_20160708_053352.JPG","type": "image/jpeg","address": "","text": "","lat": 35.690625,"lng": 139.699788,"nice": 0,"niceby": "","width": 473,"height": 480,"imgkey": "","misc": "東京都新宿区西新宿1丁目1"}

この内容をバルク API でまとめて挿入します。以下の例ではインデックスに manholes、タイプに geo を指定しています:
# curl -XPOST http://localhost:9200/manholes/geo/_bulk --data-binary @manho.json

ではこの状態で「美しさ」というキーワードで検索してみましょう:
# curl -X GET http://localhost:9200/manholes/geo/_search -d '{"query":{"match":{"text":"美しさ"}}}'

{
  :
 "hits":{
  "total":2,
  "max_score":0.045926645,
  "hits":[
   {
    "_index":"manholes",
    "_type":"geo",
    "_id":"4946513086810703103",
    "_score":0.045926645,
    "_source":{
     "id": 4946513086810703103,
       :
     "text": "某水泳アニメの聖地となった岩美町の色蓋(おすい)。&lt;br/&gt;浦 富海岸が描かれて美しい蓋。此の蓋もかなり美しいですが、實際の海岸はもっと美しかったです。&lt;br/&gt;色蓋は浦富海岸沿いの縣道155號線にたくさん設置されてますが、路上なので状態は惡いものが多いです。&lt;br/&gt;此の蓋もガムが付いて無ければ完璧だ ったのですが殘念です。蓋にガム捨てるのほんとやめて欲しい。&lt;br/&gt;",
       :
   },
   {
    "_index":"manholes",
    "_type":"geo",
    "_id":"4255462064362665608",
    "_score":0.0049227546,
    "_source":{
     "id": 4255462064362665608,
       :
     "text": "にかほ市(旧象潟町)小滝地區の農業集落排水の色蓋。&lt;br/&gt;描かれているのは奈曽の白滝。&lt;br/&gt;奈曽の白滝の駐車場前の歩道に設置されていますが劣化が激しいです。&lt;br/&gt;ちなみに、駐 車場の横には農業集落排水の処理場があります。",
       :
   }
  ]
 }
}

「美しさ」で完全一致するデータは存在しませんが、「美しい」でヒットしたようです。正しく日本語が形態素解析されて判断されていることがわかります。

というわけで、日本語全文検索エンジンの出来上がり!


WordPress のデータベースから SQL でデータを取り出すシリーズ(?)の応用編。今回のテーマは
 ポストした本文とタイトルと、その文書に付与されたカテゴリの一覧だけを取り出す
というものです。

最初に結論を書いておきます。この SQL で目的を達成できるはず(公開データと未公開データの両方を取り出しています):
select wp_posts.post_content as content, wp_posts.post_title, wp_terms.name as category
 from wp_posts, wp_terms, wp_term_relationships
 where wp_posts.post_type = 'post' 
 and ( wp_posts.post_status = 'publish' or wp_posts.post_status = 'draft' )
 and wp_posts.ID = wp_term_relationships.object_id
 and wp_term_relationships.term_taxonomy_id = wp_terms.term_id;

説明する上で、WordPress のテーブルの相関関係を理解しておく必要があります。関連図についてはこちらを参照ください:
データベース構造 - WordPress Codex 日本語版


まず WordPress にポストしたレコードデータは wp_posts テーブル内に格納されています。特に(固定ページなどではなく)ポストデータは
 post_type = 'post'
となっているものが該当します。

そして今回は公開済みレコードと未公開(ドラフト)レコードの両方を取り出すことにしました。公開状況は同テーブルの post_status に格納されており、
 post_status = 'publish' or post_status = 'draft'
のいずれかの条件を満たしているものを取り出し、そこから本文(post_content)とタイトル(post_title)を取りだせばよい、ということになります。もしもドラフトが不要な場合はこの行の or 以降は不要です。

取り出すレコードの選別はこれだけです。次に各レコードに紐付けられたカテゴリ名称を取り出す必要があります。

カテゴリーのデータは wp_terms テーブルに(カテゴリ名は name 列に)含まれています。そしてどの文書がどのカテゴリに属しているのか、というリレーションは wp_term_relationships テーブルに格納されており、同テーブル内の object_id が wp_posts.ID 、term_taxonomy_id が wp_terms.term_id に該当しています。

例えば、wp_term_relationships テーブルに以下のようなレコードが存在していた場合、
object_idterm_taxonomy_id
41
61
82

wp_posts テーブルの ID が 4 の文書と 6 の文書は、wp_terms テーブルの term_id = 1 のカテゴリに属していて、ID が 8 の文書は term_id = 2 のカテゴリに属している、ということになります。

これらの関係を1つの SQL に書き直すと上記の SQL になります。
2016070701


WordPress に使っているデータベース(一般的には MySQL のデータベースだと思います)から、SQL を使って、ブログの名称や説明文を取り出す方法です:

2016070501
↑この図の赤枠部分がブログの名称、青枠部分が説明文です。


前提として、データベース作成時のテーブルのプレフィックスはデフォルトの 'wp_' をそのまま使っているものとします。つまりデータベース内には wp_posts とか wp_terms といったテーブルが存在している状態で利用中のものとします:
2016070502


で、ブログの名称は wp_options テーブル内に option_name の値が 'blogname' であるレコードの option_value 値として格納されています。したがってブログ名称を取り出す際の SQL は以下になります:
select wp_options.option_value as name from wp_options where wp_options.option_name = 'blogname';

2016070503
↑取り出せました


同様に、説明文は option_name の値が 'blogdescription' であるレコードの option_value 値です。したがって SQL だと以下になります:
select wp_options.option_value as name from wp_options where wp_options.option_name = 'blogdescription';
2016070504


この辺りがわかっていると、SQL でブログタイトルや説明を無理やり書き換える、ということも可能になります。


ダンジョン探索型 RPG の草分け的な存在でもある NetHack を CentOS にインストールしてみました。テキストキャラクターインターフェース RPG の元祖とも言える Rogue の後継で、いくつかの進化バージョンがありますが、今回紹介するのは UTF-8 に対応した日本語版 jNetHack 3.4.3 です。なお、今回導入した環境は CentOS 6.7 64bit ですが、(TTY 向けの導入しか紹介していないので)他の Linux ディストリビューションでもほぼ同様の方法で導入できると思います。


導入の方法ですが、現在は yum で簡単にインストール・・・ というわけにはいかないようで、必要なライブラリを揃えて、ソースコードを SVN からクローンして、パッチを充ててからビルドしてインストール、という手順が必要になるようです。以下にその手順を紹介します。

まずはビルド時に必要となるライブラリ群を yum で導入します。ここはコマンド一発:
# yum install -y libX11-devel ncurses-devel libXt-devel libXaw-devel

次に SourceForge から jNetHack のソースコードを入手し、更に UTF-8 対応用パッチを入手します。なお、以下の作業は全て /usr/local/src ディレクトリ以下で行うので、最初にこのディレクトリに移動しておきます:
# cd /usr/local/src

# svn co http://svn.sourceforge.jp/svnroot/jnethack/ jnh-svn
# wget http://elbereth.up.seesaa.net/nethack/jnethack-3.4.3-0.10-utf8-2.patch.bz2


ソースコードのディレクトリに移動し、UTF-8 パッチを適用します:
# cd ./jnh-svn/jnethack/trunk
# bzip2 -d /usr/local/src/jnethack-3.4.3-0.10-utf8-2.patch.bz2
# cat /usr/local/src/jnethack-3.4.3-0.10-utf8-2.patch | patch -p1

ビルド前に Linux 用にファイルを編集します。まず sys/unix/Makefile.top の該当業(青字部分)を編集します:
# vi sys/unix/Makefile.top

: # make NetHack PREFIX = /usr/local GAME = jnethack # GAME = nethack.prg GAMEUID = games GAMEGRP = bin # Permissions - some places use setgid instead of setuid, for instance # See also the option "SECURE" in include/config.h GAMEPERM = 04755 FILEPERM = 0644 EXEPERM = 0755 DIRPERM = 0755 :

次に sys/unix/Makefile.src の CLAGS を Linux 用に変更します:
# vi sys/unix/Makefile.src

: # flags for Linux # compile normally # CFLAGS = -O2 -fomit-frame-pointer -I../include : CFLAGS = -W -g -O2 -fomit-frame-pointer -I../include :

また include/unixconf.h 内で Linux 用の定義が一部無効になっているので、以下の4つをコメントアウトして有効にします:
# vi include/unixconf.h

: #define SYSV #define LINUX #define TERMINFO #define TIMED_DELAY :

同様に include/config.h 内の DLB の定義もコメントアウトして有効にします:
# vi include/config.h

: #define DLB :

これで準備は完了です。以下の手順で環境設定を有効にして、make all して make install します:
# cd sys/unix
# sh ./setup.sh
# cd ../../
# make all
# make install

なお、一度ゲームをスタートした後で、再度環境変更のビルドを行う場合、↑の最後のコマンドを make install から make update にすると、それまでのセーブデータを保護しながら本体だけをアップデートすることが可能です(make install だとセーブデータも上書きします)。

これで jNetHack が導入できました。jnethack コマンド本体は /usr/local/games/ にインストールされているので、フルパスで実行するか、このディレクトリにパスを通してください:
2016070301


実行時には UTF-8 対応の端末でログインし、この本体をコマンドラインから指定して実行します:
# /usr/local/games/jnethack

すると UTF-8 対応の jNetHack が実行されます:
2016070302


vi エディタでお馴染みの H,J,K,L による移動をベースとした RPG (の元祖)です。元々の Rogue が 1980 年に生まれたので、ゲームシステム自体は 36 年前から存在していることになります:
2016070303


NetHack 自体に関してはこちらの Wiki を参照ください:
https://ja.wikipedia.org/wiki/NetHack


 

HTTP2 に対応し、「デフォルト設定でも Nginx 並に速い」と噂の HTTP サーバー h2o を CentOS に導入してみました。

h2o は DeNA の奥氏を中心に開発されている HTTP サーバーです。h2o の誕生経緯に関してはこちらの資料にまとまっていたので、興味のある方は参照ください:
H2O - making HTTP better

h2o を CentOS にインストールする場合、現時点ではソースコードからビルドする必要があります。というわけで、まずはそのための前提ライブラリを導入します:
# yum groupinstall "Development Tools"
# yum install curl curl-devel libarchive libarchive-devel expat expat-devel zlib zlib-devel openssl cname
# rpm -Uvh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
# yum install libyaml-devel --enablerepo=rpmforge

その後、h2o のソースコードを git clone して、make して、ビルドして、インストール:
# cd /usr/local/src
# git clone https://github.com/h2o/h2o.git
# cd h2o
# cmake -DWITH_BUNDLED_SSL=on .
# make h2o
# make install

設定ファイルを用意します。今回はシンプルにこんな内容にしました:
(/usr/local/src/h2o/examples/h2o/h2o.conf の中身)

listen: 8080
hosts:
  default:
    paths:
      /:
        file.dir: examples/doc_root
    access-log: /dev/stdout

設定ファイルが用意できたら実行します。本体は /usr/local/src/h2o/h2o で、実行時に -c パラメータで設定ファイルを指定します:
# cd /usr/local/src/h2o
# ./h2o -c examples/h2o/h2o.conf

今回は 8080 番ポートで起動するような設定ファイルを用意したので、動作確認には 8080 番ポートへアクセスします:

2016063001
 ↑動いてます


このページのトップヘ