はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

会計・経理、財務諸表の勉強メモ(原価を内容別に分類するという考え方)

最近学んだこと(原価を内容別に分類するという考え方がある)

なにやら今更なんだけど、会計や経理の話はどうもしっくりこないところがしばしばある。

例えば↓のような記事。 globis.jp

結局どっちやねんという話。

知識不足・勉強不足の自分の主観なので、上記のサイトの内容についての是非ではないのだが、自分が巡り合った書籍も含めて、有識者の人たちは大前提として当たり前のように理解している何かの事項が自分には欠けていると感じていた。

その欠けている全てではないのだけど、次のリンクでやっと分かった。(大げさ)

- 損益の貢献度を正しく見極める原価計算のポイント - 経営改善ナビ]

info.isi-grp.co.jp

  1. 形態別分類
  2. 機能別分類
  3. 製品との関連による分類
  4. 操業度による分類
  5. 管理可能費と管理不能

f:id:azotar:20181219005122p:plain

上記はもともと、だれかが最初に5種類あるといって定義したものではなく、経営の効率や注目したいところの流派によりおおよそ5流派が主流でそれに名前をつけて分類したというところだろう。私見だけど、業種や今分析したい観点で上記のどれを選ぶべきか、併用すべきかというところについて、筋道が正しく示されている解説・書籍が会計や経理の本質を初学者に分からせてくれるという意味で良書なんだと思う。 (謎の偉そうなシメ)

貸借対照表とバランスシート(BS)

貸借対照表というのは止めよう。誰かと話してても舌を噛んでしまう。現代の日本においては(大げさ)バランスシートの方が意味する内容を少ない音節で表した良いワードだと思う。

損益計算書は、貸借対照表とバランスシートほどではないが、PL(Profit and Loss)でこちらの方が大分良いように思う。

財務諸表3表

(多少違っていても、)もう次のように思うことにする。

  1. BS→資産の内訳:つまりドラクエ的には会社のHP
  2. PL→(今期を例にとった)儲ける力:つまりドラクエ的にはLv(あるいは攻撃力)
  3. CF→(今期を例にとった)活動を金の動きで可視化:つまりドラクエ的にはコマンド入力の履歴と与えたダメージ等(つまるところ戦い方なんだろうという理解)

f:id:azotar:20181219004734p:plain

矛盾を表すコトバ

この人、何言ってやがるという時に感じる違和感の大半は、その人の発言とその人の行動のある種の矛盾からくるものが多いような気がする。

他人のとるにたらないミスを責める人は、むしろ大きな、周りにも迷惑のかかるミスをする。

別に相手の矛盾をついて、言い争う必要はない。

モヤモヤを感じた理由を言葉にできれば、それだけですっきりすることもある。

それでも相手の矛盾を指摘したい場合がないわけではない。

こういう相手は自分の矛盾に気づかない・気づきにくい状況や性質だというのが、ことをややこしくしている。

よって、お婆ちゃんの知恵的なやり方としては、発生している矛盾の構造やからくりそのものを表すうまい単語をチョイスできるようにしておきたい。 (アルゴリズムや問題の構造について説明していてはなかなかスピードが出ない。パワーワードを選ぶ方が良い場合もある。)

そんな「矛盾」を表す、あるいは厳密には「矛盾」というのとも違うかもしれないが、つじつまが合わない様子を表す単語のうち、会話などで使い出の良いワードを以下に集めてみました。

冒頭では、切り口を「どうしようもないヒト」風にしましたが、シゴトやシステムあるいはなんらかのしかけにも当てはまると思います。 むしろシゴトで発生する問題を一言で言い現せれば分析地獄に陥らずに済むかもと思ったり。

矛盾の種類、あるいは近い概念を表すことば

  • あべこべ
  • てれこ
  • 入れ違い
  • 食い違い
  • つじつまが合わない
  • 右手でXXして左手でYY(XXとYYは反対の行動)
  • 出たり入ったりを繰り返し
  • 反りが合わない
  • 周波数が合っていない
  • 歯車が噛み合っていない
  • 立つ瀬がない
  • がんじがらめ
  • 対岸の火事
  • 他人事
  • 傍観者
  • 船頭が多い(船頭のみ多い)
  • 頭でっかち
  • 天に唾を吐く
  • ブーメラン
  • エンジンがない車
  • アクセルとブレーキを同時に踏むような
  • 指揮官不在、鉄砲玉ばかり
  • 手段の目的化
  • 火に油を注ぐ
  • 水漏れ
  • 綱引き
  • 逆効果
  • 玉転がし
  • 石橋をたたいて壊す
  • 心配して吊り橋を揺らす
  • 灯台下暗し
  • 火と水
  • 天と地

Analyze設定のマイフェイバリット(Elasticsearch)

◯◯たるもの、嗜みの一つとして、Elasticsearchの日本語関連のAnalyze設定のフェイバリットのひとつ、ふたつはお持ちかと思います。

検索要件次第のところもありますが、そこがはっきりしない場合など、自分の脳内基本設定の軸があることで、それとの比較でトレードオフがあぶり出されることになると思いますので、スポンサーから特に指定がない場合は、この設定でまずはやってみようというものがあった方がなにかと効率的です。

2018年12月現在の自分の好みの初期設定的なものを自分の頭の整理ということで書き出してみます&背景を述べてみます。 (なんらかの知見っぽいものを炙り出せれば、あるいは勘違いなどあればそれはそれで誰かの役に立つと思って書いていますが、思ったより膨らまないかもしれません。その場合はごめんなさい。)

動作確認したバージョンは、Elasticsearch 6.4です。

