神戸学院大学 経営学部 林坂ゼミ

Docker 入門トップページ

« 戻る 次へ »

Docker 入門

Elasticsearch で全文検索を実行する(macOS版)

ドキュメントの様々な検索

まず,シェル変数に API キーが読み込まれていることを確認します.

elastic-start-local % echo ${ES_LOCAL_API_KEY} ⏎
Q25EX3RKb0I3SzJUQjVKNm8xQXQ6OXcxT3phdlhVV1BIeEhVZXh1clJMZw==
elastic-start-local %

まだ読み込まれていなければ次のコマンドを実行してファイル .env からシェル変数に読み込みます.

elastic-start-local % source .env ⏎
elastic-start-local %

さらにデータが登録されていることを確認します.まだ登録されてなければここを参照してデータを登録してください.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" "http://localhost:9200/hyaku_index/_count?pretty" ⏎
{
  "count" : 100,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  }
}
elastic-start-local %

目次に戻る

「雪」で検索

最も簡単な例として "uta" のフィールドについての全文検索を行います.例えば「雪」という単語が含まれるドキュメントを検索したい場合は次のような JSON ファイルを準備します.

json/search_yuki.json
{
  "query": {
    "match": {
      "uta": "雪"
    }
  }
}

検索するにはやはり --json オプションで JSON ファイルのパスを指定します.

curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_yuki.json "http://localhost:9200/hyaku_index/_search?pretty"

実際にコマンドを実行すると次の結果が得られました.4件のドキュメントが検索され,検索スコア (_score) の高い順に表示されていることが分かります.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_yuki.json "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 3.1701806,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "ywdow5oB41J4chRlTHY1",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "4",
          "uta" : "田子の浦に うち出でて見れば 白妙の 富士の高嶺に はふりつつ",
          "kana" : "たごのうらに うちいでてみれば しろたへの ふじのたかねに ゆきはふりつつ",
          "kajin" : "山部赤人"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "5gdow5oB41J4chRlTnYZ",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "31",
          "uta" : "朝ぼらけ ありあけの月と 見るまでに 吉野の里に 降れる雪",
          "kana" : "あさぼらけ ありあけのつきと みるまでに よしののさとに ふれるしらゆき",
          "kajin" : "坂上是則"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Jwdow5oB41J4chRlUnd0",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "96",
          "uta" : "花さそふ 嵐の庭の ならで ふりゆくものは わが身なりけり",
          "kana" : "はなさそふ あらしのにはの ゆきならで ふりゆくものは わがみなりけり",
          "kajin" : "入道前太政大臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "1gdow5oB41J4chRlTXYB",
        "_score" : 2.7989807,
        "_source" : {
          "id" : "15",
          "uta" : "君がため 春の野に出でて 若菜つむ わが衣手に はふりつつ",
          "kana" : "きみがため はるののにいでて わかなつむ わがころもでに ゆきはふりつつ",
          "kajin" : "光孝天皇"
        }
      }
    ]
  }
}
elastic-start-local %

なお,JSON ファイルを準備することなく,コマンドで同じ検索を行うことも可能です.

curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "雪"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty"

実際にコマンドを実行します.上と同じ結果が得られるはずです.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "雪"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : 3.1701806,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "ywdow5oB41J4chRlTHY1",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "4",
          "uta" : "田子の浦に うち出でて見れば 白妙の 富士の高嶺に 雪はふりつつ",
          "kana" : "たごのうらに うちいでてみれば しろたへの ふじのたかねに ゆきはふりつつ",
          "kajin" : "山部赤人"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "5gdow5oB41J4chRlTnYZ",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "31",
          "uta" : "朝ぼらけ ありあけの月と 見るまでに 吉野の里に 降れる白雪",
          "kana" : "あさぼらけ ありあけのつきと みるまでに よしののさとに ふれるしらゆき",
          "kajin" : "坂上是則"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Jwdow5oB41J4chRlUnd0",
        "_score" : 3.1701806,
        "_source" : {
          "id" : "96",
          "uta" : "花さそふ 嵐の庭の 雪ならで ふりゆくものは わが身なりけり",
          "kana" : "はなさそふ あらしのにはの ゆきならで ふりゆくものは わがみなりけり",
          "kajin" : "入道前太政大臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "1gdow5oB41J4chRlTXYB",
        "_score" : 2.7989807,
        "_source" : {
          "id" : "15",
          "uta" : "君がため 春の野に出でて 若菜つむ わが衣手に 雪はふりつつ",
          "kana" : "きみがため はるののにいでて わかなつむ わがころもでに ゆきはふりつつ",
          "kajin" : "光孝天皇"
        }
      }
    ]
  }
}
elastic-start-local %

