Skip to content

Add analysis-kuromoji plugin documentation - #12924

Open
WojciechKrakowiak wants to merge 8 commits into
opensearch-project:mainfrom
WojciechKrakowiak:kuromoji-docs
Open

Add analysis-kuromoji plugin documentation#12924
WojciechKrakowiak wants to merge 8 commits into
opensearch-project:mainfrom
WojciechKrakowiak:kuromoji-docs

Conversation

@WojciechKrakowiak

Copy link
Copy Markdown
Contributor

Description

Added quite complete documentation for analysis-kuromoji plugin, that covers analyzer, tokenizer and all token and character filters.

Documentation was created with help of IBM Bob, with analysis of OpenSearch/Lucene/kuromoji source codes. I carefully reviewed generated docs, verifying, testing and fixing nomenclature, every single example and available parameter. I checked translations and verified that I cannot google generated examples or pieces of documentation (to make sure they aren't taken from other websites).

I did my best to verify the content, though I don't speak japan and any additional verification is welcome (though it also may come later from users).

Version

List the OpenSearch version to which this PR applies, e.g. 2.14, 2.12--2.14, or all.

kuromoji-completion analyzer and tokenizer was added in 3.0 (basing on opensearch-project/OpenSearch#4835)
Other components have over 5 years.

Checklist

  • By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and subject to the Developers Certificate of Origin.
    For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@github-actions github-actions Bot added the In progress Issue/PR: The issue or PR is in progress. label Aug 13, 2026
@github-actions

Copy link
Copy Markdown

Thank you for submitting your PR. The PR states are In progress (or Draft) -> Tech review -> Doc review -> Merged.

Before you submit your PR for doc review, make sure the content is technically accurate. If you need help finding a tech reviewer, tag a maintainer.

When you're ready for doc review, tag the assignee of this PR. The doc reviewer may push edits to the PR directly or leave comments and editorial suggestions for you to address (let us know in a comment if you have a preference).

Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
@WojciechKrakowiak
WojciechKrakowiak marked this pull request as ready for review August 13, 2026 13:11
@github-actions github-actions Bot added Tech review PR: Tech review in progress and removed In progress Issue/PR: The issue or PR is in progress. labels Aug 13, 2026
@kolchfa-aws kolchfa-aws added Doc review PR: Doc review in progress backport 3.8 and removed Tech review PR: Tech review in progress labels Aug 14, 2026
Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
@WojciechKrakowiak

Copy link
Copy Markdown
Contributor Author

I fixed all style issues reported by reviewdog that I considered relevant.

Remaining ones are treating names or japanese-related terms as options and I consider them false positive.

@kolchfa-aws

Copy link
Copy Markdown
Collaborator

Thank you, @WojciechKrakowiak! This is great. Trying to find a Japanese-speaking reviewer now.

@lawofcycles

Copy link
Copy Markdown
Contributor

I will review this from the perspective of a native Japanese speaker. Please wait a moment.

Comment thread _analyzers/tokenizers/kuromoji.md Outdated
Comment thread _analyzers/language-analyzers/kuromoji.md Outdated
Comment thread _analyzers/language-analyzers/kuromoji.md Outdated
Comment thread _analyzers/language-analyzers/kuromoji.md Outdated
```
{% include copy-curl.html %}

The analyzer uses the custom dictionary rule to segment `東京スカイツリー` into `東京` and `スカイツリー`. The default Katakana stemmer (`kuromoji_stemmer`) then removes the trailing long vowel mark from `スカイツリー` to produce `スカイツリ`, while particles (`に`) and auxiliary verbs (`ました`) are removed, and `行きました` is normalized to its base form `行く`:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kuromoji splits 行きました into three tokens (行き, まし, ), so ました is not a single auxiliary verb. kuromoji_baseform normalizes 行き to 行く, and kuromoji_part_of_speech removes まし and separately.


Parameter | Data type | Description
:--- | :--- | :---
`mode` | String | Completion mode. Valid values are `index` (default) and `query`. Use `index` in the index analyzer to generate all reading variants; use `query` in the search analyzer to generate the query-side reading.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads as if only index mode generates variants. Actually both modes emit variants; query mode additionally merges Kana with trailing lowercase letters (mid-typing IME input, e.g. コンピュ + tkonpyut) so autocomplete matches on in-progress input.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify my earlier comment: both modes emit Romaji variants when given complete text like コンピューター. The actual difference is how they handle partial IME input like コンピュt (Kana + trailing alphabet from mid-typing): index mode splits it into [コンピュ, t], while query mode merges it as コンピュtkonpyut. This is why the pair is needed — index mode indexes completed text, query mode handles in-progress input. The current description doesn't convey this split.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I think I have it well described on the kuromoji-completion token filter page, I'll simply reuse the same description.

Controls how tokens are generated. Valid values are `index` (default) and `query`. Both modes expand Katakana tokens into their original form plus all Romaji variants. The `query` mode adds two additional behaviors for handling partial IME input: it concatenates consecutive Kana tokens into a single token before romanizing, and it merges a Kana token with a trailing lowercase alphabet token that represents a partially typed IME keystroke (for example, `サッ` followed by `k` becomes `サッk`).

Comment thread _analyzers/language-analyzers/kuromoji.md Outdated
Comment thread _analyzers/token-filters/ja-stop.md Outdated
Comment thread _analyzers/token-filters/ja-stop.md Outdated
Comment thread _analyzers/token-filters/ja-stop.md Outdated
Comment thread _analyzers/token-filters/ja-stop.md Outdated

Compare this with the following [full pipeline example](#example-usage-with-kuromoji_baseform-and-kuromoji_part_of_speech), which uses the same input. Adding `kuromoji_baseform` normalises `学ん` to `学ぶ`, and `kuromoji_part_of_speech` removes `を` and `だ` as grammatical tokens — leaving `ja_stop` to handle `こと`, a common noun that part-of-speech filtering alone would not remove.

## Example: Usage with kuromoji_baseform and kuromoji_part_of_speech

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ex1 (ja_stop only) and Ex2 (full pipeline) produce nearly identical results on this input — both remove な, こと, を, だ. The only observable difference is 学ん vs 学ぶ (from baseform). The claim "leaving ja_stop to handle こと" doesn't hold in this pairing since Ex1 already removes こと.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will provide a better example, in which results differ by number of resulting tokens and a baseform of one token.

The sentence `新聞を読んでいるばかりだ` ("I do nothing but read the newspaper") produces the tokens `新聞`, `を`, `読ん`, `で`, `いる`, `ばかり`, `だ`.

In minimal example:

{
  "tokens": [
    {
      "token": "新聞",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "読ん",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 2
    },
    {
      "token": "ばかり",
      "start_offset": 8,
      "end_offset": 11,
      "type": "word",
      "position": 5
    }
  ]
}


`を`, `で`, `いる`, and `だ` are in the `_japanese_` stop set and are removed.

In pipeline example:

{
  "tokens": [
    {
      "token": "新聞",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "読む",
      "start_offset": 3,
      "end_offset": 5,
      "type": "word",
      "position": 2
    }
  ]
}

- **`kuromoji_baseform`** normalises the inflected verb stem `読ん` to its dictionary form `読む`.
- **`kuromoji_part_of_speech`** removes `ばかり` (adverbial particle) that would not be handled by `ja_stop`, and removes particles `を`, `で`, and `だ` (which would also be removed by `ja_stop`).
- **`ja_stop`** removes remaining `いる`, which is listed in the built-in Japanese stop set.

}
```

The past-tense forms 食べて and 飲んだ are replaced with their base forms 食べる and 飲む. The particles を and auxiliary verbs て and だ remain in the token stream because `kuromoji_baseform` only normalizes inflection; it does not remove grammatical tokens.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

食べて is te-form (a conjunctive form), not past tense — past tense would be 食べた. 飲んだ is past tense. Also is a conjunctive particle, not an auxiliary verb.

# Kuromoji completion token filter

The `kuromoji_completion` token filter is used to stem Katakana words in Japanese, which are often used to represent foreign words or loanwords. This filter is especially useful for autocompletion or suggest queries, in which partial matches on Katakana words can be expanded to include their full forms.
The `kuromoji_completion` token filter generates romanized reading variants for Katakana tokens. When used in an index analyzer, it emits both the original token and one or more romanized alternatives at the same position. This allows users to search for Japanese content by typing in either Japanese characters or their phonetic equivalents.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Katakana tokens" is inaccurate — Romaji variants are generated for any Japanese token with reading info, not just Katakana. In the example on L89, the Kanji+Hiragana token 使う produces tukau/tsukau.


Parameter | Data type | Description
:--- | :--- | :---
`mode` | String | Completion mode. Valid values are `index` (default) and `query`. Use `index` in the index analyzer to generate all reading variants; use `query` in the search analyzer to generate the query-side reading.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify my earlier comment: both modes emit Romaji variants when given complete text like コンピューター. The actual difference is how they handle partial IME input like コンピュt (Kana + trailing alphabet from mid-typing): index mode splits it into [コンピュ, t], while query mode merges it as コンピュtkonpyut. This is why the pair is needed — index mode indexes completed text, query mode handles in-progress input. The current description doesn't convey this split.


For the full list of available stoptags, see [stoptags.txt](https://github.com/apache/lucene/blob/main/lucene/analysis/kuromoji/src/resources/org/apache/lucene/analysis/ja/stoptags.txt) in the Lucene source.

IPAdic tags use a hierarchical structure separated by hyphens. An entry of `助詞` matches all particles, while `助詞-格助詞` matches only case particles. Entries in `stoptags` are treated as prefix matches against the full POS tag.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stoptags matching is exact, not prefix. Lucene's JapanesePartOfSpeechStopFilter uses stopTags.contains(pos) (Set equality). An entry of 助詞 does NOT match 助詞-連体化 or 助詞-格助詞-一般. To remove all particles, every subcategory (助詞, 助詞-格助詞, 助詞-格助詞-一般, ...) must be listed explicitly — this is why the default stoptags.txt enumerates them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll remove this whole note and update the parameter description.

```
{% include copy-curl.html %}

The response shows the particles の (の, genitive), で (location marker), and を (object marker) removed:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The (の, ...) looks like a typo — the romanized form was likely intended. Also "location marker" / "object marker" are informal; standard terms are "locative" / "accusative".

Comment thread _analyzers/token-filters/kuromoji-stemmer.md Outdated
Comment thread _analyzers/tokenizers/kuromoji.md Outdated
@lawofcycles

Copy link
Copy Markdown
Contributor

@WojciechKrakowiak Thank you for well written documentation! I left some comments.


The filter applies to tokens that carry dictionary form information from the Kuromoji tokenizer. Tokens without dictionary information (such as unknown words) are passed through unchanged.

Note that the Kuromoji tokenizer splits some conjugated forms into multiple tokens before this filter runs. For example, the past-tense *i*-adjective 美しかった (was beautiful) is split into 美しかっ and た. The filter normalizes 美しかっ to 美しい, but た remains as a separate token. To remove auxiliary verb tokens like た, add [`kuromoji_part_of_speech`]({{site.url}}{{site.baseurl}}/analyzers/token-filters/kuromoji-part-of-speech/) and [`ja_stop`]({{site.url}}{{site.baseurl}}/analyzers/token-filters/ja-stop/) to the filter chain.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph and L16 make the same point twice — Kuromoji splits inflected forms and kuromoji_baseform normalizes only the stem. Consider consolidating.

@WojciechKrakowiak

Copy link
Copy Markdown
Contributor Author

@lawofcycles thank you for the throughout review! I'll work with it and apply changes :)

Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
Signed-off-by: Wojciech Krakowiak <wojciech.krakowiak@ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport 3.8 Doc review PR: Doc review in progress

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants