昨日の続き。
繰り返しになるが、解決したい課題と現状は、
- 数ギガサイズのアクセスログがある
- 上記に対して数万パターンの文字列それぞれが何回出現するかを数え上げたい
という状況。なので、このログファイルをngroongaを介してgroongaのデータベースを新規作成し、そこに格納してみる。
もちろん事前にGroongaのインストールが必要なのでインストールする。Macであればbrewでインストールすることができる。
$ brew install groonga
で、次に、
- Groonga DB作成
- DBへデータを格納するためのテーブル作成
- 今回はシンプルに1テーブルにbodyという1カラムを作ってそこにアクセスログ1行分を格納する
- 上記のテーブル中のカラムを全文検索するための語彙表(インデックス)用のテーブル作成
- テキストファイルベースのログファイルを格納
という上記の一通りの作業は以下のコードで実現できる。
var nroonga = require('nroonga'), byline = require('byline'), fs = require('fs'); var db = new nroonga.Database('db/accesslog'); db.commandSync('table_create Logs TABLE_NO_KEY'); db.commandSync('column_create Logs body --type Text'); db.commandSync('table_create', {'name':'LogIndexes', 'flags':'TABLE_PAT_KEY', 'key_type':'ShortText', 'default_tokenizer':'TokenBigram', 'normalizer':'NormalizerAuto'}); db.commandSync('column_create', {'table':'LogIndexes', 'name':'body_index', 'flags':'COLUMN_INDEX|WITH_POSITION', 'type':'Logs', 'source':'body'}); var stream = fs.createReadStream('access.log', { encoding: 'utf8' }); stream = byline.createStream(stream); stream.on('data', function(line) { var log = JSON.stringify([ {'body':line} ]); db.commandSync("load", {'table':'Logs', 'values': log}); }); stream.on('finish', function(){ console.log("Finish parse log file."); db.close(); });
上記の格納が完了した後*1、実際に格納した情報にたいして全文検索するには以下の様に select
のクエリを発行する。limit:0
を指定することでGroongaでは件数のみを取得できるので、例えば今回格納したログファイル中に「Windows」という文字列が何回出現したかということを得たい場合は以下の様な形になる。
var nroonga = require('nroonga'); var db = new nroonga.Database('db/accesslog'); var res = db.commandSync('select', {'table': 'Logs', 'match_columns':'body', 'query': 'Windows', 'limit':0}); console.log(res); //=> [ [ [ 1869923 ], [ [Object], [Object] ] ] ]
かなり気楽に全文検索できる状態を作れる。最初スクリプトとgrepツールで解決しようと思ったが少し時間がかかりすぎなところがあったので対象を絞ることを考えていたりしたが自分が解決したかったことは解決できそう。
例えばサービスの方針等々を検討する際の数値集めが簡単に行える様にするためにログ基盤を整える等が最も正しい解決策だとは思うのだが、ひとまず何かしらログが残されていれば手元にこういった環境を作って調べてみるのも一つのやり方かなと思う。