大きめの OSS に Issue を出してみた(OpenSearch)
OpenSearch に出したバグ報告を中心に、出すまでの流れ・Issue の組み立て・出したあとに起きたこと・心構えを、図でまとめた記録。機能要望の 1 件にも触れる。
2026 年 10 月、検索エンジンの OpenSearch に Issue を 2 件出しました。1 件はバグ報告、1 件は機能要望です。OpenSearch は GitHub のスターが 1 万を超える大きめの OSS で、Issue の書き方にもテンプレートや仕分けの決まりがあります。この記事では主にバグ報告の 1 件について、何をどんな順番で調べ、どう書き、出したあと何が起きたかを、心構えと一緒にまとめます。
1なぜ出したか
仕事で検索まわりを触っていると、OpenSearch の挙動をコードまで追う場面がよくあります。追った結果「これは自分の使い方の問題ではなく、OpenSearch 側の問題だ」と言えるところまで来たら、Issue にして共有した方が、同じところで困る人が減ります。
とはいえ、知らない人たちが毎日たくさんの Issue をさばいている場所に書き込むのは、少し身構えます。なので「相手の時間をできるだけ使わせない Issue」を目標に、調べたことを全部まとめてから出しました。
2出した Issue
バグ報告は、OpenSearch 3.x の検索の最適化(range クエリの近似)が、本来かかってはいけない場所にもかかってしまい、条件に合うドキュメントがエラーもなく黙って結果から落ちる、というものです[1]。この記事では中身より「どう出したか」に絞るので、詳しくは Issue の本文を見てください。
3出すまでの流れ
気づいてから出すまでに、8 つのことをしました。順番は前後しますが、どれも「メンテナーが読んで、すぐ動ける」ためのものです。
- 1おかしいと気づく
post_filterにrangeを置くと、当てはまるはずの件数より少ない。エラーは出ず、件数は「ちょうど」と報告される。なぜエラーにならない間違いは気づきにくく、気づいた人が報告しないと残り続ける。
- 2再現を小さくする
単一ノード・1 シャード・curl だけのスクリプトに削った。データは
value = (i × 7919) % Nで、件数Nを変えるだけで規模を変えられる。なぜメンテナーが手元でそのまま動かせる形にする。前提が少ないほど、別の原因を疑われにくい。
- 3いつから壊れたか調べる
公式 Docker イメージで 2.19.5 / 3.0.0 / 3.1.0 / 3.9.0 を並べ、同じスクリプトの結果を表にした。2.19.5 は正しく、3.x で壊れている。
なぜ「いつから」が分かると、原因の変更をかなり絞り込める。
- 4同じ報告がないか探す
既存の Issue と PR を探して、見つからなかったことを本文に書いた。
なぜ重複はメンテナーの手間になる。探した、と書いておけば読む側も安心できる。
- 5原因をコードで追う
近似を有効にする処理が、本命のクエリ以外(
post_filterなど)にもかかっていた。該当箇所はバージョンのタグに固定したリンクで示した。なぜ
mainブランチへのリンクは、コードが変わると行がずれる。タグに固定すれば、後から読んでも同じ行を指す。 - 6直し方を考える
「本命のクエリのときだけ近似する」という 1 行の条件を diff で載せた。参照の同一性に頼る弱点と、別の直し方の案も添えた。
なぜ直し方は相手が決めるもの。案として出し、懸念も先に書いておくと議論が早い。
- 7手元で確かめる
3.9.0 をローカルでビルドし、新しいテストが修正前に落ちて修正後に通ること、既存のテストが通ることを確かめた。
なぜ「たぶん直る」ではなく「こう確かめた」と書けると、修正案の信頼度が上がる。
- 8テンプレートに沿って出す
バグ報告のテンプレート(症状・コンポーネント・再現・期待する動作・環境)に沿って書き、最後に「よければ PR を出す」と添えた。
なぜテンプレートの順はメンテナーが読みたい順。自分で直す気があることも伝わる。
中でも時間をかけたのが再現を小さくすることです。最初に気づいたときの状況には、関係のない設定やデータがたくさん混ざっています。それを 1 つずつ外し、curl だけで動く、1 ノード・1 シャードのスクリプトにしました。件数 N を変えるだけで規模を変えられるので、「ドキュメントが多いときだけ起きる」といった条件も確かめられます。
OS=http://localhost:9200
N=20000 # must not be a multiple of 7919
IDX=postfilter-range-repro
curl -s -XDELETE "$OS/$IDX"
curl -s -XPUT "$OS/$IDX" -H 'Content-Type: application/json' -d '{
"settings": { "number_of_shards": 1, "number_of_replicas": 0 },
"mappings": { "properties": { "value": { "type": "long" }, "grp": { "type": "keyword" } } }
}'
# value = (i * 7919) % N is a permutation of 0..N-1 that is unrelated to doc order.
# Every 10th doc has grp=a, so N/10 docs match the main query.
seq 0 $((N - 1)) | awk -v n="$N" '{
printf "{\"index\":{\"_id\":\"%d\"}}\n", $1
if ($1 % 10 == 0) printf "{\"value\":%d,\"grp\":\"a\"}\n", ($1 * 7919) % n
else printf "{\"value\":%d}\n", ($1 * 7919) % n
}' | curl -s -XPOST "$OS/$IDX/_bulk?refresh=true" -H 'Content-Type: application/x-ndjson' --data-binary @- > /dev/null
curl -s -XPOST "$OS/$IDX/_forcemerge?max_num_segments=1"
curl -s -XPOST "$OS/$IDX/_refresh"
# Every document satisfies value >= 0, so hits.total should be N/10
curl -s -XPOST "$OS/$IDX/_search?filter_path=hits.total" -H 'Content-Type: application/json' -d '{
"query": { "term": { "grp": "a" } },
"post_filter": { "range": { "value": { "gte": 0 } } }
}'もう 1 つはいつから壊れたかです。公式の Docker イメージならバージョンを指定してすぐ起動できるので、同じスクリプトを 2.19.5・3.0.0・3.1.0・3.9.0 で流し、結果を表にしました。2.19.5 は正しく、3.0.0 は大きなセグメントでだけ、3.1.0 以降は常に壊れている、という段差が見えたことで、原因になった変更をかなり絞り込めました。
4Issue の組み立て
OpenSearch には Issue のテンプレートがあり、バグ報告なら「症状・関係するコンポーネント・再現手順・期待する動作・環境」の順に書く欄が用意されています[5]。この順番は、メンテナーが知りたい順番そのものです。バグ報告では、そのあとにテンプレートにない「原因」と「修正案」を足しました。

