全文検索システムFessって何
オープンソース全文検索サーバー Fess
内部でElasticsearchを使っている全文検索システム。
全文検索=ファイルの中身本文、メタデータ等も含めてまるごと検索すること。
Fessが指示屋でElasticsearchがDB役と検索担当みたいな感じ。
商用の全文検索システムと比較しても遜色のない、軽くて早くてうまい優れもの。
(Fessも商用サポートサービスがある)
解決したい事柄
一晩に一回ファイルサーバをなめさせて検索DBの情報を更新している(クロールと呼ぶ)。
このジョブの開始終了時刻等のステータスはウェブの管理画面から見られるのだけど、日々の確認でいちいち管理画面開くのめどい。レポートにする時見て書き写すのもコピペるのもめどい。
クロール終了時のメール通知機能はあるのだが、そのためにメールサーバを準備するのはめどい。
というわけで、内部のどこかに格納されているジョブログの元からデータ引っ張り出して都合よく扱いやすくするスクリプトを書いてなんとかする。
環境
環境の都合上、Windows(PowerShell)を使う。
Windows10 1903
PowerShell 5.1
Fess 12.6.2
Elasticsearch 6.7.2
方針
- ジョブログが格納されているインデックスを特定する。
- マッピングを確認し目的のフィールドを探し出す。
- 目的のデータを読み出すJSONを書く。
- 読みだしたデータを扱いやすいよう加工する。
ジョブログが格納されているインデックス
(Invoke-WebRequest http://localhost:9200/_cat/indices?v"&"s=index).content
インデックス一覧を確認するいつものコード。PowerShellでこのように&
を含めたい場合"&"
と書かなければならないのはお約束。
一覧から目星をつけて.fess_config.job_log
に目的のログが入っていることを確認した。
目的のフィールドを探し出す
(Invoke-WebRequest http://localhost:9200/.fess_config.job_log/_mapping?pretty).content
マッピングを確認するいつものやつ。結果は省略。
properties以下にあるstartTime
、endTime
が目的のフィールドだ。
目的のデータを読み出すJSON
{
"size":5,
"query":{
"match":{
"jobName":"Default Crawler"
}
},
"sort":[
{"startTime":{"order":"desc"}}
]
}
同インデックスにはいろんなジョブログが入っているので、そのうちDefault Crawler
ログだけを抽出する。
読み出すフィールドを限定するかどうかは悩みどころ。この程度のデータ量なら全部とってきて後でフィルタしたほうが便利なのだけど、全部とってきて後で~はアンチパターンのダークサイドへ近づく道な気もする…。
読みだしたデータを扱いやすいよう加工する
UnixTimeから日本時間への変換
問合せを投げてやれば結果一覧がオブジェクトの形で返ってくる。あとはよしなに…とそう簡単にはいかない。格納されているデータはUnixTime
なのだ。1599400800000なんて返されても人間にはわからないので、タイムゾーンも考慮して変換しなければならない。
変換にあたっては以下の記事にあるコードを参考にした。丁寧に説明されていてわかりやすい。
記事中では秒を想定しAddSeconds
メソッドを使用しているところ、UnixTimeはミリ秒なのでAddMilliSeconds
メソッドを使用した。
【PowerShell】Unix時間を人が読めるようにする
Function Get-DateFromUnixTime($FessUnixTime){
[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1970/1/1').AddMilliSeconds($FessUnixTime))
}
[timezone]::CurrentTimeZone はC#の書き方らしい。PowerShellで細かいことしようとするとたいていC#か.NETの話が出てくるな。
ちなみに上で例に出した UnixTime 1599400800000 は 2020年9月6日23:00:00 である。
時刻の差分を求める
ジョブの処理に何時間かかったか?も一緒に求めておきたいため、差分を取るコードを書く。
# $RespはElasticsearchへ問い合わせた結果が入っている
$Obj = $Resp.hits.hits._source |
select jobName,startTime,endTime
$Obj | ForEach-Object{
$_.startTime = Get-DateFromUnixTime($_.startTime)
$_.endTime = Get-DateFromUnixTime($_.endTime)
$TimeSpan = ($_.endTime - $_.startTime).ToString("hh'時間'mm'分'ss'秒'")
$_ | Add-Member -MemberType NoteProperty -Name "timeSpan" -Value $TimeSpan
}
$Resp.hits.hits._source
は問い合わせた際のお約束で、ここに結果一覧が入っている。だいたいこれをWhere-ObjectやSelect-ObjectでフィルタしForEach-Objectで回すことになる。
$TimeSpan
の所で時刻の差分を求めている。DateTime型である$_.endTime
と$_.startTime
の差を求めると、TimeSpan型が返るのだそうだ。
DateTimeとTimeSpanをWindows PowerShellであれこれ
DateTimeオブジェクト同士を減算するとTimeSpanオブジェクトが戻り値になります。
TimeSpan型そのままだとこれまた人間の目では読みづらいので、ToStringメソッドで時間表記に直している。
出来たPSスクリプト
Function Get-DateFromUnixTime($FessUnixTime){
[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1970/1/1').AddMilliSeconds($FessUnixTime))
}
$IndexName = ".fess_config.job_log"
$Body = @'
{
"size":5,
"query":{
"match":{
"jobName":"Default Crawler"
}
},
"sort":[
{"startTime":{"order":"desc"}}
]
}
'@
$Param = @{
ContentType = "application/json"
Method = "Post"
Uri = "http://localhost:9200/${IndexName}/_search/"
Body = $Body
}
$Resp = Invoke-RestMethod @Param
$Obj = $Resp.hits.hits._source |
Select-Object jobName,startTime,endTime
$Obj | ForEach-Object{
$_.startTime = Get-DateFromUnixTime($_.startTime)
$_.endTime = Get-DateFromUnixTime($_.endTime)
$TimeSpan = ($_.endTime - $_.startTime).ToString("hh'時間'mm'分'ss'秒'")
$_ | Add-Member -MemberType NoteProperty -Name "timeSpan" -Value $TimeSpan
}
$Obj | Format-Table
jobName startTime endTime timeSpan
------- --------- ------- --------
Default Crawler 2020/09/06 23:00:00 2020/09/07 5:48:58 06時間48分58秒
Default Crawler 2020/09/05 23:00:00 2020/09/06 5:32:11 06時間32分11秒
Default Crawler 2020/09/04 23:00:00 2020/09/05 5:34:28 06時間34分28秒
Default Crawler 2020/09/03 23:00:00 2020/09/04 5:46:35 06時間46分35秒
Default Crawler 2020/09/02 23:00:00 2020/09/03 5:40:22 06時間40分22秒
あとはファイル出力するなり、時刻の差分チェックして7時間以上かかってたら通知するなり、いろいろ。