🤖

🤖

:gijutsu_burogu:

Go Module: forkしたpackageをimportし利用する方法

やりたいこと

github.com/someone/hogeでgithub.com/forked/fugaをimportして利用したいです。 関係性は以下です。

どうやるか

例

https://github.com/elastic/cloud-sdk-goをforkしてhttps://github.com/kotaroooo0/cloud-sdk-goのリポジトリがあるとします。 第三のリポジトリhttps://github.com/kotaroooo0/poc-validate-onlyからhttps://github.com/kotaroooo0/cloud-sdk-goをimportして利用したいとします。

手順

1.https://github.com/kotaroooo0/cloud-sdk-goでGitのタグ打ちをします。

$ git tag v1.0.0

$ git push origin  v1.0.0

2.https://github.com/kotaroooo0/poc-validate-only/blob/main/go.mod#L7のようにreplaceを使います。

module github.com/kotaroooo0/poc-validate-only

go 1.21.7

require github.com/elastic/cloud-sdk-go v1.16.0

replace github.com/elastic/cloud-sdk-go v1.16.0 => github.com/kotaroooo0/cloud-sdk-go v1.0.0

これだけで第三のリポジトリhttps://github.com/kotaroooo0/poc-validate-onlyからhttps://github.com/kotaroooo0/cloud-sdk-goを利用できます。

備考

https://github.com/kotaroooo0/poc-validate-onlyではimportの部分は変更する必要なし

以下のように既存でimportしていたgithub.com/elastic/cloud-sdk-goは触らなくても自動でgithub.com/kotaroooo0/cloud-sdk-goをimportし参照してくれます。

https://github.com/kotaroooo0/poc-validate-only/blob/main/main.go#L3-L15

originalに参照を戻す方法

以下を消せばまたgithub.com/elastic/cloud-sdk-go を参照するように戻ります。

replace github.com/elastic/cloud-sdk-go v1.16.0 => github.com/kotaroooo0/cloud-sdk-go v1.0.0

github.com/kotaroooo0/cloud-sdk-goのgo.modについて

以下のようにgithub.com/kotaroooo0/cloud-sdk-goのgo.modはForkした時のまま変更する必要はありません。 module名も変更する必要はありません。

https://github.com/kotaroooo0/cloud-sdk-go/blob/update-deployment-validate-only-params/go.mod

参考

go.dev

SolrCloud on AWS EKSを利用した検索基盤の導入

blog.recruit.co.jp

検索システム開発での難しさ

本記事は情報検索・検索技術 Advent Calendar 2022の6日目の記事です。

はじめに

最近はプロダクトの検索システムを全文検索エンジンを利用して開発しています。 通常の機能開発と同じ難しさもあれば、検索機能独特の難しさもあると感じています。 本記事では、以下のよくありそうな場面を想定してみます。

  • 通常の機能開発を行うプロダクトごとの職能横断のチームX
  • 検索技術を専門とし検索APIのようなサブシステムを提供するチームY
  • マーケやディレクターのようなビジネスサイド

難しさ

難しさには、状況によりさまざまあると思います。 まず、プロダクトでの課題がありその原因への打ち手として検索機能開発があるというのが全体像だと思います。 その検索機能開発において、チームXとチームYが連携して開発していきます。

本記事では4つの難しさについて触れます。

  • 作るのが難しい

    • これはチームYに閉じた話で、文字通りそのままで検索システムは設計実装するのが難しいということです。
  • 検索ロジックの妥当性を示すのが難しい

    • これはビジネスサイドとの合意形成やチーム内の検索ロジックの方針を決定する際の問題です。作る/作った検索ロジックについて妥当性を示すのが難しく、新しく機能を追加する際にリターンを明確にできずリスクを取ることが難しいことがあります。
  • 打ち手から課題へアプローチするのが難しい

    • これはビジネスサイドとの開発前の合意形成の話で、課題より先に技術(How)からスタートしていくなど特殊なプロセスが必要になるということです。特に検索技術はビジネスサイドにとってはブラックボックスであることもあり、この必要性が大きいと思います。
  • 装着が難しい

    • これはチームXとチームYの連携の話であり、バッチで検索エンジンへデータ連携、API仕様、ロジックをどちらが持つのか、非機能要件はどうするのかなど論点が多いです。
難しさ なぜ難しいのか 具体例
作るのが難しい API開発に加えてデータ分析・検索エンジンに関するスキルが必要になることが多いため。また、データ分析による定量評価と定性評価→検索ロジック施策決め→実装を繰り返す必要があり、プロジェクト開始時に作る検索ロジックを明確に決定できない場合もあるため。 ElasticsearchやSolrのクエリやテンプレート、運用知識が必要になる。あるロジックを導入したら、Recallは上がったがPrecisonは下がった。
検索ロジックの妥当性を示すのが難しい 検索ロジックの評価が難しいため。オフライン定性評価・定量評価を行い、最終的にはオンラインABテストで評価することが多い。 正解データがないこともしばしばあり定量評価が難しい。その中でもチーム内外問わず合意形成していかなければならない。
打ち手から課題にアプローチするのが難しい 課題から打ち手を考えるだけでなく、打ち手から解決できる課題を提案し、その打ち手が本当に最適なのか確かめるプロセスが必要になるため。検索技術はビジネスサイドにはブラックボックスであることもありエンジニアからこのようなプロセスで提案することも必要になる。  シノニム・辞書登録を導入すると検索品質は向上するため課題とセットでビジネスサイドに提案し、課題解決手段としてシノニム・辞書登録が最適であることを説明する。
装着が難しい チームYのような検索機能を開発するチームは専門性の高いサブシステムの開発や提供を行うチームであることがあり、この場合にチームXのような一気通貫して価値提供しているチームとは、プロダクトに対する解像度が違うことがあるため。そのため、WANT要件をMUST要件であると勘違いしオーバーエンジニアリングしたりする。 チームYは思い込みで間違えた前提を置き過剰に検索品質(クオリティ)を高めるためにコスト・デリバリーを犠牲にすることがある。

対策

それぞれの難しさに対しての対策は、具体的な状況によりケースバイケースで本記事では深くは触れません。 「作るのが難しい」は個人のスキルアップでどうにかできる場合もあれば、開発プロセス・体制を変更することで難しさを減らすこともできると思います。 「検索ロジックの妥当性を示すのが難しい」は、プロトタイピングにより具体的に検索ロジックを体験してもらうことでステークホルダーからフィードバックを得られたり評価することができるかもしれません。 「打ち手から課題にアプローチするのが難しい」は、現状のプロダクト・システムやUXの理解とその課題を発見し、それを解決できる技術がないかというのと、この技術はなんの課題を解決するんだろうと両面から日頃から考えるのが大事かもしれません。 「装着が難しい」は、チーム間の責務を定義・変更したり、密にチーム間でコミュニケーションを取ることで解決できるかもしれません。

おわりに

抽象的なテーマについて記事を書いてみようと思いチャレンジしましたが、あまりうまく整理できませんでした...! みなさんが感じている検索機能開発での難しさがあれば、知りたいです。

メモ: ElasticsearchのForce Merge

前提

Elasticsearchインデックス

Elasticsearchのインデックスは以下の構造になってます。

Elasticsearchのインデックス構造

Merge とは

インデックスの構造での Segment は Immutable なので、Document が削除・更新された時には、Segment 内のドキュメントは物理削除されず論理削除されます。 なので、データサイズが増える一方で、削除・更新された Document を物理削除して Segment を統合するマージ処理を定期的に行います。

通常はルールベースでスケジューリングされます。

www.elastic.co

Force Merge とは

www.elastic.co

用途

・インデックスのサイズの縮小

・検索パフォーマンスの向上

・新しいデータのインデックス作成にかかる時間削減

参考

engineering.mercari.com

ElasticsearchのNamed queries

やりたいこと

「ナイキ スニーカー」という検索キーワードがあった場合に、ナイキ→ブランド、スニーカー→商品カテゴリとElasticsearchで分類したい。(前提としてブランド名一覧のワード集合と商品カテゴリ一覧のワード集合はあるとする) Elasticsearchで分類するメリットはアナライザにより表記揺れを吸収できる点で、「NIKE」でも「ナイキ」でもブランドとしての「ナイキ」と分類できる。 単純に考えると以下のようなElasticsearchに以下のようなクエリを投げるイメージ。

GET _search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "field.analyzed": {
              "query": "ナイキ"
            }
          }
        },
        {
          "match": {
            "field.analyzed": {
              "query": "すにーかー"
            }
          }
        }
      ]
    }
  }
}

課題

Elasticsearchから返却されるのはキーワードがアナライザを通ってマッチするドキュメントが返却される場合がある。 その場合に、キーワードと分類分けの対応づけが分からなくなる。 例えば、ワード「ナイキ」で検索してドキュメント「Nike」が返却されても、「ナイキ」がブランド「Nike」と分類されたと呼び出し側は判断することができない。 これは、「ナイキ」だけで検索した場合であれば分類可能であるが、should句で複数ワードで検索した場合は分からない。 ワード数の回数クエリを投げれば分類可能であるがこれはパフォーマンスや負荷の観点で非効率的である。

inputとoutput

解決法

この課題はNamed queriesで解決できる。

Named queriesとは

www.elastic.co

以下のようにshould句でOR検索をする際に、どのドキュメントがどのワードでマッチしたかもElasticsearchのレスポンスで返却される。

GET _search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "field.analyzed": {
              "query": "ナイキ",
              "_name": "ナイキ"
            }
          }
        },
        {
          "match": {
            "field.analyzed": {
              "query": "すにーかー",
              "_name": "すにーかー"
            }
          }
        }
      ]
    }
  }
}

Named queriesの他のユースケース

こちらの記事で他のNamed queriesの具体的ユースケースについて触れられていました。

opster.com

Use case 1 – query debugging

Use case 2 – specific query logic

Use case 3 – diversifying search results

Use case 4 – logging

検索改善へのヒントが得られるかも知れません、詳しくはリンク先へ!

Elasticsearchのタイムアウト設定

Elasticsearchのタイムアウト設定とは

Elasticsearch側で設定するタイムアウトで、下図②の時間に対するタイムアウト設定のこと。

ドキュメントには以下のように示されています。 タイムアウト時はエラーを返却するわけでなく時間内に蓄積された検索ヒットで結果を返却します。

A search timeout, bounding the search request to be executed within the specified time value and bail with the hits accumulated up to that point when expired. Search requests are canceled after the timeout is reached using the Search Cancellation mechanism. Defaults to no timeout. See Time units.

www.elastic.co

内部的には以下のAPIによりキャンセルが行われているようです。

www.elastic.co

クライアント側ではレスポンスタイムアウト、コネクションタイムアウトを設定することがあります。 図で①+②+③でタイムアウト判定するのがレスポンスタイムアウト、図では示せていないがコネクション接続でのタイムアウト判定するのがコネクションタイムアウト。

クライアント側でレスポンスタイムアウトを設定している時は、その値より小さい値をElasticsearchのタイムアウト設定をすると良いでしょう。 検索結果が欠損するのを許容できレスポンスタイムの99%パーセンタイルの改善の際などに有用です。

Elasticsearchのタイムアウト設定方法

# request
GET /twitter/_search
{
    "timeout": "1s", // "500ms", "3s"という形式
    "query" : {
        "term" : { "user" : "kimchy" }
    }
}

# response
{
    "took": 1,
    "timed_out": false, // タイムアウトが発生したかどうか
    "_shards":{
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
    },
    "hits":{
        "total" : {
            "value": 1,
            "relation": "eq"
        },
        "max_score": 1.3862944,
        "hits" : [
            {
                "_index" : "twitter",
                "_type" : "_doc",
                "_id" : "0",
                "_score": 1.3862944,
                "_source" : {
                    "user" : "kimchy",
                    "message": "trying out Elasticsearch",
                    "date" : "2009-11-15T14:12:12",
                    "likes" : 0
                }
            }
        ]
    }
}

WEB+DB Press Vol.126で「作って学ぶ検索エンジンのしくみ──Goで実装! 膨大な情報からどう高速に探すのか」という記事を寄稿しました

はじめに

タイトルにあるように、12月24日に販売されたWEB+DB Pressに記事を寄稿しました。

gihyo.jp

去年の10月ごろから学習のために全文検索エンジンを自作し始めたのですが、このような機会をいただけるとは全く想像していませんでした。 去年のアドベントカレンダーのブログ記事で全文検索エンジン自作に関するアウトプットを初めてしました。 その後、今年の4月にGo Conferenceで登壇し、今回はWEB+DB Pressに寄稿することになりました。 アウトプットがアウトプットにつながるという経験をしました。

kotaroooo0-dev.hatenablog.com

speakerdeck.com

記事について

Goで全文検索エンジンを自作する内容になっています。 記事の内容はGo Conferenceでのスライドが元になっています。 それに加えて記事では、このスライドよりも詳細に説明し、幅広い内容を取り扱っています。

初学者の方でも理解できるように全文検索エンジンの仕組みの説明を多めに書きました。 また、実装に関してもGoで書きました。 Goはシンプルな言語仕様で特殊な文法が少ないので、Goに親しみのない方でも処理の内容を理解しやすいと思います。

また、今回の自作検索エンジンでは、設計面・実装面ともに最適でない部分が多くあるかもしれません。 感想やフィードバックなどいただければ幸いです。

github.com

最後に執筆のチャンスをいただけて感謝しています。 技術評論社の方々、ありがとうございました。