from 30

30歳からwebエンジニアになったけど、思ったよりも苦しいので、その苦闘の記録をば

Elasticsearchの親子関係のまとめ

概要

現在作成しているシステムにおいて、DBはDynamoDBを採用している。
DynamoDBのLSI, GSIの取り回しだけでは検索に限界があるので、
DynamoDBStream経由でElasticsearchにインデックスして、
Elasticesearchに検索を任せている。

Elasticsearchのドキュメントにリレーションを持たせないことがあるが、
よく内容を忘れるので備忘のためにこちらに記載しておきます。

www.elastic.co

シナリオ

  • Company
  • Employee

の2つのエティティがある。 全部kibanaで試すのを想定

Comapnyが親、Employeeが子

mappingsの定義

PUT /index/
{
  "mappings": {
    "properties": {
      "company": {
        "properties": {
          "name": {
            "type": "keyword"
          },
          "years": {
            "type": "integer"
          }
        }
      },
      "employee": {
        "properties": {
          "name": {
            "type": "keyword"
          },
          "age": {
            "type": "integer"
          }
        }
      },
      "company_employee_relation": {
          "type": "join",
          "relations": {
              "company": "employee"
          }
      }
    }
  }
}

ポイント

  • company_employee_relationは適当な名称でOK
  • relationsの中は「 {親}:{子} 」にする。あとでdocを登録するときに使用する

データの登録

PUT /index/_bulk
{"index": { "_id": "Company:001"}}
{ "company": {"name": "株式会社ソックス", "years": 80}, "company_employee_relation": "company"}
{"index": { "_id": "Company:002"}}
{ "company": {"name": "キャップ工業株式会社", "years": 120}, "company_employee_relation": "company"}
{"index": { "_id": "Company:003"}}
{ "company": {"name": "株式会社フーディー", "years": 30}, "company_employee_relation": "company"}
{"index": { "_id": "Employee:001", "routing": "Company:001"}}
{ "employee": {"name": "武石徹", "age": 32}, "company_employee_relation": {"name": "employee", "parent": "Company:001"}}
{"index": { "_id": "Employee:002", "routing": "Company:001"}}
{ "employee": {"name": "宮市武", "age": 30}, "company_employee_relation": {"name": "employee", "parent": "Company:001"}}
{"index": { "_id": "Employee:003", "routing": "Company:003"}}
{ "employee": {"name": "相田宗介", "age": 31}, "company_employee_relation": {"name": "employee", "parent": "Company:003"}}
{"index": { "_id": "Employee:004", "routing": "Company:002"}}
{ "employee": {"name": "田中仁", "age": 31}, "company_employee_relation": {"name": "employee", "parent": "Company:002"}}

ポイント

  • 親を登録する時に以下を指定
    • relation名(ここでいうと"company_employee_relation")
    • 親の種類(ここでいうと"company")
  • 子を入れる時に以下を指定
    • relation名(ここでいうと"company_employee_relation")
    • nameは子の種類(ここでいうと"employee")
    • parentのindexを指定(ここでいうと"Company:00X")を指定する
    • indexのroutingで親のindexを指定(親と同一shardsに入れるため)

クエリの例

以下のような感じで、{クエリ}という親/子をもつindexを検索という感じのクエリが可能

GET /index/_search
{
  "query": {
    "has_parent": {
      "parent_type": "company",
      "query": {
        "wildcard": {
          "company.name": {
            "value": "株式会社*"
          }
        }
      }
    }
  }
}
GET /index/_search
{
  "query": {
    "has_child": {
      "type": "employee",
      "query": {
        "range": {
          "employee.age": {
            "gte": 31,
            "lte": 32
          }
        }
      },
      "inner_hits": {}
    }
  }
}

所感

顧客のレガシーシステムをマイクロサービスな構成にマイグレーションする案件を受けることが多い。
お客様の頭はRDBMSな感じになっていることも多くて、そうなってくると結構リレーションの多い検索要件があったりする。
DynamoDBファーストチョイスで考えていると対応できないことも多いけど、
Elasticsearchを使うと結構器用に扱えるのいいなと思いました。 (あんまり日本語の字句解析とかElasticsearchっぽいことはあまりできていないけど...)