目次に戻る

「恋」で検索

次は「恋」という文字で全文検索を行います.

json/search_koi.json
{
  "query": {
    "match": {
      "uta": "恋"
    }
  }
}

検索ワードが異なるだけなので,コマンドは JSON ファイルのパスを書き換えるだけで良いでしょう.

curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_koi.json "http://localhost:9200/hyaku_index/_search?pretty"

コマンドを実際に実行します.5件のドキュメントが検索されました.やはりスコア順に整列されていることが分かります.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_koi.json "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 3.1028621,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "Cwdow5oB41J4chRlUHeO",
        "_score" : 3.1028621,
        "_source" : {
          "id" : "68",
          "uta" : "心にも あらでうき世に ながらへば しかるべき 夜半の月かな",
          "kana" : "こころにも あらでうきよに ながらへば こひしかるべき よはのつきかな",
          "kajin" : "三条院"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "9Qdow5oB41J4chRlT3Yc",
        "_score" : 2.9656954,
        "_source" : {
          "id" : "46",
          "uta" : "由良のとを 渡る舟人 かぢを絶え ゆくへも知らぬ の道かな",
          "kana" : "ゆらのとを わたるふなびと かぢをたえ ゆくへもしらぬ こひのみちかな",
          "kajin" : "曾禰好忠"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "CAdow5oB41J4chRlUHdc",
        "_score" : 2.9656954,
        "_source" : {
          "id" : "65",
          "uta" : "恨みわび ほさぬ袖だに あるものを にくちなむ 名こそ惜しけれ",
          "kana" : "うらみわび ほさぬそでだに あるものを こひにくちなむ なこそをしけれ",
          "kajin" : "相模"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Hwdow5oB41J4chRlUXfo",
        "_score" : 2.7247887,
        "_source" : {
          "id" : "88",
          "uta" : "難波江の 葦のかりねの ひとよゆゑ みをつくしてや ひわたるべき",
          "kana" : "なにはえの あしのかりねの ひとよゆゑ みをつくしてや こひわたるべき",
          "kajin" : "皇嘉門院別当"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "7wdow5oB41J4chRlTna0",
        "_score" : 2.6184392,
        "_source" : {
          "id" : "40",
          "uta" : "しのぶれど 色に出でにけり わがは 物や思ふと 人の問ふまで",
          "kana" : "しのぶれど いろにいでにけり わがこひは ものやおもふと ひとのとふまで",
          "kajin" : "平兼盛"
        }
      }
    ]
  }
}
elastic-start-local %

目次に戻る

「身世」で検索

次は「身世」というワードで検索を実行してどのような結果になるか考察してみましょう.「身世」という言葉は一般的な熟語ではないはずです.まずはこれまでと同じように JSON ファイルを準備します.

json/miyo.json
{
  "query": {
    "match": {
      "uta": "身世"
    }
  }
}

検索コマンドは次のとおりです.

curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_miyo.json "http://localhost:9200/hyaku_index/_search?pretty"