- タイトル
[BUG] Range query approximation is applied to
post_filter, rescore_query, named queries and inner_hits, silently dropping matches役割何が・どこで・どうなるかを 1 行に入れる。一覧で見たメンテナーが、自分の担当か、どれくらい深刻かを開かずに判断できる。
- ### Describe the bugテンプレート
症状(エラーなしで一致が落ちる、件数が
eqのまま減る)、影響する 6 か所、影響するバージョン(3.0.0〜3.9.0 とmain、2.19.5 は影響なし)。役割最初の数行で「誰に関係あるか」が分かるようにする。影響範囲が広いことも、ここで伝わる。
- ### Related componentテンプレート
Search:Performance
役割テンプレートの選択肢から選ぶ。ラベルになり、その領域の担当者に届く。
- ### To Reproduceテンプレート
curl だけで動く再現スクリプトと、4 つのバージョンでの結果の表。ほかの影響箇所の再現は折りたたみに入れた。
役割コピーして貼れば再現できる、が一番強い。表があると「いつから」が一目で分かる。長い補足は折りたたんで、本筋を読みやすく保つ。
- ### Expected behaviorテンプレート
post_filterなどではrangeを正確に評価してほしい(2.19 と同じ)。近似してよいのは本命のクエリだけ。役割「何が正しいか」を一文で。ここが曖昧だと、仕様なのかバグなのかの議論から始まってしまう。
- ### Additional Detailsテンプレート
公式 Docker イメージ、単一ノード、セキュリティプラグインなし、macOS(arm64)。プラグインに依存しないこと。
役割環境の違いで再現しない、を防ぐ。
- 原因追加した節
近似を有効にする処理が、本命のクエリ以外にもかかっている。該当コードへのリンク(3.9.0 のタグに固定)と、3.0.0 では大きなセグメントでしか起きなかった理由。
役割テンプレートにはない節。調べたことを残しておくと、直す人が同じ調査をやり直さずに済む。
- 修正案追加した節
1 行の条件を足す diff、ローカルで足したテストと通した既存テスト、この直し方の弱点と別案、「よければ PR を出す」。
役割案として出す。弱点も自分から書く。決めるのはメンテナー。
修正案は、近似を有効にする処理を「本命のクエリのときだけ」に絞る 1 行の条件です。ただし、これは参照が同じかどうかで本命のクエリを見分けるので、誰かが本命のクエリを包み直すと近似が効かなくなる、という弱点があります。結果が間違うことはなく遅くなるだけ、という点も含めて本文に書き、別の直し方の案も添えました。
@Override
public Query rewrite(Query original) throws IOException {
- if (original instanceof ApproximateScoreQuery approximateScoreQuery) {
+ // Only the main query may be approximated: ...
+ if (original instanceof ApproximateScoreQuery approximateScoreQuery && searchContext != null && original == searchContext.query()) {
approximateScoreQuery.setContext(searchContext);
}テストも手元で回しました。修正前に落ちて修正後に通る新しいテストを足し、関係する既存のテストが通ることを確かめて、その一覧を書いています。修正案が「たぶん直る」ではなく「こう確かめた」になると、受け取る側の安心感がまったく違うはずです。
5出したあと
出したあとの流れは、思っていたよりずっと機械的で、そして速いものでした。
- 10/11 21:47 JSTバグ報告 · 自分バグ報告 #23303 を出す
テンプレートに沿って、再現・バージョン表・原因・修正案まで書いて出した。
- 10/11 21:48 JST出してから約 10 秒後バグ報告 · 自動ラベルとボードが自動で付く
10 秒ほどの間に
bug・untriaged・lucene・Search:Performanceのラベルが付き、プロジェクトのボードに載った。untriagedは「まだ誰も仕分けていない」の印。 - 10/12 03:05 JST出してから約 5 時間後バグ報告 · ほかの人別の人が修正 PR #23304 を出す
約 5 時間後。中身は Issue に書いた修正案と同じ 1 行の条件と、そのテスト。PR の本文に「Fixes #23303」とあり、Issue と結びついた。
- 10/12 03:41 JST出してから約 6 時間後バグ報告 · ほかの人PR の CI が承認待ちに
CI(Gradle check)が失敗し、その後の実行はメンテナーの承認待ちになった。PR の作者が承認と再実行をお願いしている。
- 10/15 01:12 JST出してから約 3 日後バグ報告 · メンテナートリアージされる
約 3 日後。メンテナーが「[Search Triage]」としてお礼を書き、レビュー担当を 2 人指名した。
untriagedとluceneのラベルが外れた。 - 10/25 03:13 JST機能要望 · 自分機能要望 #23429 を出す
数秒で
untriaged・Search・luceneのラベルが付き、ボードに載った。 - 10/28バグ報告 · 現在今: どちらもオープン
バグ報告は PR のレビュー待ち。機能要望はまだトリアージ待ちで、コメントは付いていない。
出した直後に、テンプレートと自動処理でラベルが付き、プロジェクトのボードに載りました。untriaged は「まだ誰も仕分けていない」の印で、メンテナーが中身を見て担当の領域を決めると外れます。バグ報告では、約 3 日後に検索まわりのトリアージで外れ、レビュー担当が指名されました。

意外だったのは、約 5 時間後に別の人が修正 PR を出したことです[2]。中身は Issue に書いた修正案と同じ条件でした。本文の最後に「よければ自分で PR を出す」と書いていましたが、修正案を具体的に書いておけば、ほかの人がそのまま PR にできます。Issue は「報告」であると同時に、誰かが直すための材料でもあるのだと実感しました。

6機能要望も出してみた
2 件目は機能要望です[3]。近似 k-NN 検索(Faiss エンジン)と post_filter を組み合わせると、同じクエリなのに、無関係なフィールドを更新しただけで結果が変わる、という現象を追っていました。原因は、k 件を選ぶ処理がセグメントごとに行われることです[6]。
これを確かめるには「どのヒットがどのセグメントから来たか」が分かればよいのですが、それを返す API はありません。ソートの値とセグメント一覧を突き合わせる回避策は使えたものの、前提が多く壊れやすい。そこで、version などと同じ形のオプトインのフラグで、ヒットごとのセグメントを返せるようにしてほしい、という提案にしました。
バグ報告との違いは、「正しい動作」が決まっていないことです。なので、困っている問題を再現できる形で示し、ほしい形を具体的な JSON で見せ、すでにある機能(_cat/segmentsや Profile API など)ではなぜ足りないかを 1 つずつ書きました。機能要望は「それなら〇〇で足りるのでは」と返されやすいので、代わりの案を先に調べて潰しておくのが大事だと考えました。こちらはまだトリアージ待ちです。
- タイトル
[Feature Request] Expose which Lucene segment each search hit came from (opt-in, for debugging)
役割何がほしいかに加えて、「opt-in」「for debugging」で影響の小ささと用途を先に伝える。
- ### Is your feature request related to a problem? Please describeテンプレート
k-NN と
post_filterを組み合わせると、ドキュメントがどのセグメントにいるかで結果が変わる現象。curl の再現と、今の回避策(_docソート +_cat/segments)が壊れやすい理由。役割機能要望でも、まず「困っていること」を再現できる形で示す。ほしい機能の必要性は、問題の具体さで決まる。
- ### Describe the solution you'd likeテンプレート
versionやseq_no_primary_termと同じ形の opt-in フラグ。返す JSON の例と、fetch の段階で読めるので小さく作れそうだという見立て。役割既存の API に似た形にすると、受け入れる側の想像がしやすい。完成形の例を見せると話が早い。
- ### Related componentテンプレート
Search
役割ラベルになり、担当の領域に届く。
- ### Describe alternatives you've consideredテンプレート
_cat/segments、_shard_doc、k-NN の explain、Profile API、efficient filtering。それぞれで何が分かって何が分からないか。役割「それなら〇〇で足りるのでは」を先回りして潰す。既存の機能をよく調べたことも伝わる。
- ### Additional contextテンプレート
2.19.5 と 3.9.0 で同じ結果になること、関連するドキュメントへのリンク。
役割前提をそろえる。
7心構え
2 件出してみて、大きめの OSS に Issue を出すときに大事だと思ったことをまとめます。
- 再現を小さくし、いつから壊れたかを調べると、Issue の説得力が一気に上がる。
- テンプレートの順はメンテナーが読みたい順。原因や修正案は、その後ろに足す。
- 修正案を具体的に書くと、自分以外の人もすぐ直しに動ける。
- 機能要望は、困っていることの再現と、既存の機能では足りない理由が要る。
8リンク
出した Issue と関連する PR、OpenSearch の資料です。Issue の本文は #23303 で全文を読めます。
- [1]kotta-27. [BUG] Range query approximation is applied to post_filter, rescore_query, named queries and inner_hits, silently dropping matches. opensearch-project/OpenSearch #23303
- [2]Shivampal157. Fix approximation on non-main queries. opensearch-project/OpenSearch #23304
- [3]kotta-27. [Feature Request] Expose which Lucene segment each search hit came from (opt-in, for debugging). opensearch-project/OpenSearch #23429
- [4]OpenSearch. CONTRIBUTING.md. opensearch-project/OpenSearch
- [5]OpenSearch. バグ報告のテンプレート(bug_template.yml). opensearch-project/OpenSearch
- [6]OpenSearch. Approximate k-NN search. OpenSearch Documentation 2.17