この記事のスタンスとしては、性能はそこまで論じないことにします。もちろん性能が重要ではないという意味ではなくむしろ最も大事ですが、ここでは頭の整理を重視します。

初期設定のスタート版

先に推奨設定の例を貼り付けておきます。

PUT hogeindex
{
  "mappings": {
    "_doc": {
      "dynamic_templates": [
        {
          "my_dynamic_ja_analyzer_conf": {
            "match_mapping_type": "*",
            "match": "*_ja",
            "mapping": {
              "analyzer": "my_ja_analyzer_conf"
            }
          }
        }
      ]
    }
  },
  "settings": {
    "analysis": {
      "analyzer": {
        "my_ja_analyzer_conf": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer",
          "mode": "search",
          "char_filter": [
            "icu_normalizer",
            "kuromoji_iteration_mark"
          ],
          "filter": [
            "kuromoji_baseform",
            "kuromoji_part_of_speech",
            "ja_stop",
            "lowercase",
            "kuromoji_number",
            "kuromoji_stemmer"
          ]
        }
      }
    }
  }
}

ここで、

POST /my_ja_map/_analyze
{
  "analyzer" : "my_ja_analyzer_conf",
  "text" : 
"関西国際空港発の羽田空港行きの便に乗ります。
JANaの10時33分の便です。
ターミナルの前のタクシーをつかまえてください。"
}

でアナライズを通すと、

"関西", "関西国際空港", "国際", "空港", "発",  "羽田空港",  "行き",    "便",  "乗る",
"jana",  "10", "時",   "33", "分の",  "便",       
"ターミナル",   "前",  "タクシ",  "つかまえる",  ”くださる"

というトークンが帰ってきます。

単語を中心に助詞などが取り除かれました。また英数字が半角に統一されています。「タクシ」というのもあります。

以下、おもな勘所についてそれぞれ気まぐれに述べてみます。

トークナイザーについて

初期設定の最終系(少々意味不明な言い方ですが...)では、富豪プログラミング的発想で、形態素解析N-gramのハイブリッド(前者をブースト、スコアリング高め)にします。

が、ここでは同時に説明するのは(私のキャパからすると)難しいので、形態素解析に絞ります。

以下、しばらく、tokenizerに形態素解析のkuromojiを使う前提で説明しています。

modeについて

形態素解析のライブラリでは、素直に単語に分割するのではとイメージされますが、基本はそのようであるものの、実用の話で言うと、特に固有名詞などで発生しやすい未知語の扱いについて少々工夫ができるようです。

例えば、関西国際空港であれば、「空港」と「関西国際空港」を同じ文書位置にインデックスしておきたいということもあると思います。

このような設定の加減は「mode」プロパティで可能です。

設定としては、normal、search、extendedの3種ができますが、上記の複数配備の例に近く、ある程度当たりやすさの幅が確保できる(ような気がする)searchが好みです。

現在のデフォルトのmodeはsearchのようですが、これは明示的に指定しておくのが良いと思います。

なお、「大田区」は、【大田,区,大田区】になることを期待しましたが、手元の環境だと【大田,区】になりました。

品詞指定によるインデックス不要な語の除外(part_of_speech ...)

特に検索エンジンにあかるくなくともなんとなく分かる(?)話としては、

「にあたり」「として」「けれど」「ところが」といったつなぎの単語は検索インデックスに入れなくても良いのでは、ヒットしなくても良いのではという気になりませんか。

品詞で言うと、「助詞」は最たるもののように思われます。

「助詞」だけでなく、やろうと思えば「名詞」さえも除去できますが、

"filter": [
  "kuromoji_baseform",
  "kuromoji_part_of_speech",
  "ja_stop",
  "kuromoji_number",
  "kuromoji_stemmer"
]

のようにkuromoji_part_of_speechをfilterに指定するだけのデフォルト除去対象の設定で良いと思います。 (やろうと思えば、公式のコレ............のようにひとつずつ設定できます。)