実際にコマンドを実行すると次のとおり10件のドキュメントが検索されました.やはりスコア順に整列されています.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_miyo.json "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10,
      "relation" : "eq"
    },
    "max_score" : 5.2931757,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "Kgdow5oB41J4chRlUnem",
        "_score" : 5.2931757,
        "_source" : {
          "id" : "99",
          "uta" : "人もをし 人も恨めし あぢきなく を思ふゆゑに 物思ふは",
          "kana" : "ひともをし ひともうらめし あぢきなく よをおもふゆゑに ものおもふみは",
          "kajin" : "後鳥羽院"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "8wdow5oB41J4chRlTnb5",
        "_score" : 3.2533321,
        "_source" : {
          "id" : "44",
          "uta" : "あふことの たえてしなくは なかなかに 人をもをも 恨みざらまし",
          "kana" : "あふことの たえてしなくば なかなかに ひとをもみをも うらみざらまし",
          "kajin" : "中納言朝忠"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "9Adow5oB41J4chRlT3YK",
        "_score" : 3.2533321,
        "_source" : {
          "id" : "45",
          "uta" : "あはれとも いふべき人は 思ほえで のいたづらに なりぬべきかな",
          "kana" : "あはれとも いふべきひとは おもほえで みのいたづらに なりぬべきかな",
          "kajin" : "謙徳公"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "7Qdow5oB41J4chRlTnaQ",
        "_score" : 2.9656954,
        "_source" : {
          "id" : "38",
          "uta" : "忘らるる をば思はず 誓ひてし 人のいのちの 惜しくもあるかな",
          "kana" : "わすらるる みをばおもはず ちかひてし ひとのいのちの をしくもあるかな",
          "kajin" : "右近"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Cwdow5oB41J4chRlUHeO",
        "_score" : 2.924759,
        "_source" : {
          "id" : "68",
          "uta" : "心にも あらでうきに ながらへば 恋しかるべき 夜半の月かな",
          "kana" : "こころにも あらでうきよに ながらへば こひしかるべき よはのつきかな",
          "kajin" : "三条院"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Gwdow5oB41J4chRlUXei",
        "_score" : 2.924759,
        "_source" : {
          "id" : "84",
          "uta" : "ながらへば またこのごろや しのばれむ 憂しと見しぞ 今は恋しき",
          "kana" : "ながらへば またこのごろや しのばれむ うしとみしよぞ いまはこひしき",
          "kajin" : "藤原清輔朝臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "KAdow5oB41J4chRlUneF",
        "_score" : 2.8401425,
        "_source" : {
          "id" : "97",
          "uta" : "こぬ人を まつほの浦の 夕なぎに 焼くやもしほの もこがれつつ",
          "kana" : "こぬひとを まつほのうらの ゆふなぎに やくやもしほの みもこがれつつ",
          "kajin" : "権中納言定家"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "0Adow5oB41J4chRlTHaW",
        "_score" : 2.7954657,
        "_source" : {
          "id" : "9",
          "uta" : "花の色は うつりにけりな いたづらに わが身世にふる ながめせしまに",
          "kana" : "はなのいろは うつりにけりな いたづらに わがみよにふる ながめせしまに",
          "kajin" : "小野小町"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "zwdow5oB41J4chRlTHaE",
        "_score" : 2.5683868,
        "_source" : {
          "id" : "8",
          "uta" : "わが庵は 都のたつみ しかぞすむ をうぢ山と 人はいふなり",
          "kana" : "わがいほは みやこのたつみ しかぞすむ よをうぢやまと ひとはいふなり",
          "kajin" : "喜撰法師"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Jgdow5oB41J4chRlUndj",
        "_score" : 2.4681418,
        "_source" : {
          "id" : "95",
          "uta" : "おほけなく うきの民に おほふかな わが立つ杣に すみぞめの袖",
          "kana" : "おほけなく うきよのたみに おほふかな わがたつそまに すみぞめのそで",
          "kajin" : "前大僧正慈円"
        }
      }
    ]
  }
}
elastic-start-local %

ここでは上の結果を詳細に観察しておきましょう.いま「身世」というワードで検索をしました.「身世」という文字列そのものが含まれている "uta" は「花の色は ... わが身世にふる ...」の1件ですが,そのスコアは「2.795」の8位です.一方で,検索スコアが最も高かったのは「人もをし ... を思ふゆゑに 物思ふは」です.これは「身世」そのものは含まれておらず,「身」と「世」の両方が含まれています.そのほかヒットした結果には「身」または「世」のどちらかが含まれているだけですが,その多くは「身世」が含まれる「花の色は ...」よりも高いスコアになっています.

Elasticsearch の「match」クエリでは,検索ワードについて kuromoji による形態素解析を行います.このとき「身世」という文字列は kuromoji の辞書にないため未知語として「身」と「世」に分割された上で全文検索が行われます.このため「身」と「世」の両方が含まれるドキュメントだけでなく,一方だけが含まれるドキュメントも検索されました.なお,検索のスコアは BM25 というアルゴリズムに基づいて決定されます.

次は kuromoji の辞書に存在する「夏山」で検索してみましょう.

目次に戻る

「夏」「夏山」「夏 山」で検索

次は「夏山」での検索がどのようになるかを検証します.具体的には「身世」と異なり「夏山」は kuromoji の辞書に存在していることから,分割されず狭くヒットするようになります.そのことを確かめてみましょう.

まず「夏」で検索してみます.ここでは簡略化のためコマンドの中に JSON データを入力しています(もちろん JSON ファイルを作成しても構いません).その結果,3件のドキュメントがヒットしました.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "夏"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 3,
      "relation" : "eq"
    },
    "max_score" : 3.5847409,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "KQdow5oB41J4chRlUneW",
        "_score" : 3.5847409,
        "_source" : {
          "id" : "98",
          "uta" : "風そよぐ ならの小川の 夕暮れは みそぎぞの しるしなりける",
          "kana" : "かぜそよぐ ならのをがはの ゆふぐれは みそぎぞなつの しるしなりける",
          "kajin" : "従二位家隆"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "yQdnw5oB41J4chRlJHZ0",
        "_score" : 3.0250864,
        "_source" : {
          "id" : "2",
          "uta" : "春すぎて 来にけらし 白妙の 衣ほすてふ 天の香具山",
          "kana" : "はるすぎて なつきにけらし しろたへの ころもほすてふ あまのかぐやま",
          "kajin" : "持統天皇"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "6wdow5oB41J4chRlTnZv",
        "_score" : 3.0250864,
        "_source" : {
          "id" : "36",
          "uta" : "の夜は まだ宵ながら 明けぬるを 雲のいづこに 月やどるらむ",
          "kana" : "なつのよは まだよひながら あけぬるを くものいづこに つきやどるらむ",
          "kajin" : "清原深養父"
        }
      }
    ]
  }
}
elastic-start-local %

次に「夏山」で検索しますが,一切ヒットしませんでした.これは「身世」の場合と異なり,分割されずに「夏山」が1つのトークンとして処理されていることを意味しています.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "夏山"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
elastic-start-local %

次は「夏 山」で検索します.空白文字が含まれていることから,検索は「夏」と「山」の OR 検索になり,15件がヒットしました.このとき,「夏」と「山」の両方が含まれているドキュメントのスコアが高くなっていることに注意してください.なお,15件がヒットしたものの10件だけが表示されています.表示件数の変更はここに戻ってください.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "夏 山"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 15,
      "relation" : "eq"
    },
    "max_score" : 4.8356543,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "yQdnw5oB41J4chRlJHZ0",
        "_score" : 4.8356543,
        "_source" : {
          "id" : "2",
          "uta" : "春すぎて 来にけらし 白妙の 衣ほすてふ 天の香具",
          "kana" : "はるすぎて なつきにけらし しろたへの ころもほすてふ あまのかぐやま",
          "kajin" : "持統天皇"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "KQdow5oB41J4chRlUneW",
        "_score" : 3.5847409,
        "_source" : {
          "id" : "98",
          "uta" : "風そよぐ ならの小川の 夕暮れは みそぎぞの しるしなりける",
          "kana" : "かぜそよぐ ならのをがはの ゆふぐれは みそぎぞなつの しるしなりける",
          "kajin" : "従二位家隆"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "6wdow5oB41J4chRlTnZv",
        "_score" : 3.0250864,
        "_source" : {
          "id" : "36",
          "uta" : "の夜は まだ宵ながら 明けぬるを 雲のいづこに 月やどるらむ",
          "kana" : "なつのよは まだよひながら あけぬるを くものいづこに つきやどるらむ",
          "kajin" : "清原深養父"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "4Adow5oB41J4chRlTXa0",
        "_score" : 2.1455314,
        "_source" : {
          "id" : "25",
          "uta" : "名にしおはば 逢坂の さねかづら 人に知られで くるよしもがな",
          "kana" : "なにしおはば あふさかやまの さねかづら ひとにしられで くるよしもがな",
          "kajin" : "三条右大臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Ggdow5oB41J4chRlUXeQ",
        "_score" : 2.1455314,
        "_source" : {
          "id" : "83",
          "uta" : "世の中よ 道こそなけれ 思ひ入る の奥にも 鹿ぞ鳴くなる",
          "kana" : "よのなかよ みちこそなけれ おもひいる やまのおくにも しかぞなくなる",
          "kajin" : "皇太后宮大夫俊成"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "zgdow5oB41J4chRlTHZy",
        "_score" : 2.050685,
        "_source" : {
          "id" : "7",
          "uta" : "天の原 ふりさけ見れば 春日なる 三笠のに 出でし月かも",
          "kana" : "あまのはら ふりさけみれば かすがなる みかさのやまに いでしつきかも",
          "kajin" : "安倍仲麿"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "EQdow5oB41J4chRlUHfx",
        "_score" : 2.050685,
        "_source" : {
          "id" : "74",
          "uta" : "憂かりける 人を初瀬の おろしよ はげしかれとは 祈らぬものを",
          "kana" : "うかりける ひとをはつせの やまおろしよ はげしかれとは いのらぬものを",
          "kajin" : "源俊頼朝臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "3wdow5oB41J4chRlTXaj",
        "_score" : 1.9638691,
        "_source" : {
          "id" : "24",
          "uta" : "このたびは ぬさもとりあへず 手向 紅葉のにしき 神のまにまに",
          "kana" : "このたびは ぬさもとりあへず たむけやま もみぢのにしき かみのまにまに",
          "kajin" : "菅家"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "Awdow5oB41J4chRlUHcJ",
        "_score" : 1.9638691,
        "_source" : {
          "id" : "60",
          "uta" : "大江 いく野の道の 遠ければ まだふみも見ず 天の橋立",
          "kana" : "おほえやま いくののみちの とほければ まだふみもみず あまのはしだて",
          "kajin" : "小式部内侍"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "JQdow5oB41J4chRlUndS",
        "_score" : 1.9638691,
        "_source" : {
          "id" : "94",
          "uta" : "み吉野の の秋風 さ夜ふけて ふるさと寒く 衣うつなり",
          "kana" : "みよしのの やまのあきかぜ さよふけて ふるさとさむく ころもうつなり",
          "kajin" : "参議雅経"
        }
      }
    ]
  }
}
elastic-start-local %

目次に戻る

「淡路島」「淡」「淡路」「路」で検索

「淡路島」も kuromoji の辞書に登録されているようで,形態素解析によって1つのトークンとして取り扱われるようです.そのことを確認して理解を深めていきましょう.まず,「淡路島」で検索すると1件のドキュメントが検索されます.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "淡路島"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 12,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 4.1080723,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "FQdow5oB41J4chRlUXc0",
        "_score" : 4.1080723,
        "_source" : {
          "id" : "78",
          "uta" : "淡路島 かよふ千鳥の 鳴く声に 幾夜ねざめぬ 須磨の関守",
          "kana" : "あはぢしま かよふちどりの なくこゑに いくよねざめぬ すまのせきもり",
          "kajin" : "源兼昌"
        }
      }
    ]
  }
}
elastic-start-local %

「淡」や「淡路」では一切ヒットしませんでした.これはやはり「淡路島」が1つのトークンとして取り扱われていることを意味しています.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "淡"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
elastic-start-local %
elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "淡路"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
elastic-start-local %

一方で「路」で検索した場合には,「夢の通ひ」や「雲のかよひ」がヒットしたが,やはり「淡路島」はヒットしませんでした.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json '{
  "query": {
    "match": {
      "uta": "路"
    }
  }
}
' "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 3.3278084,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "2Qdow5oB41J4chRlTXY8",
        "_score" : 3.3278084,
        "_source" : {
          "id" : "18",
          "uta" : "住の江の 岸による波 よるさへや 夢の通ひ路 人めよくらむ",
          "kana" : "すみのえの きしによるなみ よるさへや ゆめのかよひぢ ひとめよくらむ",
          "kajin" : "藤原敏行朝臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "0wdow5oB41J4chRlTHbN",
        "_score" : 3.2028017,
        "_source" : {
          "id" : "12",
          "uta" : "天つ風 雲のかよひ路 吹きとぢよ をとめの姿 しばしとどめむ",
          "kana" : "あまつかぜ くものかよひぢ ふきとぢよ をとめのすがた しばしとどめむ",
          "kajin" : "僧正遍昭"
        }
      }
    ]
  }
}
elastic-start-local %

目次に戻る

完全一致検索

次にフィールドの完全一致検索について確認します.ここでは "kajin" フィールドから検索を行うことにします.これまでどおり,JSON ファイルを次のとおり準備します.つまり "kajin" が「藤原興風(ふじわら の おきかぜ)」であるドキュメントを検索します.

json/search_fujiwara.json
{
  "query": {
    "match": {
      "kajin": "藤原興風"
    }
  }
}

上の JSON ファイルを指定して検索したところ,確かに「藤原興風」がトップでヒットしましたが,「藤原義孝」や「藤原敏行朝臣」など,「藤原」が含まれるドキュメントもヒットしてしまいました.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_fujiwara.json "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 11.041369,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "hAqN2ZoB6SMxTmSaUz-u",
        "_score" : 11.041369,
        "_source" : {
          "id" : "34",
          "uta" : "誰をかも 知る人にせむ 高砂の 松も昔の 友ならなくに",
          "kana" : "たれをかも しるひとにせむ たかさごの まつもむかしの ともならなくに",
          "kajin" : "藤原興風"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "lAqN2ZoB6SMxTmSaVD9T",
        "_score" : 3.303321,
        "_source" : {
          "id" : "50",
          "uta" : "君がため 惜しからざりし 命さへ 長くもがなと 思ひけるかな",
          "kana" : "きみがため をしからざりし いのちさへ ながくもがなと おもひけるかな",
          "kajin" : "藤原義孝"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "dAqN2ZoB6SMxTmSaUj_w",
        "_score" : 2.8363104,
        "_source" : {
          "id" : "18",
          "uta" : "住の江の 岸による波 よるさへや 夢の通ひ路 人めよくらむ",
          "kana" : "すみのえの きしによるなみ よるさへや ゆめのかよひぢ ひとめよくらむ",
          "kajin" : "藤原敏行朝臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "lQqN2ZoB6SMxTmSaVD9d",
        "_score" : 2.8363104,
        "_source" : {
          "id" : "51",
          "uta" : "かくとだに えやはいぶきの さしも草 さしも知らじな もゆる思ひを",
          "kana" : "かくとだに えやはいぶきの さしもぐさ さしもしらじな もゆるおもひを",
          "kajin" : "藤原実方朝臣"
        }
      },
      {
        "_index" : "hyaku_index",
        "_id" : "lgqN2ZoB6SMxTmSaVD9n",
        "_score" : 2.8363104,
        "_source" : {
          "id" : "52",
          "uta" : "明けぬれば 暮るるものとは 知りながら なほうらめしき 朝ぼらけかな",
          "kana" : "あけぬれば くるるものとは しりながら なほうらめしき あさぼらけかな",
          "kajin" : "藤原道信朝臣"
        }
      }
    ]
  }
}
elastic-start-local %