さて、デフォルト除去対象がどれなのかというと、↓でコメントアウトが外れているもの(行頭が#でないもの)が除去対象になるようです。

https://github.com/apache/lucene-solr/blob/master/lucene/analysis/kuromoji/src/resources/org/apache/lucene/analysis/ja/stoptags.txt

前項のmodeとは裏腹ですが、下手に指定して、除去したいパターンを漏らしてしまうのは避けたいので、こちらについてはデフォルト=ベストプラクティスと考えることにします。 (実際ベストプラクティスだと思いますし、実のところここの設定を1つずつやるのはめんどくさい... 本音)

なお、デフォルトをうまく使おうというところはそれはそれとして、例えばお店の検索サイトや多少変わった名前が付くことのある商品サイトの場合は、kuromoji_part_of_speechは設定しない方が安全かもしれません。

全角と半角(icu_normalizer)

続いて、

  • アルファベット
  • 数字
  • 半角カナ

の正規化です。

これは私の中では必須です。データソースが何とかは基本関係ありません。絶対設定しておきましょう。「絶対」は言い過ぎだとしても、少なくともスタートはこのchar_filterをかましておく設定から入っておいて、なんかあったらチューニングするのがリスクが少ないように思います。

記事を書くのに、うかつなことを言わないように、下記の方がまとめられている内容を参考にさせていただきました。

https://christina04.hatenablog.com/entry/2016/08/03/123000

踊り字の置き換え(kuromoji_iteration_mark)

これも私の中では特に設定しない理由がないのでオンにします。一件単純そうでも、仮に自前で実装するとかなりめんどくさいですよね。そうでもないかな。

アルファベットの大文字と小文字を統一(uppercase、lowercase)

日本語が中心のドキュメントでは、これも対応しておくのが無難だと思います。私はlowercase派です。

ストップワードの除去(ja_stop)

これも対象にしておきます。実のところpart_of_speechの時点でかなり削られているので、この設定がどれくらい効いているかは特に確かめたことがありません。

逆に、ja_stopで満足できない場合は、Stop Token Filterで個別指定すると良さそうです。

 https://www.elastic.co/guide/en/elasticsearch/reference/6.5/analysis-stop-tokenfilter.html

ja_stopに限りませんが、全体的に余計な単語は除去しておく方が、サジェストや特別なクエリ(significant term など)でうまい値が出やすくなると思います。

カタカナ文字の後ろの長音を正規化(除去)(kuromoji_stemmer)

「サーバー」→「サーバ」にします。ちなみに、デフォルトだと4文字以上のワードの場合が対象で、「コピー」は「コピ」にはなりません。確かに元が3文字ぐらいだと表記揺れ自体は減りそうです。

ここでふと気になりましたので、試してます。

サーバ- サーバ- サーバ﹣ サーバ− サーバ‐ サーバ⁃ サーバ‒ サーバ〜 サーバ〰

をアナライズしてみたところ、全て「サーバ」に統一されました。※一緒に設定してあるストップワード除去などのアナライザーの効果によるものもあると思います。

コピペ間違いなどしているかもしれませんが、上記は、この順に

  • HYPHEN-MINUS
  • FULLWIDTH HYPHEN-MINUS
  • SMALL HYPHEN-MINUS
  • MINUS SIGN
  • HYPHEN
  • HYPHEN BULLET
  • NON-BREAKING HYPHEN
  • 全角チルダ
  • 波ダッシュ

です。

漢数字をアラビア数字に正規化(kuromoji_number)

これも設定しない理由はありません。...というと言い過ぎですが、他のものより優先度は低いものの、検索対象にゆる系のドキュメントが多いことが明らかであれば、設定しておくのかなと思います。

逆に、元のドキュメントが正規化されていたりライティングルールが適切に運用されている、もしくはRDBが元ネタというような場合は、無理に設定しない方が良い場合もチラチラ。

というのも(と偉そうに言う話ではないですが)、これらの厳格な運用がされている・されなければならないデータについては、漢数字とアラビア数字をあえて別物と捉えて管理している可能性があるためです。

検索ユーザー層と照らし合わせてどちらか選ぶことになるのかなと思いました。

この他、ゆる系のドキュメントがデータソースだとしても、場合によっては、トレードオフが出そうなものを頭の片隅においておくと良いかもしれません。 正規化したことによるトレードオフのアルアルのものとしては「住所」、次に固有名詞でしょうか。

原形化(アカデミックな言い方はこれでよかったけ?)(kuromoji_baseform)

これも有効にしておきましょう。名詞が中心かつなんらかの理由でできるだけ製品デフォルトで扱いたいというような場合以外は、有効にしておく方が思わぬものに悩まされずにすむと思います。

Mapping Char Filterの使いっぷり

上記の初期設定のスタート版には入れていませんが、人名をなんらか扱うものの場合、

渡辺 渡邊 渡邉

問題を解決してやる必要があるかもしれません。

人名や固有名詞がそれなりに登場するドキュメントの検索の場合、同音異字体の扱いについてポリシー決めが必要になり、それに合わせて、異字の置き換えの必要があります。

Mapping Char Filter | Elasticsearch Reference [6.5] | Elastic

拗音と促音

調査不足かもしれませんが、拗音や促音(キャキュキョや、ラッパ、カッパ、スタッカートなど)については、正規化するものは無いようでした。

最近の検索利用者はPCやスマホでの検索になれているため、拗音や促音の入れ間違いは少ないような気がしますが、古いシステムだとデータソースの方の「カナ」項目について、「キヤキユキヨ」などが格納されているものも見るような見ないような気もしますので、そんな時には考えどころですが、検索結果が0件の時に、Term suggesterで「もしかして」表示する方が良いかもしれませんね。

漢字ひらがなを読み(カナ)に変換するか(kuromoji_readingform)

上記の初期設定のスタート版には入れていませんが、kuromoji_readingformというToken filterがあります。

これを適用すると、インデックスには問答無用で(おそらくkuromojiの辞書の)読み仮名に従いカタカナでインデックスされます。

ここまで頭の整理をした大半のものが「除去」や「同じ意味」の表記揺れのカバーでしたが、このフィルターを通すと意味は同じであるものの、インデックス格納されるデータはがらりと変わってしまいます。

ここで、少しいやらしいのが、kuromojiの辞書に従うということになります。通常は検索時にも同じアナライザーを通すでしょうから、辞書がどうこうというところはそれほど関係ないような気がします。 ただし、読み仮名で当てさせたい場合は、もともと読み仮名フィールドを持っていることが多いため(というのは少し言い過ぎなものの)、読み仮名フィールドを検索対象としてやるぐらいで良いのかなということで、私はkuromoji_readingformはあまり使わないで良いんじゃない派です。

と言いつつ、「読み仮名」ならではというか、名称どおり、読みを意識したインデックスを作るとどのようなケースにヒットさせることができるので嬉しいかというところに絞って考察してみて、次のようなものをあげてみました。

  • 素直に? フィールド派生するなどして、元のもののままのものとカナフィールドを設ける(やっぱり両方持っておこうという話)
  • 自前のオートコンプリートなどに利用するフィールドに適用する
  • 店舗名など、漢字名称とカナ(よみがな)が管理されている中、漢字名称の部分に対し、元のもののままと、kuromoji_readingformを適用したものを別にもうけて、これも平行して検索することで、読み間違いしている人の検索を救済できる(カモしれない)
  • Term suggesterのユースケースなど、一文字違いの単語を間違えて入力してしまった時に
  • 前項で示した、異体字のことをできるだけ考えなくてよい検索を提供したい(異体字として扱いたいものが多く、analyzerの設定で指定したくない、ファイル指定もしたくない/できない)
  • VOIのバックエンドの検索エンジンとして使う

3番目の例ですが、例えばこういうことです。

渡部という名前は、「ワタナベ」さんと「ワタベ」さんがそれなりにいらっしゃると思います。調べたわけではありませんが、どちらかといえば、前者の方が多数のように思います。

今ドキュメントには、「渡部」で実際の読みはややレアな方の「ワタベ」さんで、事実どおりにデータソースに登録されています。

インデックス格納時には、それぞれのフィールドのまま格納しつつ、漢字名称の「渡部」については、形態素解析の辞書を使ってkuromoji_readingformでカナを派生させて「ワタナベ」でインデックスに登録しておきます。

これにより、ユーザーが「ワタナベ」(実際カナのまま入力)さんで検索しても、一応、このドキュメントがヒットすることになります。

例のための例になってしまったところはありますが、多数派の読みに引っ張られてそちらで検索する人が多いという場合をイメージしました。

検索時とインデックス時のアナライザーを別条件にするケース

最後に本件の話に近いところで、検索時とインデックス時のアナライザーを別条件にするケースを整理。

  • オートコンプリートでedge_ngramを使うような場合(公式Rでのコメント)
  • おもにCharacter FilterかToken Filtersのケースの「除去」を行うタイプのものに当てはまると思われるが、  インデックス時には除去するFilterを控えめにしておき(インデックスに蓄えるワードを増やしておく方向で将来に備えておくが)、検索時には除去することで、直近の仕様を確保。ケースバイケースだけど、不要な品詞の除去、ストップワードの除去のパターンがあてはまりそう。長音の除去はあてはまらないかな。

もう少しだけユースケースがありそうですが、ひとまずはこれぐらいでしょうか。

まとめ

偉そうに語ってみましたが、実のところkuromoji pluginのものはほぼ全て使う。それぞれのパラメータ設定は特にしないという例に落ち着きました。

毎度のことですが、先人に感謝です。

まとめとして、もう一度、推奨設定での例を貼り付けると

f:id:azotar:20181218221751p:plain

です。

ひとまず以上ですが、よくみると途中でうんちくを語りかけておいて、特にそれ以上ふれずじまいのものがいくつかあります。

例えば、店舗名、人名、住所といったものです。自分のあたまの整理と勉強をかねてどこかで時間を見つけて考察の上、自説をたれてみたいと思います。

参考リンク

Elasticsearchを日本語で使う設定のまとめ - Qiita Elasticsearch Analyze APIでkuromoji形態素解析を試す - Qiita 自作カナ変換プラグインでElasticsearchの日本語検索をいい感じにする - Qiita Elasticsearch 日本語で全文検索 その2 – Hello! Elasticsearch. – Medium

Google Analyticsのサイト内検索(そもそものGoogle Analyticsの機能全体俯瞰メモ)

このブログでは検索サイトをテーマにしたりその延長線上でElasticsearchのオレオレ記事を書いていることが多いのですが、ここではのちのちElasticsearch関連で書きたい自説の前段として、Google Analyticsについてメモをまとめたのでそれを貼り付けてみます。

例によって、A4持ち込み可能なカンニングペーパー的なまとめ方です。

ざっくりまとめなので、誤り等あればご容赦ください。

Google Analyticsの管理画面で確認できること

f:id:azotar:20181216175949p:plain
Google Analyticsで確認できること・2018年12月ごろの俯瞰版

Google Analyticsの管理画面のメニュー等を機能と捉えて、それぞれどのようなことができるか(できそうか)を、A4に書き出ししました。

おおよそ対応する公式Rはこちらです。↓

support.google.com

Google Analyticsの管理メニュー

レポートそのものやディメンションの切り口変更などは通常メニューから成り行きで操作してできるものも多いですが、メニューの一番したのメニュー項目に、「管理」というものがあり、ここで、利用アカウントを追加したり、Google Analyticsの対象とするサイトを追加したりといった、基本設定や運用管理に該当することが可能です。

例えば、ある閾値をこえたら、メールを送信するといったこともこちらで設定します。

f:id:azotar:20181216181037p:plain

「管理」の体系には、アカウント、プロパティ、ビューがあります。

これらは、公式によると

support.google.com

なんですが、

私見では、ビューはまたがり設定ができるので、

f:id:azotar:20181216181156p:plain

のような感じかなと理解しています。

サイト内検索のアクセス統計値の掘り下げができる

ここからが私の中の本題なのですが、Google Analyticsではサイト内検索での利用検索キーワード等に関する統計も取得できます。

★★★ディメンション(もしくはこの場合レポートというのでしょうか)の中に、「サイト内検索」というものがあり、サイト内検索関連のアクセス統計値を掘り下げることができます。「ビュー」の設定の中に次の図のような設定項目があるので設定を行うと、レポートに反映されます。

いつの間に?(というほどもともと詳しくないですが)、というところですがこれは便利ですね。

どちらかといえばこのブログではサイト内検索と検索サイトでいうと後者なのですが、サイト内回遊の視点で見るとどちらも「q=検索ワード」のようなクエリパラメータの世界なので、この点では同じですので、ここにその設定方法と公式Rの該当箇所のリンクをメモっておきます。

f:id:azotar:20181216181334p:plain

公式Rでの設定方法の解説はこちらです。

support.google.com

Elasticsearchなどでは検索動向はサイトのアクセスログやアプリの検索ログなどで分析することになると思います。

検索ログをElastic Stack(kibanaとか)に流し込み分析するのがうまい方法だと思います。

一方、サイト全体を俯瞰する視座も有効ですので、他のコンテンツとの繋がりや、Googleなどからどういう流入による検索行動なのかをあきらかにすることにも繋がるので、やりたいことと噛み合わせが悪くないのであれば、せっかくなので使いこなしたいところです。

検索サイトで(実際は検索サイトでなくてもだが)Google Analyticsを使う前にノルマ的に設定した方が良いことなどメモ

Google Analyticsを利用する前にはいろいろお作法があります。

たまたまこのページにGoogle検索でお越しいただいた方などは、Webサイト構築のおまけ*1のサイト内検索やGoogle Analyticsコンフィグという方もいらっしゃると思いますので、そのような方達(自分のこと?笑)向けに、事前に前捌きしておいた方が良いと私が思ったものを箇条書きにしてみました。

そもそも一番重要な、本来GAで何を分析するか、管理するかというところは各サイトで様々ということもあるのと、ボロがでないように、ここでは除きました。

f:id:azotar:20181216182034p:plain

おわりに

もう少し掘り下げることができたら、あらためての自説を述べさせていただくかもしれませんが、今回はここまで。

*1:言葉が悪いかもしれませんがここでは便宜上の話です。

トラブルシュート時などの現状ざっくり把握のためのAPI一覧まとめ (Elasticsearch)

検索アプリエンジニアの立ち位置視点で、Elasticsearchのモニタリング全般やテーブル構造(テーブルではありませんが...、たとえ話として)、インデックスに抱えているデータをマシンルームのような制限がある場所で(※そのような状況が良いかは別に置いておいて)、コンソール・ターミナルでカタカタ調べる場合に役立つElasticsearchのビルトインのエンドポイント(APIといった方が良いのかな?)をまとめました。

公式Rをある程度なぞった程度にすぎませんし、他のチートシートやテック記事に比べると深さ不足ですが、

  • しばしば担当を離れていた検索サイトで「検索の当たり方が悪い」というクレームが発生して、レスキューに入ることになった。 *(それが良いか悪いかはともかく)Elasticsearchにしばらくふれておらず、調査の勘所を思い出すにも、急遽駆けつけてくれと無理筋オーダーで現地に向かうことになった。
  • 隔離されたマシンルームは何かと不便(それが良いか悪いか、セキュリティ等のルールだから当然なのか、ルールごっこにすぎず不条理かは問わず事実として。)
  • アシスタントはおらず、自身が担当を離れる際には整備されていたドキュメントやなんらかのノウハウも破棄されている。

というシーンをイメージして、ストーリー立ててみています。

よって、サーバが停止しているといった類のトラブルシュートをイメージしたものではないので、インフラ系の確認はあえて(ぼろが出るので)少なめにしているというのがある種ウリ(?)のまとめです。

↓下記図のようなところを手早く把握しましょうという意図でまとめました。*1

f:id:azotar:20181215071107p:plain
検索サイトの内部機能やデータの把握観点

確認したElasticsearchのバージョンは6.4です。

ターミナルでカタカタと言いましたが、無駄にパラメータが長くなるので、HTTPメソッドとパスのみ(例えるならkibanaのDev Toolsでのクエリ発行の形式)の表記にしています。

プラグインのインストール状況の確認

インストールされているプラグインによってはデフォルト動作等が変わってしまうところもあるので確認しておきます。

GET /_cat/plugins?v

インデックスの一覧

次のメソッドでインデックスの一覧が確認できます。

ヘッダー的なものが付いていないのでわかりにくいですが、ドキュメント件数やデータ容量的なものも実は確認できます。

GET /_cat/indices?v

特定のインデックス狙い撃ちで確認もできます。↓ 

GET /_cat/indices/your_index_med

ここでは、「your_index_med」が今注目しているインデックスとしました。

インデックスまわりの設定全般

GET your_index_med?flat_settings

↑で軽く俯瞰しておいて。

↓で詳細(デフォルト等)を確認します。

GET /your_index_med/_settings?flat_settings=true&include_defaults

インデックスのマッピング設定

GET /your_index_med/_mapping?filter_path

インデックスのマッピング設定(フィールド別)

GET /your_index_med/_mapping/field/*_ja?include_defaults=true

「field」の次のところがフィールド名です。ワイルドカードやカンマ区切りで複数指定が可能です。

include_defaults=true を付けるとデフォルト設定も得られます。

エイリアス

GET /_cat/aliases?v

settings、mappings、aliasesの3点セットでの一括確認

GET /your_index_med

最初からこれでいいじゃん(笑)。

というところですが、一括だと、結構な量になるのと、久々だと頭に入ってこないので...。

この項目検索できるよねの確認(capability)

GET /your_index_med/_field_caps?fields=*_ja

あるインデックスのフィールド項目の一覧

Elasticsearchでは歴史的経緯もあって、タイプと呼ぶのかスキーマと呼ぶのか、そもそも最近のバージョンではインデックスのフィールドと呼んだ方が良いのか悩ましいですが、とにかくそれの把握です。

というところなんですが、上の方で出てくるmappingですでに取得はできていますし、普通はそこで確認済みでしょう。

一方、mapping全部は場合によっては少々読みづらいので、jqコマンドにパイプしたりしてということも考えられますが、製品標準だと、filter_pathを指定すると、気持ち応答内容の階層や項目が絞られてフォーカスしやすくなります。RDBMSでのDESCコマンドに近くなりますね。

例としては、

フィールドの一覧とそのデータタイプ
GET your_index_med/_mapping?filter_path=**.*.*.type

フィールドの一覧と適用されるアナライザー
GET your_index_med/_mapping?filter_path=**.*.*.analyzer

「*」はワイルドカードですが、実のところfilter_pathの指定の方法は私はよく分かっていません。雰囲気で使っています(笑)。

ちゃんと確認したい方は、

↓ [参考] filter_pathの設定方法についてはこちらです。

Common options | Elasticsearch Reference [6.5] | Elastic

POST /your_index_med/_search
{
  "size":1,
  "query": {"match_all": {}},
  "sort": [
    {
      "_id": {
        "order": "desc"
      }
    }
  ],
  "_source": ["_id"]
}
GET /your_index_med/_doc/取得したID?_source

インデックスのデータ

件数

GET /_cat/count/your_index_med?v

または

GET /your_index_med/_count

統計情報

GET /your_index_med/_stats

実は、件数などここまで見た情報はひととおり確認できそうです。

加えて、検索回数やキャッシュ情報なども見れそうです。

確認できる情報ブロックの最上位プロパティの一覧を引用しておきます。

  • docs
  • store
  • indexing
  • get
  • search
  • merges
  • refresh
  • flush
  • warmer
  • query_cache
  • fielddata
  • completion
  • segments
  • translog
  • request_cache
  • recovery

データ量

GET /_cat/fielddata?v

fielddata設定がされているfieldとその使用量(?)がわかります。

本来は、インフラ・パフォーマンスチューニングの視点で確認するかもしれませんが、ここではfielddata設定されている項目の一覧を俯瞰で確認できるというアプリ寄りの視点で確認することにしました。

その他のコンフィグ

エイリアス: 再掲

GET /_cat/aliases?v

テンプレート

GET /_cat/templates?v

インデックステンプレートの一覧です。

インデックステンプレートは、インデックス名とのパターンマッチによってあるデータが登録される際にどのインデックステンプレートが適用されるかが決まります。

地味ながら、この問い合わせでは、実際に設定されているインデックステンプレートの一覧とともに、そのパターンマッチの正規表現の一覧が確認できるので、想定外のテンプレートがマッチしてしまったのではという切り分けの最初のステップとしては悪くないビュー確認になると思います。

ちなみに、index_patternsというプロパティ名で表されます。

個々の設定は、

GET /_template/mytemplate_1

のように、templateエンドポイントに、インデックステンプレートの名前を引き渡してやると確認できます。

パイプラインの一覧

パイプラインはそこまで関係ないですが、存在は確認しておきましょう。

GET /_ingest/pipeline

アナライザー(Japanese(kuromoji) Analysis Plugin)のインストール&設定状況

このあたりで、ついに(やっと)、アナライザーを確認してみようとうことにしますが、ひとまずkuromoji-pluginに観点を絞ります。

GET your_index_med?flat_settings

返って来た値のひとまず次を確認しておきましょう。 (他にも重要なポイントがあるでしょうし、私見ではkuromojiについては全部の設定をONにした方が良いのではと考える派ですが、下記の点は「当たらない」系トラブルに特にきいてくるようなものをあげたつもりです。

  1. kuromoji_tokenizer の周辺にある、modeプロパティの値が、normal, search, extendedのどれか。(個人的にはsearchが嬉しい)
  2. 対象の検索サイトが動詞や形容詞、副詞でもそれなりに検索させるサイトの場合、あるいは今回のトラブルシュートとしてあげられている例がこれらの品詞を含む検索ワードの例の場合、"kuromoji_baseform"が、filter プロパティの配列に入っているか。また、"kuromoji_stemmer"がfilterプロパティに設定されているか。
  3. "romaji_readingform","katakana_readingform"が、filter プロパティの配列に入っているか。前項に比べると単純に確認の意味。

おわりに

冒頭に書いた、「検索のヒット」のトラブルシューティングのくせに、検索を試してみるところまで行っていませんが、長くなったので一旦これまで。

補足

メイン稿の中では触れませんでしたが、catエンドポイントについては、おまじないのように「?v」というクエリパラメータをつけています。

これは verboseのvで、これを付けるとヘッダー行が表示されます。

また、

クエリパラメータとして、

help 

を付けると、ヘッダー行に示されている各項の説明が確認できます。

*1:例によって正確な図というか処理方式やアルゴリズムを示した図ではありません。

Text datatypeとKeyword datatypeの公式Rでの設定項目を一覧比較して理解を深める(Elasticsearch)

はじめに

Elasticsearchのデータタイプのうち文字列系のtextとkeywordについて、それぞれを選択した場合の詳細Mapping設定での設定可能プロパティを表にして、これらの違いから、textとkeywordのデータタイプ自体の理解を深めることにしました。

文字列系データタイプのtextとkeyword について 

この記事を書いている今日現在のElasticsearchの最新バージョンは6.5です。

Elasticsearchは伸び代のあるソフトウェアということもあり、バージョンごとに仕様が変わることもしばしばのようです。

文字列系の項目のデータタイプとしては、ver 6.xでは text、keywordの2種類があります。過去にはstringというものだったようです。*1

雑に言うと、textは言語解析ありの全文検索用、keywordはtermでの検索(なんと言えば良いのか、コード値で厳格にヒットさせるような検索)用です。

特に根拠もなく、中の人にきいたわけでもないですが、これからはtextとkeywordの2種類でいくのではないでしょうか。

なお、Mapping設定で特に指定しなくともver 6.xの現在では、同じフィールドに関して、textタイプとkeywordタイプの両方のデータがインデックスに登録されるようなので、ざっくり使いたい時はあまり気にしなくても良いかもしれません。

一方、混みいった設定をする場合やそもそも設計を明確にするため、様々な理由で明示的にtextかkeywordか設定する場合には、さらなるオプション設定を行うことになります。

例えば、

// 中略

    "type":"text",
     "analyzer":"kuromoji",
     "fielddata":true,

// 中略

のような塩梅です。

そしてこの追加のオプション設定のプロパティですが、設定シンタックス等は末尾にURL引用の公式Rのページをご覧いただくこととして、オプションの数がなかなかの数あって、結構悩ましいです。

デフォルト(何も個別に指定しない)でも良いケースが大半だと思いますが、そんなことは忘れて、雑にいろいろやっていると突然あるクエリーの発行時に、textタイプでこのクエリーを使うなら、XXXオプションを設定してくださいとエラーを食らうことになります。

ということで、もやもやを解消するために、両者の設定可能プロパティをマルバツにしてみました。

textとkeywordデータタイプの設定可能プロパティ

f:id:azotar:20181213213504p:plain
textとkeywordの設定オプション

並べてみると、数に圧倒されそうになる一方で、文字列の検索ならtextとkeyword両方に対応しているもの、textだけのもの、keywordだけのものが色分けされて、個人的にはスッキリしました。

各プロパティの意味をある程度理解する必要はありますが、こうやってみると、片方だけ対応しているものは、そのデータタイプに見合ったオプションになっているようですね。 当たり前といえば当たり前ですが、逆に言うと、このオプションが必要になる・意味があるのがtext、はたまたkeywordであるという理解の仕方もあるのではと思いました*2

textとkeywordデータタイプ の公式Rへのリンク

www.elastic.co

www.elastic.co

 

textとkeywordデータタイプの設定可能プロパティ(コピペ用のテキストでの再掲)

項番 設定プロパティ 設定事項 text keyword
1 index 検索対象とするかの設定
2 ignore_above この値を超える桁数のデータはインデックスに格納しない
3 store trueに設定した場合、_source領域に保持しない(が、stored_fieldに個別に保持する...のでそこから取り出せる)
4 fields 一つのフィールドに複数設定を適用(例. 英語とフランス語のそれぞれのアナライザーを指定した内部フィールドを2つ用意。←このような用途の際に派生フィールドを作る必要がない)
5 similarity 類似度の計算アルゴリズムを指定(デフォルトはBM25)
6 boost ブースト
7 norms スコアリングでフィールドの長さを考慮
8 analyzer アナライザー
9 search_analyzer 検索時にインデックスと異なるアナライザーを使う場合に設定
10 search_quote_analyzer 要確認(ひとまず、search_analyzerの設定と同じものがデフォルトらしい)
11 normalizer ノーマライズするか
12 index_options ポジション情報などをインデックスに格納して検索やハイライトに対応しやすくする
13 eager_global_ordinals terms aggsの利用頻度が高いならtrueに設定すると良い
14 index_prefixes 最初の数桁を個別領域に格納して、prefix searchの効率向上
15 position_increment_gap 説明略
16 index_phrases 2コイチワーズを個別領域に格納してフレーズ検索を効率化
17 term_vector アナライズ対象フィールドのterm vectorsの値をインデックスに格納するか
18 fielddata sortやaggsのためにメモリに値を確保するか(text用)
19 fielddata_frequency_filter fielddata設定のさらなるチューニング
20 doc_values sortやaggsのためにディスクに値を確保するか(keywrod用)
21 null_value 値がない場合にNULL値を明示的に保持
22 split_queries_on_whitespace 検索語をスペースで分割して複数ワードとして検索するか(合ってるかな...?)

*1:なのでネットの記事では「string」がよくでてきます。

*2:ということでこの記事の正当性を主張する。

検索練習用のサンプルデータのインポート@Elasticsearch:住所マスタを使おう

はじめに

Elasticsearchの検索やヒットの仕方をいろいろためしてみたいという時に、そもそものデータをどう用意するかというところは地味ながら悩ましいです。

というのも、bulk APIはあるものの、Elasticsearchは良くも悪くもJSONの世界観ですし、試したり慣れたりしたいのに、そこにいくまでに無視できない量と手間のアナログな作業が発生します。

別にElasticsearchに限りませんが、波に乗るまでのハードルをどう超えるかは初学時のテーマです。

また、サンプルデータとして、ほど良いものをチョイスすることもぼちぼち大事です。

例えば、データがどんな感じで分布しているか分からない、想像つかないようなデータをインポートしても実際の当たり具合が想像つかなく、データ自体への習熟に時間を要してしまいます。

Elastcisearchの公式でサンプルデータを用意してくれていますが、これにケチをつける意味ではないものの、古くはscott/tigerの例と同様に、元のデータの分布や件数がにピンとこない場合、試行と結果の腹落ちに支障をきたします。

RDBならまだマシですが、全文検索の場合はスコアリングが絡むため、やはり元データについて予備知識がないものは避けたいところです。

前置きが長くなりましたが、テストデータとしてほど良いもの(と私が考える例として)、住所マスタ的なものに思い当たりましたので、そいつをサクッとインポートする手順を以下にまとめてみました。

住所マスタであれば、47都道府県ぐらいは(私個人は一部あやふやなところがあるものの)ソラでタイプできますし、最上位をaggsすれば47種類になることも想像つきます。

自分の身近な地名であれば、配下にどのような地域があるかもイメージしやすいですし、形態素解析分かち書きで、都合よく行く場合とそうでない場合などの感覚も掴みやすいと考えます。

仮想環境のイメージファイルもを配る余裕もスキルもないので、コマンド打ちコピペで対応ですが、その分手軽です。

特別なことはしていないですが、特別でないからこそ、ひとまずElasticsearchはポチポチインストールした、そのあと標準で入っているコマンドなどで対応できるようなものとしました。

考え方

  1. 住所マスタのCSVを入手します。
  2. ワンライナーの範囲で、バルクロード用のJSONに変換します。簡単のため 「a」というフィールドの1項目だけ(この項目中に元CSVの9項目がスペース区切りで入っている)のドュメントとします。
  3. バルクロードしますが、この際、ingest nodeでpipeline方式をかまします。pipelineのprocessorでは、空白区切りの文字列を9つのフィールドにバラしてドキュメント登録するような「grok」編集を使います。

Elasticsearchはインストール済みで、ここではanalyzerにkuromojiを登録できていることとします。

手順

事前準備(ファイルのダウンロード)

住所マスタといえば、日本郵政が郵便番号マスタとしてフリーで配布しているものがあるので、これを使います。

www.post.japanpost.jp

全国一括

でダウンロードできます。

1) zipを展開します。するとKEN_ALL.CSVというファイルが出力されます。
2) 続いて、バルクロード用のJSONファイルに変換します。
export META='{ "index" : {"pipeline": "pcode-pl" } }'; cat KEN_ALL.CSV | nkf  | ruby -ne 'puts $_.gsub(/[" ]/,"").split(",")[0..8].join(" ")' | ruby -nle 'puts ENV["META"] + "\n{\"a\":\"" + $_ + "\"}"'> ken_all.json

※手元の環境はmacなのでなんとなくインストールされているnkfを使っています。

3) Elasticsearch側のmappingとpipelineの設定をします。

ここでは、kibanaのDev Toolsに貼り付けるイメージで示しています。

コマンドラインからだとcurlなどのオプションに注意ください。

PUT _ingest/pipeline/pcode-pl
{
  "description" : "pcode",
  "processors": [
    {
      "grok": {
        "field": "a",
        "patterns": ["%{NOTSPACE:1_ps} %{NOTSPACE:2_ps} %{NOTSPACE:3_ps} %{NOTSPACE:4_ps} %{NOTSPACE:5_ps} %{NOTSPACE:6_ps}  %{NOTSPACE:7_ps} %{NOTSPACE:8_ps} %{NOTSPACE:9_ps}"]
      }
    }
  ]
}

PUT your_index3
{
  "mappings": {
    "your_type3": {
      "dynamic_templates": [
        {
          "_ps_as_kuromoji": {
            "match_mapping_type": "*",
            "match":   "*_ps",
            "mapping": {
              "type": "text",
              "analyzer":"kuromoji",
              "fielddata":true
            }
          }
        }
      ]
    }
  }
}
4) コマンドラインに移ってバルクロードします。
curl -H "Content-type: application/x-ndjson" -X POST localhost:9200/your_index3/your_type3/_bulk?refresh --data-binary  @ken_all.json

ここまででサンプルデータの取り込みは完了です。

5) kibanaのDev Toolsで検索してみます。
POST /your_index3/_search 
{
  "query": {"match_all": {
  }}
}

↓リザルト

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 124243,
    "max_score": 1,
    "hits": [
      {
        "_index": "your_index3",
        "_type": "your_type3",
        "_id": "jh43iWcBA4FDKIYMBOCw",
        "_score": 1,
        "_source": {
          "a": "01101 064 0640941 ホツカイドウ サツポロシチユウオウク アサヒガオカ 北海道 札幌市中央区 旭ケ丘",
          "1_ps": "01101",
          "2_ps": "064",
          "8_ps": "札幌市中央区",
          "7_ps": "北海道",
          "9_ps": "旭ケ丘",
          "5_ps": "サツポロシチユウオウク",
          "6_ps": "アサヒガオカ",
          "3_ps": "0640941",
          "4_ps": "ホツカイドウ"
        }
      },
      {
        "_index": "your_index3",
        "_type": "your_type3",
        "_id": "kR43iWcBA4FDKIYMBOCw",
        "_score": 1,
        "_source": {
          "a": "01101 064 0640820 ホツカイドウ サツポロシチユウオウク オオドオリニシ(20-28チヨウメ) 北海道 札幌市中央区 大通西(20〜28丁目)",
          "1_ps": "01101",
          "2_ps": "064",
          "8_ps": "札幌市中央区",
          "7_ps": "北海道",
          "9_ps": "大通西(20〜28丁目)",
          "5_ps": "サツポロシチユウオウク",
          "6_ps": "オオドオリニシ(20-28チヨウメ)",
          "3_ps": "0640820",
          "4_ps": "ホツカイドウ"
        }
      },
    

ヒットしてそうです。

おわりに

よくあるCSVの情報源であれば、上記をコピペしてしかもほとんど改変せずにデータを登録できると思います。

参考リンク

住所関連でElasticsearchということで、次の記事を発見しました。

たまたま本サイトに、住所関連の検索ノウハウをもとめて迷い込んだ方は、こちらのサイトさんの方がより適切と思われますので、リンクをご紹介させていただきます。

komaken.me

以上、おわりです。