以前にマッピングの生成を行った手順がありました.そこでの定義に利用した mapping.json をもう一度確認しましょう.ここで,"kajin" フィールドは kuromoji の形態素解析によって「藤原」などがトークン化されているので,上のような検索結果になりました.しかしながら,マッピングでは keyword も設定されていることから最大128文字までの完全一致検索もできるようになっています(実は "uta" や "kana" も最大512文字までの完全一致検索が可能です).

json/mapping.json
{
  "properties": {
    "id": {
      "type": "long"
    },
    "uta": {
      "analyzer": "hyaku_kuromoji_analyzer",
      "type": "text",
      "fields": {
        "keyword": { "type": "keyword", "ignore_above": 512 }
      }
    },
    "kana": {
      "analyzer": "hyaku_kuromoji_analyzer",
      "type": "text",
      "fields": {
        "keyword": { "type": "keyword", "ignore_above": 512 }
      }
    },
    "kajin": {
      "type": "text",
      "analyzer": "hyaku_kuromoji_analyzer",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above":128,
          "normalizer": "lowercase_normalizer"
        }
      }
    }
  }
}

したがって,完全一致検索を行いたい場合は,JSON ファイルを次のように変更します.

search_fujiwara_keyword.json
{
  "query": {
    "term": {
      "kajin.keyword": "藤原興風"
    }
  }
}

これによって "kajin" が「藤原興風」と完全に一致するドキュメントだけを検索することができるようになりました.

elastic-start-local % curl -H "Authorization: ApiKey ${ES_LOCAL_API_KEY}" --json @json/search_fujiwara_keyword.json "http://localhost:9200/hyaku_index/_search?pretty" ⏎
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 4.2096553,
    "hits" : [
      {
        "_index" : "hyaku_index",
        "_id" : "hAqN2ZoB6SMxTmSaUz-u",
        "_score" : 4.2096553,
        "_source" : {
          "id" : "34",
          "uta" : "誰をかも 知る人にせむ 高砂の 松も昔の 友ならなくに",
          "kana" : "たれをかも しるひとにせむ たかさごの まつもむかしの ともならなくに",
          "kajin" : "藤原興風"
        }
      }
    ]
  }
}
elastic-start-local %

目次に戻る