お知らせ - 11系 — Groonga v14.0.9ドキュメント

Skip to main content
`Ctrl`+`K`

# お知らせ - 11系

## 11.1.3リリース - 2022-01-29

### 改良

- [snippet] 32個以上のキーワードを使えるようにしました。 [GitHub#1313][Takashi Hashidaさんがパッチ提供]

  いままで、 `snippet` は32個以上のキーワードを指定できませんでしたが、この改良によって、以下のように32個以上のキーワードを指定できます。

      table_create Entries TABLE_NO_KEY
      column_create Entries content COLUMN_SCALAR ShortText

      load --table Entries
      [
      {"content": "Groonga is a fast and accurate full text search engine based on inverted index. One of the characteristics of Groonga is that a newly registered document instantly appears in search results. Also, Groonga allows updates without read locks. These characteristics result in superior performance on real-time applications.\nGroonga is also a column-oriented database management system (DBMS). Compared with well-known row-oriented systems, such as MySQL and PostgreSQL, column-oriented systems are more suited for aggregate queries. Due to this advantage, Groonga can cover weakness of row-oriented systems.\nThe basic functions of Groonga are provided in a C library. Also, libraries for using Groonga in other languages, such as Ruby, are provided by related projects. In addition, groonga-based storage engines are provided for MySQL and PostgreSQL. These libraries and storage engines allow any application to use Groonga. See usage examples."},
      {"content": "In widely used DBMSs, updates are immediately processed, for example, a newly registered record appears in the result of the next query. In contrast, some full text search engines do not support instant updates, because it is difficult to dynamically update inverted indexes, the underlying data structure.\nGroonga also uses inverted indexes but supports instant updates. In addition, Groonga allows you to search documents even when updating the document collection. Due to these superior characteristics, Groonga is very flexible as a full text search engine. Also, Groonga always shows good performance because it divides a large task, inverted index merging, into smaller tasks."}
      ]

      select Entries \
        --output_columns ' \
        snippet(content, \
        "groonga", "inverted", "index", "fast", "full", "text", "search", "engine", "registered", "document", \
        "results", "appears", "also", "system", "libraries", "for", "mysql", "postgresql", "column-oriented", "dbms", \
        "basic", "ruby", "projects", "storage", "allow", "application", "usage", "sql", "well-known", "real-time", \
        "weakness", "merging", "performance", "superior", "large", "dynamically", "difficult", "query", "examples", "divides", \
        { \
          "default_open_tag": "[", \
          "default_close_tag": "]", \
          "width" : 2048 \
        })'
      [
        [
          0,
          1643165838.691991,
          0.0003311634063720703
        ],
        [
          [
            [
              2
            ],
            [
              [
                "snippet",
                null
              ]
            ],
            [
              [
                "[Groonga] is a [fast] and accurate [full] [text] [search] [engine] based on [inverted] [index]. One of the characteristics of [Groonga] is that a newly [registered] [document] instantly [appears] in [search] [results]. [Also], [Groonga] [allow]s updates without read locks. These characteristics result in [superior] [performance] on [real-time] [application]s.\n[Groonga] is [also] a [column-oriented] database management [system] ([DBMS]). Compared with [well-known] row-oriented [system]s, such as [MySQL] and [PostgreSQL], [column-oriented] [system]s are more suited [for] aggregate queries. Due to this advantage, [Groonga] can cover [weakness] of row-oriented [system]s.\nThe [basic] functions of [Groonga] are provided in a C library. [Also], [libraries] [for] using [Groonga] in other languages, such as [Ruby], are provided by related [projects]. In addition, [groonga]-based [storage] [engine]s are provided [for] [MySQL] and [PostgreSQL]. These [libraries] and [storage] [engine]s [allow] any [application] to use [Groonga]. See [usage] [examples]."
              ]
            ],
            [
              [
                "In widely used [DBMS]s, updates are immediately processed, [for] example, a newly [registered] record [appears] in the result of the next [query]. In contrast, some [full] [text] [search] [engine]s do not support instant updates, because it is [difficult] to [dynamically] update [inverted] [index]es, the underlying data structure.\n[Groonga] [also] uses [inverted] [index]es but supports instant updates. In addition, [Groonga] [allow]s you to [search] [document]s even when updating the [document] collection. Due to these [superior] characteristics, [Groonga] is very flexible as a [full] [text] [search] [engine]. [Also], [Groonga] always shows good [performance] because it [divides] a [large] task, [inverted] [index] [merging], into smaller tasks."
              ]
            ]
          ]
        ]
      ]

- [NormalizerNFKC130] 新しいオプション `remove_symbol` を追加しました。

  このオプションは、以下のように正規化対象の文字列から記号(例えば #, !, ", &, % 等)を削除します。

      normalize   'NormalizerNFKC130("remove_symbol", true)'   "#This & is %% a pen."   WITH_TYPES
      [
        [
          0,
          1643595008.729597,
          0.0005540847778320312
        ],
        {
          "normalized": "this  is  a pen",
          "types": [
            "alpha",
            "alpha",
            "alpha",
            "alpha",
            "others",
            "others",
            "alpha",
            "alpha",
            "others",
            "others",
            "alpha",
            "others",
            "alpha",
            "alpha",
            "alpha"
          ],
          "checks": [
          ]
        }
      ]

- [AlmaLinux] ARM64版 AlmaLinux 8 向けのパッケージをサポートしました。

- [httpd] バンドルしているnginxのバージョンを1.21.5に更新しました。

- [Documentation] ruby_eval の誤記を修正しました。[GitHub#1317][wi24rdさんがパッチ提供]

- [Ubuntu] Ubuntu 21.04 (Hirsute Hippo)サポートをやめました。

  - Ubuntu 21.04 は、2022年1月20日でEOLとなったためです。

### 修正

- [load] 存在しないカラムを指定してロードした時にクラッシュする問題を修正しました。

  この問題は、 `load` の引数として、 `input_type` に `apache-arrow` を指定したときのみ発生します。

- Groongaが依存している arrow-libs がバージョンアップしたことによって、Groongaのバージョンアップが失敗する問題を修正しました。[groonga-talk,540][Josep Sanzさんの報告][Gitter,61eaaa306d9ba23328d23ce1][shibanao4870さんの報告][GitHub#1316][Keitaro YOSHIMURAさんの報告]

  ただし、 arrow-libs がメジャーバージョンアップした場合は、この問題は再発します。その場合は、Groongaパッケージを再構築して対応する予定です。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

### 感謝

- Takashi Hashidaさん

- wi24rdさん

- Josep Sanzさん

- Keitaro YOSHIMURAさん

- shibanao4870さん

## 11.1.1リリース - 2021-12-29

### 改良

- [select] 組み合わせフレーズ近傍検索をサポートしました。

  この機能は、 `'*NP"..." OR *NP"..." OR ...'` の短縮形です。例えば、以下のように、 `query` を使って `*NP` を複数実行する式のかわりに `*NPP` を使えます。

      query ("title * 10 || content",
             "*NP"a 1 x" OR
              *NP"a 1 y" OR
              *NP"a 1 z" OR
              *NP"a 2 x" OR
              *NP"a 2 y" OR
              *NP"a 2 z" OR
              *NP"a 3 x" OR
              *NP"a 3 y" OR
              *NP"a 3 z" OR
              *NP"b 1 x" OR
              *NP"b 1 y" OR
              *NP"b 1 z" OR
              *NP"b 2 x" OR
              *NP"b 2 y" OR
              *NP"b 2 z" OR
              *NP"b 3 x" OR
              *NP"b 3 y" OR
              *NP"b 3 z"")

  この機能によって、上記の式を `*NPP"(a b) (1 2 3) (x y z)"` と書くことができます。加えて、 `*NPP"(a b) (1 2 3) (x y z)"` は、 `'*NP"..." OR *NP"..." OR ...'` より高速です。

      query ("title * 10 || content",
             "*NPP"(a b) (1 2 3) (x y z)"")

  この機能は、 `'*NP"..." OR *NP"..." OR ...'` のような近傍フレーズ検索のパフォーマンスを改善するために実装されました。

- [select] 順序付き組み合わせフレーズ近傍検索をサポートしました。

  この機能は、 `'*ONP"..." OR *ONP"..." OR ...'` の短縮形です。例えば、以下のように、 `query` を使って `*ONP` を複数実行する式のかわりに `*ONPP` を使えます。

      query ("title * 10 || content",
             "*ONP"a 1 x" OR
              *ONP"a 1 y" OR
              *ONP"a 1 z" OR
              *ONP"a 2 x" OR
              *ONP"a 2 y" OR
              *ONP"a 2 z" OR
              *ONP"a 3 x" OR
              *ONP"a 3 y" OR
              *ONP"a 3 z" OR
              *ONP"b 1 x" OR
              *ONP"b 1 y" OR
              *ONP"b 1 z" OR
              *ONP"b 2 x" OR
              *ONP"b 2 y" OR
              *ONP"b 2 z" OR
              *ONP"b 3 x" OR
              *ONP"b 3 y" OR
              *ONP"b 3 z"")

  この機能によって、上記の式を `*ONPP"(a b) (1 2 3) (x y z)"` と書くことができます。加えて、 `*ONPP"(a b) (1 2 3) (x y z)"` は、 `'*ONP"..." OR *ONP"..." OR ...'` より高速です。

      query ("title * 10 || content",
             "*ONPP"(a b) (1 2 3) (x y z)"")

  この機能は `'*ONP"..." OR *ONP"..." OR ...'` のような順序付き近傍フレーズ検索のパフォーマンスを改善するために実装されました。

- [request_cancel] 検索実行中に `request_cancel` を検知しやすくしました。

  `request_cancel` を検知するために、リターンコードのチェックを増やしたためです。

- [thread_dump] 新しいコマンド `thread_dump` を追加しました。

  現状、このコマンドはWindowsでしか動作しません。

  このコマンド実行時の全てのスレッドのバックトレースをNOTICEレベルのログとして出力できます。

  この機能は、Groongaが応答を返さない等の問題を解決するのに役立ちます。

- [CentOS] CentOS 8のサポートをやめました。

  CentOS 8は、2021年12月31日でEOLとなるためです。

### 修正

- 無効なパラメータを持つインデックスカラムを削除できない問題を修正しました。 [GitHub#1301][Takashi Hashidaさんがパッチ提供]

  - 例えば、以下のように `column_create` を使って無効なインデックスカラムを作成した時、テーブルが削除できなくなります。

        table_create Statuses TABLE_NO_KEY
        column_create Statuses start_time COLUMN_SCALAR UInt16
        column_create Statuses end_time COLUMN_SCALAR UInt16

        table_create Times TABLE_PAT_KEY UInt16
        column_create Times statuses COLUMN_INDEX Statuses start_time,end_time
        [
          [
            -22,
            1639037503.16114,
            0.003981828689575195,
            "grn_obj_set_info(): GRN_INFO_SOURCE: multi column index must be created with WITH_SECTION flag: <Times.statuses>",
            [
              [
                "grn_obj_set_info_source_validate",
                "../../groonga/lib/db.c",
                9605
              ],
              [
                "/tmp/d.grn",
                6,
                "column_create Times statuses COLUMN_INDEX Statuses start_time,end_time"
              ]
            ]
          ],
          false
        ]
        table_remove Times
        [
          [
            -22,
            1639037503.16515,
            0.0005414485931396484,
            "[object][remove] column is broken: <Times.statuses>",
            [
              [
                "remove_columns",
                "../../groonga/lib/db.c",
                10649
              ],
              [
                "/tmp/d.grn",
                8,
                "table_remove Times"
              ]
            ]
          ],
          false
        ]

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

### 感謝

- Takashi Hashidaさん

## 11.1.0リリース - 2021-11-29

### 改良

- [load] ISO 8601 形式をサポートしました。[GitHub#1228][Takashi Hashidaさんがパッチ提供]

  この変更によって、`load` は、以下のフォーマットをサポートします。

  > - YYYY-MM-ddThh:mm:ss.sZ

  > - YYYY-MM-ddThh:mm:ss.s+10:00

  > - YYYY-MM-ddThh:mm:ss.s-10:00

  この構文の `T` と `Z` の代わりに `t` と `z` を使うこともできます。`-` の代わりに `/` を使うこともできます。ただし、 `/` は、ISO 8601 形式ではないことに注意してください。`/` は互換性のためにサポートしています。

      plugin_register functions/time

      table_create Logs TABLE_NO_KEY
      column_create Logs case COLUMN_SCALAR ShortText
      column_create Logs created_at COLUMN_SCALAR Time
      column_create Logs created_at_text COLUMN_SCALAR ShortText

      load --table Logs
      [
      {"case": "timezone: Z", "created_at": "2000-01-01T10:00:00Z", "created_at_text": "2000-01-01T10:00:00Z"},
      {"case": "timezone: z", "created_at": "2000-01-01t10:00:00z", "created_at_text": "2000-01-01T10:00:00z"},
      {"case": "timezone: 00:00", "created_at": "2000-01-01T10:00:00+00:00", "created_at_text": "2000-01-01T10:00:00+00:00"},
      {"case": "timezone: +01:01", "created_at": "2000-01-01T11:01:00+01:01", "created_at_text": "2000-01-01T11:01:00+01:01"},
      {"case": "timezone: +11:11", "created_at": "2000-01-01T21:11:00+11:11", "created_at_text": "2000-01-01T21:11:00+11:11"},
      {"case": "timezone: -01:01", "created_at": "2000-01-01T08:59:00-01:01", "created_at_text": "2000-01-01T08:59:00-01:01"},
      {"case": "timezone: -11:11", "created_at": "1999-12-31T22:49:00-11:11", "created_at_text": "1999-12-31T22:49:00-11:11"},
      {"case": "timezone hour threshold: +23:00", "created_at": "2000-01-02T09:00:00+23:00", "created_at_text": "2000-01-02T09:00:00+23:00"},
      {"case": "timezone minute threshold: +00:59", "created_at": "2000-01-01T10:59:00+00:59", "created_at_text": "2000-01-01T10:59:00+00:59"},
      {"case": "timezone omitting minute: +01", "created_at": "2000-01-01T11:00:00+01", "created_at_text": "2000-01-01T11:00:00+01"},
      {"case": "timezone omitting minute: -01", "created_at": "2000-01-01T09:00:00-01", "created_at_text": "2000-01-01T09:00:00-01"},
      {"case": "timezone: localtime", "created_at": "2000-01-01T19:00:00", "created_at_text": "2000-01-01T19:00:00"},
      {"case": "compatible: date delimiter: /", "created_at": "2000/01/01T10:00:00Z", "created_at_text": "2000/01/01T10:00:00Z"},
      {"case": "decimal", "created_at": "2000-01-01T11:01:00.123+01:01", "created_at_text": "2000-01-01T11:01:00.123+01:01"}
      ]

      select Logs \
        --limit -1 \
        --output_columns "case, time_format_iso8601(created_at), created_at_text"
      [
        [
          0,
          0.0,
          0.0
        ],
        [
          [
            [
              14
            ],
            [
              [
                "case",
                "ShortText"
              ],
              [
                "time_format_iso8601",
                null
              ],
              [
                "created_at_text",
                "ShortText"
              ]
            ],
            [
              "timezone: Z",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T10:00:00Z"
            ],
            [
              "timezone: z",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T10:00:00z"
            ],
            [
              "timezone: 00:00",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T10:00:00+00:00"
            ],
            [
              "timezone: +01:01",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T11:01:00+01:01"
            ],
            [
              "timezone: +11:11",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T21:11:00+11:11"
            ],
            [
              "timezone: -01:01",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T08:59:00-01:01"
            ],
            [
              "timezone: -11:11",
              "2000-01-01T19:00:00.000000+09:00",
              "1999-12-31T22:49:00-11:11"
            ],
            [
              "timezone hour threshold: +23:00",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-02T09:00:00+23:00"
            ],
            [
              "timezone minute threshold: +00:59",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T10:59:00+00:59"
            ],
            [
              "timezone omitting minute: +01",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T11:00:00+01"
            ],
            [
              "timezone omitting minute: -01",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T09:00:00-01"
            ],
            [
              "timezone: localtime",
              "2000-01-01T19:00:00.000000+09:00",
              "2000-01-01T19:00:00"
            ],
            [
              "compatible: date delimiter: /",
              "2000-01-01T19:00:00.000000+09:00",
              "2000/01/01T10:00:00Z"
            ],
            [
              "decimal",
              "2000-01-01T19:00:00.123000+09:00",
              "2000-01-01T11:01:00.123+01:01"
            ]
          ]
        ]
      ]

- [select] 新しい `query_flags` `DISABLE_PREFIX_SEARCH` を追加しました。

  以下のように、`DISABLE_PREFIX_SEARCH` によって、前方一致検索の演算子の `^` と `*` を検索キーワードとして使えます。

  この機能は、 `^` や `*` を含むドキュメントを検索したい時に便利です。

      table_create Users TABLE_PAT_KEY ShortText

      load --table Users
      [
      {"_key": "alice"},
      {"_key": "alan"},
      {"_key": "ba*"}
      ]

      select Users \
        --match_columns "_key" \
        --query "a*" \
        --query_flags "DISABLE_PREFIX_SEARCH"
      [[0,0.0,0.0],[[[1],[["_id","UInt32"],["_key","ShortText"]],[3,"ba*"]]]]

      table_create Users TABLE_PAT_KEY ShortText

      load --table Users
      [
      {"_key": "alice"},
      {"_key": "alan"},
      {"_key": "^a"}
      ]

      select Users \
        --query "_key:^a" \
        --query_flags "ALLOW_COLUMN|DISABLE_PREFIX_SEARCH"
      [[0,0.0,0.0],[[[1],[["_id","UInt32"],["_key","ShortText"]],[3,"^a"]]]]

- [select] 新しい `query_flags` `DISABLE_AND_NOT` を追加しました。

  以下のように、 `DISABLE_AND_NOT` によって、 `AND NOT` の演算子の `-` を検索キーワードとして使えます。

  この機能は、 `-` を含むドキュメントを検索したい時に便利です。

      table_create Users TABLE_PAT_KEY ShortText

      load --table Users
      [
      {"_key": "alice"},
      {"_key": "bob"},
      {"_key": "cab-"}
      ]

      select Users   --match_columns "_key"   --query "b - a"   --query_flags "DISABLE_AND_NOT"
      [[0,0.0,0.0],[[[1],[["_id","UInt32"],["_key","ShortText"]],[3,"cab-"]]]]

### 修正

- [ブラウザーベースの管理ツール] レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信される問題を修正しました。 [Github#1186][Takashi Hashidaさんがパッチ提供]

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

### 感謝

- Takashi Hashidaさん

## 11.0.9リリース - 2021-11-04

### 改良

- [snippet] 正規表現を使ってスニペットの区切り文字を見つける新しいオプション `delimiter_regexp` を追加しました。

  snippet は、検索キーワードの周辺のテキストを抽出します。 snippet で抽出したテキストをスニペットと呼びます。

  通常、 snippet は、検索キーワードの周辺の200byteのテキストを返しますが、 snippet は、センテンスの区切りを考慮しません。スニペットは、複数のセンテンスで構成されることがあります。

  `delimiter_regexp` オプションは、検索キーワードと同じセンテンスのテキストのみを抽出したいときに便利です。例えば、以下のように、 `\.\s*` を使って対象のセンテンス内のテキストのみを抽出できます。文字列は、 `\` でエスケープする必要があることに注意してください。

  >     table_create Documents TABLE_NO_KEY
  >     column_create Documents content COLUMN_SCALAR Text

  >     table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram  --normalizer NormalizerAuto
  >     column_create Terms documents_content_index COLUMN_INDEX|WITH_POSITION Documents content

  >     load --table Documents
  >     [
  >     ["content"],
  >     ["Groonga is a fast and accurate full text search engine based on inverted index. One of the characteristics of groonga is that a newly registered document instantly appears in search results. Also, groonga allows updates without read locks. These characteristics result in superior performance on real-time applications."],
  >     ["Groonga is also a column-oriented database management system (DBMS). Compared with well-known row-oriented systems, such as MySQL and PostgreSQL, column-oriented systems are more suited for aggregate queries. Due to this advantage, groonga can cover weakness of row-oriented systems."]
  >     ]

  >     select Documents \
  >       --output_columns 'snippet(content, \
  >                                 { \
  >                                    "default_open_tag": "[", \
  >                                    "default_close_tag": "]", \
  >                                    "delimiter_regexp": "\\\\.\\\\s*" \
  >                                 })' \
  >       --match_columns content \
  >       --query "fast performance"
  >     [
  >       [
  >         0,
  >         1337566253.89858,
  >         0.000355720520019531
  >       ],
  >       [
  >         [
  >           [
  >             1
  >           ],
  >           [
  >             [
  >               "snippet",
  >               null
  >             ]
  >           ],
  >           [
  >             [
  >               "Groonga is a [fast] and accurate full text search engine based on inverted index",
  >               "These characteristics result in superior [performance] on real-time applications"
  >             ]
  >           ]
  >         ]
  >       ]
  >     ]

- [window_rank] 新しい関数 `window_rank()` を追加しました。

  - ギャップを含む各レコードの順位を計算できます。通常、順位は、複数のレコードが同じ順位の時は増加しません。例えば、ソートキーの値が 100、 100、 200の場合、これらの順位は 1、 1、 3です。1位のレコードが2つあるので、最後のレコードの順位は2ではなく3です。

    これは、 window_record_number と似ていますが、 window_record_number はギャップを考慮しません。

        table_create Points TABLE_NO_KEY
        column_create Points game COLUMN_SCALAR ShortText
        column_create Points score COLUMN_SCALAR UInt32

        load --table Points
        [
        ["game",  "score"],
        ["game1", 100],
        ["game1", 200],
        ["game1", 100],
        ["game1", 400],
        ["game2", 150],
        ["game2", 200],
        ["game2", 200],
        ["game2", 200]
        ]

        select Points \
          --columns[rank].stage filtered \
          --columns[rank].value 'window_rank()' \
          --columns[rank].type UInt32 \
          --columns[rank].window.sort_keys score \
          --output_columns 'game, score, rank' \
          --sort_keys score
        [
          [
            0,
            1337566253.89858,
            0.000355720520019531
          ],
          [
            [
              [
                8
              ],
              [
                [
                  "game",
                  "ShortText"
                ],
                [
                  "score",
                  "UInt32"
                ],
                [
                  "rank",
                  "UInt32"
                ]
              ],
              [
                "game1",
                100,
                1
              ],
              [
                "game1",
                100,
                1
              ],
              [
                "game2",
                150,
                3
              ],
              [
                "game2",
                200,
                4
              ],
              [
                "game2",
                200,
                4
              ],
              [
                "game1",
                200,
                4
              ],
              [
                "game2",
                200,
                4
              ],
              [
                "game1",
                400,
                8
              ]
            ]
          ]
        ]

- [in_values] テーブル検索時に自動的にキャストするようにしました。

  例えば、 キーの型が `UInt64` のテーブルに `UInt32` の値をロードした場合、Groongaは、 `in_values()` でテーブルを検索するときに自動的に値を `UInt64` にキャストします。ただ、 `in_values(_key, 10)` はこの機能の対象外です。10は、 `Int32` として解釈されるためです。

      table_create Numbers TABLE_HASH_KEY UInt64
      load --table Numbers
      [
      {"_key": 100},
      {"_key": 200},
      {"_key": 300}
      ]

      select Numbers   --output_columns _key   --filter 'in_values(_key, 200, 100)'   --sortby _id
      [[0,0.0,0.0],[[[2],[["_key","UInt64"]],[100],[200]]]]

- [httpd] バンドルしているnginxのバージョンを1.21.3に更新しました。

- [AlmaLinux] AlmaLinux 8 向けのパッケージをサポートしました。

- [Ubuntu] Ubuntu 21.10 (Impish Indri)をサポートしました。

### 修正

- Groongaがコマンドエラー(例えば、filterのシンタックスエラー)発生時に応答を返さない問題を修正しました。

  - これは、 `--output_type apache-arrow` を使っているときのみ発生します。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

## 11.0.7リリース - 2021-09-29

### 改良

- [load] "[int, int,...]" のような文字列を [int, int,...] のような整数のベクターにキャストするようにしました。

  例えば、以下のように、"[1, -2]" のようなベクターを文字列としてロードしたとしても、 [1, -2] のように整数のベクターとして扱います。

  >     table_create Data TABLE_NO_KEY
  >     column_create Data numbers COLUMN_VECTOR Int16
  >     table_create Numbers TABLE_PAT_KEY Int16
  >     column_create Numbers data_numbers COLUMN_INDEX Data numbers

  >     load --table Data
  >     [
  >     {"numbers": "[1, -2]"},
  >     {"numbers": "[-3, 4]"}
  >     ]

  >     dump   --dump_plugins no   --dump_schema no
  >     load --table Data
  >     [
  >     ["_id","numbers"],
  >     [1,[1,-2]],
  >     [2,[-3,4]]
  >     ]

  >     column_create Numbers data_numbers COLUMN_INDEX Data numbers
  >     select Data --filter 'numbers @ -2'
  >     [[0,0.0,0.0],[[[1],[["_id","UInt32"],["numbers","Int16"]],[1,[1,-2]]]]]

  この機能は以下の型をサポートします。

  > - Int8

  > - UInt8

  > - Int16

  > - UInt16

  > - Int32

  > - UInt32

  > - Int64

  > - UInt64

- [load] 文字列として表現されたJSON配列を文字列のベクターとしてロードできるようにしました。

  例えば、以下のように "["hello", "world"]" のような文字列として表現されたJSON配列をロードした場合、 ["hello", "world"] のように2つの要素を持つベクターとして扱います。

      table_create Data TABLE_NO_KEY
      [[0,0.0,0.0],true]
      column_create Data strings COLUMN_VECTOR ShortText
      [[0,0.0,0.0],true]
      table_create Terms TABLE_PAT_KEY ShortText   --normalizer NormalizerNFKC130   --default_tokenizer TokenNgram
      [[0,0.0,0.0],true]
      column_create Terms data_strings COLUMN_INDEX Data strings
      [[0,0.0,0.0],true]
      load --table Data
      [
      {"strings": "[\"Hello\", \"World\"]"},
      {"strings": "[\"Good-bye\", \"World\"]"}
      ]
      [[0,0.0,0.0],2]
      dump   --dump_plugins no   --dump_schema no
      load --table Data
      [
      ["_id","strings"],
      [1,["Hello","World"]],
      [2,["Good-bye","World"]]
      ]

      column_create Terms data_strings COLUMN_INDEX Data strings
      select Data --filter 'strings @ "bye"'
      [
        [
          0,
          0.0,
          0.0
        ],
        [
          [
            [
              1
            ],
            [
              [
                "_id",
                "UInt32"
              ],
              [
                "strings",
                "ShortText"
              ]
            ],
            [
              2,
              [
                "Good-bye",
                "World"
              ]
            ]
          ]
        ]
      ]

  以前のバージョンでは、 "["hello", "world"]" のような文字列として表現されたJSON配列をロードした場合は、 ["["hello", "world"]"] のような一つの要素を持つベクターとして扱っていました。

- [Documentation] 以下の項目についてのドキュメントを追加しました。

  - [column_create] `WEIGHT_FLOAT32` フラグについてのドキュメントを追加しました。

  - [NormalizerNFKC121] `NormalizerNFKC121` についてのドキュメントを追加しました。

  - [NormalizerNFKC130] `NormalizerNFKC130` についてのドキュメントを追加しました。

  - [NormalizerTable] `NormalizerTable` についてのドキュメントを追加しました。

- Groongaが要求する Apache Arrow のバージョンを 3.0.0 に更新しました。[GitHub#1265][Takashi Hashidaさんがパッチ提供]

### 修正

- テーブル作成時に無効なオプションを持つトークナイザーを指定するとメモリーリークする問題を修正しました。

- Hashテーブルに新しいエントリーを追加できなくなることがある問題を修正しました。

  このバグは、Groonga 11.0.6 でのみ発生し、頻繁にデータを追加、削除すると発生することがあります。もし、このバグが発生した場合は、以下の手順を実行することで、この問題を解決できます。

  1. Groongaを11.0.6から11.0.7以降にアップグレードします。

  2. 元のテーブルと同じスキーマを持つ新しいテーブルを作ります。

  3. 元のテーブルから新しいテーブルにデータをコピーします。

- [Windows] メモリー不足によって、新しくファイルのオープンに失敗した時にリソースリークする問題を修正しました。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

### 感謝

- Takashi Hashidaさん

## 11.0.6リリース - 2021-08-29

> 警告

> Groonga 11.0.6 には、Hashテーブルに新しいエントリーを追加できなくなることがある問題があります。

> この問題は、Groonga 11.0.7で修正しました。この問題は、Groonga 11.0.6でのみ発生します。したがって、Groonga 11.0.6をお使いの方は、Groonga 11.0.7以降を使うことを強くおすすめします。

### 改良

- クラッシュ時に自動でリカバリーする機能を追加しました。(実験的な機能です。)

  この機能は実験的な機能です。現状、この機能はまだ安定していません。

  この機能によって、Groongaがクラッシュした時、Groongaはクラッシュした後、最初のデータベースオープン時に自動的にデータベースをリカバリーします。ただ、全てのクラッシュケースで自動的にデータベースをリカバリーできるわけではありません。クラッシュのタイミングによっては、手動でデータベースのリカバリーが必要になります。

  この機能が有効な時、GroongaはWAL(ログ先行書き込み)を実行します。以下のツールを使うことでWALをダンプできますが、現状これらをユーザーが使う必要はありません。

  > - [grndb] `dump-wal` コマンド

  > - `dump-wal.rb` スクリプト

- [cache_limit] `cache_limit 0` を実行したときにキャッシュを削除するようにしました。[GitHub#1224][higchiさんからの報告]

  Groongaは内部的なテーブルにクエリーキャッシュを保存しています。このテーブルは、ハッシュテーブルなので、トータルのキーサイズは最大で4GiBです。そのため、もし多くの巨大なクエリーを実行した場合、トータルのキーサイズの最大値4GiBを超え、Groongaがクエリーをキャッシュできない可能性があります。このようなケースで、 `cache_limit 0` を実行することで、クエリーキャッシュ用のテーブルをクリアーし、Groongaがクエリーキャッシュを保存できるようにします。

### 修正

- 同時期に複数のスレッドで同じオブジェクトをオープンした時に、Groongaがロックを解除しない問題を修正しました。

  同時期に複数のスレッドが同じオブジェクトをオープンすると、最初にオブジェクトを開いたスレッド以外のスレッドは対象のオブジェクトが開かれるまで待ちます。この時、対象のオブジェクトが開かれるのを待っているスレッドもロックを取得しますが、これらのロックは解放されません。したがって、Groongaのプロセスを再起動するまでこれらのロックが残り、その新しいスレッドは、Groongaが再起動するまで、そのオブジェクトを開けません。

  ただ、スレッドがオブジェクトを開く時間は非常に短いため、このバグは滅多に起こりません。

- [query_parallel_or] `query()` と結果が異なることがある問題を修正しました。

  例えば、 `query("tags || tags2", "beginner man")` とした場合は、以下のレコードはマッチしますが、`query_parallel_or("tags || tags2", "beginner man")` とした場合は、今までは、以下のレコードはマッチしませんでした。

  - `{"_key": "Bob", "comment": "Hey!", "tags": ["expert", "man"], "tags2": ["beginner"]}`

  今回の変更によって、 `query_parallel_or("tags || tags2", "beginner man")` を使った場合であっても、上記のレコードがマッチするようになりました。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- `GRN_II_CURSOR_SET_MIN_ENABLE` が原因でマッチするはずのレコードを返さないことがあります。

### 感謝

- higchiさん

## 11.0.5リリース - 2021-07-29

### 改良

- [ノーマライザー] ノーマライザーを複数指定できるようになりました。

  テーブル作成時に `--normalizers` オプションを使うことで、複数のノーマライザーを指定できます。互換性のため、既存の `--normalizer` を使っても複数のノーマライザーを指定できます。

  Groonga 11.0.4 でノーマライザーをカスタマイズするための `NormalizerTable` を追加しました。この `NormalizerTable` と既存のノーマライザーを組み合わせることでより柔軟な制御ができます。

  例えば、この機能は、以下のようなケースで有用です。

  > - 電話番号を検索します。ただし、データはOCRで手書きのデータを取り込みます。OCRは数字と文字列をご認識することがあります。(例えば、 5とS など)

  具体的には以下の通りです。

      table_create Normalizations TABLE_PAT_KEY ShortText
      column_create Normalizations normalized COLUMN_SCALAR ShortText
      load --table Normalizations
      [
      {"_key": "s", "normalized": "5"}
      ]

      table_create Tels TABLE_NO_KEY
      column_create Tels tel COLUMN_SCALAR ShortText

      table_create TelsIndex TABLE_PAT_KEY ShortText \
        --normalizers 'NormalizerNFKC130("unify_hyphen_and_prolonged_sound_mark", true), \
                       NormalizerTable("column", "Normalizations.normalized")' \
        --default_tokenizer 'TokenNgram("loose_symbol", true, "loose_blank", true)'
      column_create TelsIndex tel_index COLUMN_INDEX|WITH_SECTION Tels tel

      load --table Tels
      [
      {"tel": "03-4S-1234"}
      {"tel": "03-45-9876"}
      ]

      select --table Tels \
        --filter 'tel @ "03-45-1234"'
      [
        [
          0,
          1625227424.560146,
          0.0001730918884277344
        ],
        [
          [
            [
              1
            ],
            [
              [
                "_id",
                "UInt32"
              ],
              [
                "tel",
                "ShortText"
              ]
            ],
            [
              1,
              "03-4S-1234"
            ]
          ]
        ]
      ]

  既存のノーマライザーでは、このようなケースには対応できませんでしたが、このリリースから既存のノーマライザーと `NormalizerTable` を組み合わせることで、このようなケースにも対応できます。

- [query_parallel_or][query] シーケンシャルサーチのしきい値をカスタマイズできるようにしました。

  以下のオプションを使うことで、シーケンシャルサーチを使うかどうかのしきい値をクエリーごとにカスタマイズできます。

  > - `{"max_n_enough_filtered_records": xx}`

  >   `max_n_enough_filtered_records` は、レコード数を指定します。 `query` または、 `query_parallel_or` で、この値以下までレコードが絞り込めそうな場合、シーケンシャルサーチを使います。

  > - `{"enough_filtered_ratio": x.x}`

  >   `enough_filtered_ratio` は、全体に占める割合を指定します。 `query` または、 `query_parallel_or` で、この割合以下までレコードが絞り込めそうな場合、シーケンシャルサーチを使います。例えば、`{"enough_filtered_ratio": 0.5}` とした場合、 `query` または `query_parallel_or` で全体の半分まで絞り込めそうな場合はシーケンシャルサーチを使います。

  具体的には以下の通りです。

  >     table_create Products TABLE_NO_KEY
  >     column_create Products name COLUMN_SCALAR ShortText

  >     table_create Terms TABLE_PAT_KEY ShortText --normalizer NormalizerAuto
  >     column_create Terms products_name COLUMN_INDEX Products name

  >     load --table Products
  >     [
  >     ["name"],
  >     ["Groonga"],
  >     ["Mroonga"],
  >     ["Rroonga"],
  >     ["PGroonga"],
  >     ["Ruby"],
  >     ["PostgreSQL"]
  >     ]

  >     select \
  >       --table Products \
  >       --filter 'query("name", "r name:Ruby", {"enough_filtered_ratio": 0.5})'

  >     table_create Products TABLE_NO_KEY
  >     column_create Products name COLUMN_SCALAR ShortText

  >     table_create Terms TABLE_PAT_KEY ShortText --normalizer NormalizerAuto
  >     column_create Terms products_name COLUMN_INDEX Products name

  >     load --table Products
  >     [
  >     ["name"],
  >     ["Groonga"],
  >     ["Mroonga"],
  >     ["Rroonga"],
  >     ["PGroonga"],
  >     ["Ruby"],
  >     ["PostgreSQL"]
  >     ]

  >     select \
  >       --table Products \
  >       --filter 'query("name", "r name:Ruby", {"max_n_enough_filtered_records": 10})'

- [between][in_values] シーケンシャルサーチのしきい値をカスタマイズできるようにしました。

  [between] と [in_values] は、検索対象のレコードが十分に絞り込まれている時に、シーケンシャルサーチに切り替える機能があります。

  `GRN_IN_VALUES_TOO_MANY_INDEX_MATCH_RATIO` / `GRN_BETWEEN_TOO_MANY_INDEX_MATCH_RATIO` の値は、Groongaがシーケンシャルサーチを実行するかインデックスを使った検索をするかのしきい値として使われます。

  今までは、以下の環境変数でのみカスタマイズ可能でした。

  `in_values()`:

      # Don't use auto sequential search
      GRN_IN_VALUES_TOO_MANY_INDEX_MATCH_RATIO=-1
      # Set threshold to 0.02
      GRN_IN_VALUES_TOO_MANY_INDEX_MATCH_RATIO=0.02

  `between()`:

      # Don't use auto sequential search
      GRN_BETWEEN_TOO_MANY_INDEX_MATCH_RATIO=-1
      # Set threshold to 0.02
      GRN_BETWEEN_TOO_MANY_INDEX_MATCH_RATIO=0.02

  環境変数によるカスタマイズは、すべてのクエリーに対して適用されますが、この機能を使うことで、クエリーごとにしきい値を指定できます。

  具体的には以下の通りです。 `{"too_many_index_match_ratio": x.xx}` オプションでしきい値を指定できます。このオプションの値の型は `double` 型です。

      table_create Memos TABLE_HASH_KEY ShortText
      column_create Memos timestamp COLUMN_SCALAR Time

      table_create Times TABLE_PAT_KEY Time
      column_create Times memos_timestamp COLUMN_INDEX Memos timestamp

      load --table Memos
      [
      {"_key": "001", "timestamp": "2014-11-10 07:25:23"},
      {"_key": "002", "timestamp": "2014-11-10 07:25:24"},
      {"_key": "003", "timestamp": "2014-11-10 07:25:25"},
      {"_key": "004", "timestamp": "2014-11-10 07:25:26"},
      {"_key": "005", "timestamp": "2014-11-10 07:25:27"},
      {"_key": "006", "timestamp": "2014-11-10 07:25:28"},
      {"_key": "007", "timestamp": "2014-11-10 07:25:29"},
      {"_key": "008", "timestamp": "2014-11-10 07:25:30"},
      {"_key": "009", "timestamp": "2014-11-10 07:25:31"},
      {"_key": "010", "timestamp": "2014-11-10 07:25:32"},
      {"_key": "011", "timestamp": "2014-11-10 07:25:33"},
      {"_key": "012", "timestamp": "2014-11-10 07:25:34"},
      {"_key": "013", "timestamp": "2014-11-10 07:25:35"},
      {"_key": "014", "timestamp": "2014-11-10 07:25:36"},
      {"_key": "015", "timestamp": "2014-11-10 07:25:37"},
      {"_key": "016", "timestamp": "2014-11-10 07:25:38"},
      {"_key": "017", "timestamp": "2014-11-10 07:25:39"},
      {"_key": "018", "timestamp": "2014-11-10 07:25:40"},
      {"_key": "019", "timestamp": "2014-11-10 07:25:41"},
      {"_key": "020", "timestamp": "2014-11-10 07:25:42"},
      {"_key": "021", "timestamp": "2014-11-10 07:25:43"},
      {"_key": "022", "timestamp": "2014-11-10 07:25:44"},
      {"_key": "023", "timestamp": "2014-11-10 07:25:45"},
      {"_key": "024", "timestamp": "2014-11-10 07:25:46"},
      {"_key": "025", "timestamp": "2014-11-10 07:25:47"},
      {"_key": "026", "timestamp": "2014-11-10 07:25:48"},
      {"_key": "027", "timestamp": "2014-11-10 07:25:49"},
      {"_key": "028", "timestamp": "2014-11-10 07:25:50"},
      {"_key": "029", "timestamp": "2014-11-10 07:25:51"},
      {"_key": "030", "timestamp": "2014-11-10 07:25:52"},
      {"_key": "031", "timestamp": "2014-11-10 07:25:53"},
      {"_key": "032", "timestamp": "2014-11-10 07:25:54"},
      {"_key": "033", "timestamp": "2014-11-10 07:25:55"},
      {"_key": "034", "timestamp": "2014-11-10 07:25:56"},
      {"_key": "035", "timestamp": "2014-11-10 07:25:57"},
      {"_key": "036", "timestamp": "2014-11-10 07:25:58"},
      {"_key": "037", "timestamp": "2014-11-10 07:25:59"},
      {"_key": "038", "timestamp": "2014-11-10 07:26:00"},
      {"_key": "039", "timestamp": "2014-11-10 07:26:01"},
      {"_key": "040", "timestamp": "2014-11-10 07:26:02"},
      {"_key": "041", "timestamp": "2014-11-10 07:26:03"},
      {"_key": "042", "timestamp": "2014-11-10 07:26:04"},
      {"_key": "043", "timestamp": "2014-11-10 07:26:05"},
      {"_key": "044", "timestamp": "2014-11-10 07:26:06"},
      {"_key": "045", "timestamp": "2014-11-10 07:26:07"},
      {"_key": "046", "timestamp": "2014-11-10 07:26:08"},
      {"_key": "047", "timestamp": "2014-11-10 07:26:09"},
      {"_key": "048", "timestamp": "2014-11-10 07:26:10"},
      {"_key": "049", "timestamp": "2014-11-10 07:26:11"},
      {"_key": "050", "timestamp": "2014-11-10 07:26:12"}
      ]

      select Memos \
        --filter '_key == "003" && \
                  between(timestamp, \
                          "2014-11-10 07:25:24", \
                          "include", \
                          "2014-11-10 07:27:26", \
                          "exclude", \
                          {"too_many_index_match_ratio": 0.03})'

      table_create Tags TABLE_HASH_KEY ShortText

      table_create Memos TABLE_HASH_KEY ShortText
      column_create Memos tag COLUMN_SCALAR Tags

      load --table Memos
      [
      {"_key": "Rroonga is fast!", "tag": "Rroonga"},
      {"_key": "Groonga is fast!", "tag": "Groonga"},
      {"_key": "Mroonga is fast!", "tag": "Mroonga"},
      {"_key": "Groonga sticker!", "tag": "Groonga"},
      {"_key": "Groonga is good!", "tag": "Groonga"}
      ]

      column_create Tags memos_tag COLUMN_INDEX Memos tag

      select \
        Memos \
        --filter '_id >= 3 && \
                  in_values(tag, \
                           "Groonga", \
                           {"too_many_index_match_ratio": 0.7})' \
        --output_columns _id,_score,_key,tag

- [between] `GRN_EXPR_OPTIMIZE=yes` をサポートしました。

  `between()` で、条件式の評価順序の最適化をサポートしました。

- [query_parallel_or][query] match_columns を vector で指定できるようにしました。[GitHub#1238][naoaさんがパッチ提供]

  以下のように、 `query` と `query_parallel_or` の `match_columns` に vector を使えます。

      table_create Users TABLE_NO_KEY
      column_create Users name COLUMN_SCALAR ShortText
      column_create Users memo COLUMN_SCALAR ShortText
      column_create Users tag COLUMN_SCALAR ShortText

      table_create Terms TABLE_PAT_KEY ShortText \
        --default_tokenizer TokenNgram \
        --normalizer NormalizerNFKC130
      column_create Terms name COLUMN_INDEX|WITH_POSITION Users name
      column_create Terms memo COLUMN_INDEX|WITH_POSITION Users memo
      column_create Terms tag COLUMN_INDEX|WITH_POSITION Users tag

      load --table Users
      [
      {"name": "Alice", "memo": "Groonga user", "tag": "Groonga"},
      {"name": "Bob",   "memo": "Rroonga user", "tag": "Rroonga"}
      ]

      select Users \
        --output_columns _score,name \
        --filter 'query(["name * 100", "memo", "tag * 10"], \
                        "Alice OR Groonga")'

- [select] 前方一致検索でセクションと重みをサポートしました。[GitHub#1240][naoaさんがパッチ提供]

  前方一致検索でマルチカラムインデックスと、スコアーの調整ができます。

      table_create Memos TABLE_NO_KEY
      column_create Memos title COLUMN_SCALAR ShortText
      column_create Memos tags COLUMN_VECTOR ShortText

      table_create Terms TABLE_PAT_KEY ShortText
      column_create Terms index COLUMN_INDEX|WITH_SECTION Memos title,tags

      load --table Memos
      [
      {"title": "Groonga", "tags": ["Groonga"]},
      {"title": "Rroonga", "tags": ["Groonga", "Rroonga", "Ruby"]},
      {"title": "Mroonga", "tags": ["Groonga", "Mroonga", "MySQL"]}
      ]

      select Memos \
        --match_columns "Terms.index.title * 2" \
        --query 'G*' \
        --output_columns title,tags,_score
      [
        [
          0,
          0.0,
          0.0
        ],
        [
          [
            [
              1
            ],
            [
              [
                "title",
                "ShortText"
              ],
              [
                "tags",
                "ShortText"
              ],
              [
                "_score",
                "Int32"
              ]
            ],
            [
              "Groonga",
              [
                "Groonga"
              ],
              2
            ]
          ]
        ]
      ]

- [grndb] `grndb recover` で使用したオブジェクトを即時閉じるようにしました。

  これにより、メモリ消費を抑制できます。おそらくパフォーマンスは低下していますが、許容可能な低下です。

  `grndb check` はまだ、使用したオブジェクトを即時閉じないので注意してください。

- [query_parallel_or][query] 以下のように、 `match_columns` に `scorer_tf_idf` を指定できるようにしました。

      table_create Tags TABLE_HASH_KEY ShortText

      table_create Users TABLE_HASH_KEY ShortText
      column_create Users tags COLUMN_VECTOR Tags

      load --table Users
      [
      {"_key": "Alice",
       "tags": ["beginner", "active"]},
      {"_key": "Bob",
       "tags": ["expert", "passive"]},
      {"_key": "Chris",
       "tags": ["beginner", "passive"]}
      ]

      column_create Tags users COLUMN_INDEX Users tags

      select Users \
        --output_columns _key,_score \
        --sort_keys _id \
        --command_version 3 \
        --filter 'query_parallel_or("scorer_tf_idf(tags)", \
                                    "beginner active")'
      {
        "header": {
          "return_code": 0,
          "start_time": 0.0,
          "elapsed_time": 0.0
        },
        "body": {
          "n_hits": 1,
          "columns": [
            {
              "name": "_key",
              "type": "ShortText"
            },
            {
              "name": "_score",
              "type": "Float"
            }
          ],
          "records": [
            [
              "Alice",
              2.098612308502197
            ]
          ]
        }
      }

- [query_expand] 展開後の語の重みを操作できるようにしました。

  展開後の語に対して、重みを指定できます。

  スコアーを増やしたい場合は、 `>` を使います。スコアーを減らしたい場合は、 `<` を指定します。

  数字でスコアーの量を指定できます。負の数も指定できます。

      table_create TermExpansions TABLE_NO_KEY
      column_create TermExpansions term COLUMN_SCALAR ShortText
      column_create TermExpansions expansions COLUMN_VECTOR ShortText

      load --table TermExpansions
      [
      {"term": "Rroonga", "expansions": ["Rroonga", "Ruby Groonga"]}
      ]

      query_expand TermExpansions "Groonga <-0.2Rroonga Mroonga" \
        --term_column term \
        --expanded_term_column expansions
      [[0,0.0,0.0],"Groonga <-0.2((Rroonga) OR (Ruby Groonga)) Mroonga"]

- [httpd] バンドルしているnginxのバージョンを1.21.1に更新しました。

- バンドルしているApache Arrowを5.0.0に更新しました。

- [Ubuntu] Ubuntu 20.10 (Groovy Gorilla)サポートをやめました。

  - Ubuntu 20.10 は、2021年7月22日でEOLとなったためです。

### 修正

- [query_parallel_or][query] `query_options` とその他のオプションを指定すると、その他のオプションが無視される問題を修正しました。

  例えば、以下のケースでは、 `"default_operator": "OR"` オプションは無視されていました。

      plugin_register token_filters/stop_word

      table_create Memos TABLE_NO_KEY
      column_create Memos content COLUMN_SCALAR ShortText

      table_create Terms TABLE_PAT_KEY ShortText \
        --default_tokenizer TokenBigram \
        --normalizer NormalizerAuto \
        --token_filters TokenFilterStopWord
      column_create Terms memos_content COLUMN_INDEX|WITH_POSITION Memos content
      column_create Terms is_stop_word COLUMN_SCALAR Bool

      load --table Terms
      [
      {"_key": "and", "is_stop_word": true}
      ]

      load --table Memos
      [
      {"content": "Hello"},
      {"content": "Hello and Good-bye"},
      {"content": "and"},
      {"content": "Good-bye"}
      ]

      select Memos \
        --filter 'query_parallel_or( \
                    "content", \
                    "Hello and", \
                    {"default_operator": "OR", \
                     "options": {"TokenFilterStopWord.enable": false}})' \
        --match_escalation_threshold -1 \
        --sort_keys -_score
      [
        [
          0,
          0.0,
          0.0
        ],
        [
          [
            [
              1
            ],
            [
              [
                "_id",
                "UInt32"
              ],
              [
                "content",
                "ShortText"
              ]
            ],
            [
              2,
              "Hello and Good-bye"
            ]
          ]
        ]
      ]

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

- 多くのデータを削除し、同じデータを再度loadすることを繰り返すと、Groongaがヒットすべきレコードを返さないことがあります。

### 感謝

- naoaさん

## 11.0.4リリース - 2021-06-29

### 改良

- [Normalizer] カスタマイズされたノーマライザーを使えるようになりました。

  この機能を使うためにノーマライズ用のテーブルを定義し、このテーブルを使ってノーマライズします。つまり、カスタマイズされたノーマライザーを使うことができます。

  例えば、以下の例では、 "S" を "5" に正規化するよう定義しています。`Substitutions` テーブルが正規化用のテーブルです。

  >     table_create Substitutions TABLE_PAT_KEY ShortText
  >     column_create Substitutions substituted COLUMN_SCALAR ShortText
  >     load --table Substitutions
  >     [
  >     {"_key": "S", "substituted": "5"}
  >     ]

  >     table_create TelLists TABLE_NO_KEY
  >     column_create TelLists tel COLUMN_SCALAR ShortText

  >     table_create Terms TABLE_HASH_KEY ShortText \
  >       --default_tokenizer TokenNgram \
  >       --normalizer 'NormalizerTable("column", "Substitutions.substituted", \
  >                                     "report_source_offset", true)'
  >     column_create Terms tel_index COLUMN_INDEX|WITH_POSITION TelLists tel

  >     load --table TelLists
  >     [
  >     {"tel": "03-4S-1234"}
  >     ]

  >     select TelLists --filter 'tel @ "03-45-1234"'
  >     [
  >       [
  >         0,
  >         1624686303.538532,
  >         0.001319169998168945
  >       ],
  >       [
  >         [
  >           [
  >             1
  >           ],
  >           [
  >             [
  >               "_id",
  >               "UInt32"
  >             ],
  >             [
  >               "tel",
  >               "ShortText"
  >             ]
  >           ],
  >           [
  >             1,
  >             "03-4S-1234"
  >           ]
  >         ]
  >       ]
  >     ]

  例えば、手書きの文字を取り込む際に誤認識されやすい語を定義できます。そうすることで、誤ったデータを正規化して正しいデータにできます。

  この正規化用のテーブルを更新した場合は、インデックスの再構築が必要なことに注意してください。

- 新しいコマンド `object_warm` を追加しました。

  このコマンドは、GroongaのDBをOSのページキャッシュに乗せます。

  OS起動後、Groongaを一度も起動していない場合、GroongaのDBはOSのページキャッシュ上に存在しません。そのため、Groongaに対する最初の操作は遅くなります。

  予めこのコマンドを実行した場合、Groongaへの最初の操作は速くなります。Linuxでは、 `cat *.db > dev/null` で同等のことができますが、Windowsでは、いままで、同様のことはできませんでした。

  このコマンドを使うことで、LinuxでもWindowsでもGroongaのDBをOSのページキャッシュへ乗せることができます。また、テーブル、カラム、インデックス単位でも同様のことができます。したがって、よく使うテーブルやカラム、インデックスのみをOSのページキャッシュに乗せることができます。

  このコマンドは、以下のように様々な対象に対して実行できます。

  > - `object_warm --name index_name` と指定した場合は、指定したインデックスがOSのページキャッシュへ転送されます。

  > - `object_warm --name column_name` と指定した場合は、指定したカラムがOSののページキャッシュへ転送されます。

  > - `object_warm --name table_name` と指定した場合は、指定したテーブルをOSのページキャッシュへ転送します。

  > - `object_warm` と指定した場合は、GroongaのDB全体がOSのページキャッシュへ転送されます。

  ただ、メモリーに空きスペースが無い場合は、このコマンドは効果がないことに注意してください。

- [select] `--filter` 内で特定のレコードのスコアーを調整できるようにしました。

  `*~` という演算子を使うことで、特定のレコードのスコアーを調整できます。`*~` は `&&` や `||` と同じ論理演算子です。したがって、 `&&` や `||` と同じように使えます。`*~` のデフォルトの重みは -1 です。

  したがって、例えば、 `'content @ "Groonga" *~ content @ "Mroonga"'` は以下の操作を意味します。

  > 1. `'content @ "Groonga"` と `content @ "MySQL"` にマッチしたレコードを抽出します。

  > 2. 以下のようにスコアーを追加します。

  >    1. `'content @ "Groonga"` のスコアーを計算します。

  >    2. `content @ "Mroonga"'` のスコアーを計算します。

  >    3. bのスコアーを -1 倍します。

  >    4. このレコードのスコアーは a + b です。したがって、 aのスコアー 1 で bのスコアーが1 の場合、このレコードのスコアーは 1 + (1 * -1) = 0 です。

  また、 `*~${score_quantity}` とすることで、スコアーの量を指定できます。

  具体的には、 以下の条件( `'content @ "Groonga" *~2.5 content @ "MySQL"'` ) にマッチしたレコードのスコアーを調整します。

  >     table_create Memos TABLE_NO_KEY
  >     column_create Memos content COLUMN_SCALAR ShortText

  >     table_create Terms TABLE_PAT_KEY ShortText \
  >       --default_tokenizer TokenBigram \
  >       --normalizer NormalizerAuto
  >     column_create Terms index COLUMN_INDEX|WITH_POSITION Memos content

  >     load --table Memos
  >     [
  >     {"content": "Groonga is a full text search engine."},
  >     {"content": "Rroonga is the Ruby bindings of Groonga."},
  >     {"content": "Mroonga is a MySQL storage engine based of Groonga."}
  >     ]

  >     select Memos \
  >       --command_version 3 \
  >       --filter 'content @ "Groonga" *~2.5 content @ "Mroonga"' \
  >       --output_columns 'content, _score' \
  >       --sort_keys -_score,_id
  >     {
  >       "header": {
  >         "return_code": 0,
  >         "start_time": 1624605205.641078,
  >         "elapsed_time": 0.002965450286865234
  >       },
  >       "body": {
  >         "n_hits": 3,
  >         "columns": [
  >           {
  >             "name": "content",
  >             "type": "ShortText"
  >           },
  >           {
  >             "name": "_score",
  >             "type": "Float"
  >           }
  >         ],
  >         "records": [
  >           [
  >             "Groonga is a full text search engine.",
  >             1.0
  >           ],
  >           [
  >             "Rroonga is the Ruby bindings of Groonga.",
  >             1.0
  >           ],
  >           [
  >             "Mroonga is a MySQL storage engine based of Groonga.",
  >             -1.5
  >           ]
  >         ]
  >       }
  >     }

  adjuster を使っても同様のことができます。adjuster を使う場合、アプリケーション上で、 `--filter` 条件と `--adjuster` 条件を作る必要がありますが、この改良で、 `--filter` 条件のみを作成すればよくなりました。

  以下のように、 `query()` を使って、filter条件を記述することもできます。

  > - `--filter 'content @ "Groonga" *~2.5 content @ "Mroonga"'`

- [select] 重み付き `&&` をサポートしました。

  `*<` と `*>` を使うことで、重み付きの `&&` を使えます。`*<` のデフォルトの重みは 0.5です。 `*>` のデフォルトの重みは 2.0 です。

  `*<${score_quantity}` や `*>${score_quantity}` とすることで、スコアーの量を指定できます。また、 `*<${score_quantity}` と指定した場合は、 ${score_quantity} の正負の符号が反転します。

  例えば、 `'content @ "Groonga" *<2.5 query("content", "MySQL")'` は以下の操作を意味します。

  > 1. `'content @ "Groonga"` と `content @ "MySQL"` にマッチしたレコードを抽出します。

  > 2. 以下のようにスコアーを追加します。

  >    1. `'content @ "Groonga"` のスコアーを計算します。

  >    2. `query("content", "MySQL")` のスコアーを計算します。

  >    3. bのスコアーは、 `*<` によって -2.5倍されます。

  >    4. このレコードのスコアーは a + b です。したがって、 aのスコアー 1 で bのスコアーが1 の場合、このレコードのスコアーは 1 + (1 * -2.5) = −1.5 です。

  具体的には、 以下の条件( `'content @ "Groonga" *~2.5 query("content", "MySQL")'` にマッチしたレコードのスコアーを調整します。

  >     table_create Memos TABLE_NO_KEY
  >     column_create Memos content COLUMN_SCALAR ShortText

  >     table_create Terms TABLE_PAT_KEY ShortText \
  >       --default_tokenizer TokenBigram \
  >       --normalizer NormalizerAuto
  >     column_create Terms index COLUMN_INDEX|WITH_POSITION Memos content

  >     load --table Memos
  >     [
  >     {"content": "Groonga is a full text search engine."},
  >     {"content": "Rroonga is the Ruby bindings of Groonga."},
  >     {"content": "Mroonga is a MySQL storage engine based of Groonga."}
  >     ]

  >     select Memos \
  >       --command_version 3 \
  >       --filter 'content @ "Groonga" *<2.5 query("content", "Mroonga")' \
  >       --output_columns 'content, _score' \
  >       --sort_keys -_score,_id
  >     {
  >       "header": {
  >         "return_code": 0,
  >         "start_time": 1624605205.641078,
  >         "elapsed_time": 0.002965450286865234
  >       },
  >       "body": {
  >         "n_hits": 3,
  >         "columns": [
  >           {
  >             "name": "content",
  >             "type": "ShortText"
  >           },
  >           {
  >             "name": "_score",
  >             "type": "Float"
  >           }
  >         ],
  >         "records": [
  >           [
  >             "Groonga is a full text search engine.",
  >             1.0
  >           ],
  >           [
  >             "Rroonga is the Ruby bindings of Groonga.",
  >             1.0
  >           ],
  >           [
  >             "Mroonga is a MySQL storage engine based of Groonga.",
  >             -1.5
  >           ]
  >         ]
  >       }
  >     }

- [ログ] 標準出力、標準エラー出力への出力をサポートしました。

  [プロセスログ] と [クエリーログ] が標準出力と標準エラー出力への出力をサポートしました。

  > - `--log-path -` や `--query-log-path -` と指定した場合、Groongaはログを標準出力に出力します。

  > - `--log-path +` や `--query-log-path +` と指定した場合、Groongaはログを標準エラー出力に出力します。

  [プロセスログ] はGroongaの動作全てに関することのログです。 [クエリーログ] は、クエリー処理に関することだけのログです。

  この機能はDocker上でGroongaを実行する際に有用です。Dockerは標準で標準出力と標準エラー出力を記録する機能を持っています。したがって、Groongaのログを取得するために、Dockerの環境にログインする必要がなくなります。

  例えば、この機能は、以下のようなケースで有用です。

  > - Docker上のGroongaのスロークエリーを解析したい場合。

  >   Groonga起動時に `--query-log-path -` と指定している場合、以下のコマンドを実行するだけでスロークエリーを解析できます。

  >   > - `docker logs ${container_name} | groonga-query-log-analyze`

  このように、 Groonga上のDockerから出力したクエリーログを使って、簡単にスロークエリの解析ができます。

- [ドキュメント] `string_substring` に不足している内容を追加しました。[Github#1209][Takashi Hashidaさんがパッチ提供]

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

- `*<` と `*>` は、filter条件の右辺に `query()` を使う時のみ有効です。もし、以下のように指定した場合、 `*<` と `*>` は `&&` として機能します。

  > - `'content @ "Groonga" *< content @ "Mroonga"'`

### 感謝

- Takashi Hashidaさん

## 11.0.3リリース - 2021-05-29

### 改良

- [query] クエリー毎に `TokenFilterStem` を無効にできるようになりました。

  - `TokenFilterStem` は語幹を使った検索ができます。例えば `develop` も `developing` も `developed` も `develops` も、すべてステミングすると `develop` になります。そのため、 `develops` というクエリーで `develop` も `developing` も `developed` も検索できます。

  - 今回のリリースでは、以下のように特定のクエリーでのみ `TokenFilterStem` を無効にして検索できるようになりました。

        plugin_register token_filters/stem

        table_create Memos TABLE_NO_KEY
        column_create Memos content COLUMN_SCALAR ShortText

        table_create Terms TABLE_PAT_KEY ShortText \
          --default_tokenizer TokenBigram \
          --normalizer NormalizerAuto \
          --token_filters 'TokenFilterStem("keep_original", true)'
        column_create Terms memos_content COLUMN_INDEX|WITH_POSITION Memos content

        load --table Memos
        [
        {"content": "I develop Groonga"},
        {"content": "I'm developing Groonga"},
        {"content": "I developed Groonga"}
        ]

        select Memos \
          --match_columns content \
          --query '"developed groonga"' \
          --query_options '{"TokenFilterStem.enable": false}'
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                1
              ],
              [
                [
                  "_id",
                  "UInt32"
                ],
                [
                  "content",
                  "ShortText"
                ]
              ],
              [
                3,
                "I developed Groonga"
              ]
            ]
          ]
        ]

  - この機能は、通常は語幹を使った検索がしたいが、時々、以下のように(ステミングされていない)正確な語で検索したいときに有用です。

    - ステミングされた語を使った検索だとGroongaが大量の検索結果を返す場合。

    - `TokenFilerStem` が間違ったステミング結果を返す場合。

    - (ステミングされていない)正確な語を含むレコードのみを検索したい場合。

- [query] クエリー毎に `TokenFilterStopWord` を無効にできるようになりました。

  - `TokenFilterStopWord` は、予め登録したストップワードを検索から除外します。これは、 `and` や `is` などの頻出語を無視することで検索ノイズを減らすために使います。

  - ただ、ときどき、特定のクエリーでのみこれらの語を含めて検索したいときがあります。今回のリリースでは、以下のように特定のクエリーでのみ `TokenFilterStopWord` を無効にして検索できるようになりました。

        plugin_register token_filters/stop_word

        table_create Memos TABLE_NO_KEY
        column_create Memos content COLUMN_SCALAR ShortText

        table_create Terms TABLE_PAT_KEY ShortText \
          --default_tokenizer TokenBigram \
          --normalizer NormalizerAuto \
          --token_filters TokenFilterStopWord
        column_create Terms memos_content COLUMN_INDEX|WITH_POSITION Memos content
        column_create Terms is_stop_word COLUMN_SCALAR Bool

        load --table Terms
        [
        {"_key": "and", "is_stop_word": true}
        ]

        load --table Memos
        [
        {"content": "Hello"},
        {"content": "Hello and Good-bye"},
        {"content": "Good-bye"}
        ]

        select Memos \
          --match_columns content \
          --query "Hello and" \
          --query_options '{"TokenFilterStopWord.enable": false}' \
          --match_escalation_threshold -1 \
          --sort_keys -_score
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                1
              ],
              [
                [
                  "_id",
                  "UInt32"
                ],
                [
                  "content",
                  "ShortText"
                ]
              ],
              [
                2,
                "Hello and Good-bye"
              ]
            ]
          ]
        ]

  - 上記の例では、 `--query-options` を使って、 `TokenFilterStopWord.enable` を指定していますが、以下のように `{"options": {"TokenFilterStopWord.enable": false}}` を使っても指定できます。

        plugin_register token_filters/stop_word

        table_create Memos TABLE_NO_KEY
        column_create Memos content COLUMN_SCALAR ShortText

        table_create Terms TABLE_PAT_KEY ShortText \
          --default_tokenizer TokenBigram \
          --normalizer NormalizerAuto \
          --token_filters TokenFilterStopWord
        column_create Terms memos_content COLUMN_INDEX|WITH_POSITION Memos content
        column_create Terms is_stop_word COLUMN_SCALAR Bool

        load --table Terms
        [
        {"_key": "and", "is_stop_word": true}
        ]

        load --table Memos
        [
        {"content": "Hello"},
        {"content": "Hello and Good-bye"},
        {"content": "Good-bye"}
        ]

        select Memos \
          --filter 'query("content", \
                          "Hello and", \
                          {"options": {"TokenFilterStopWord.enable": false}})' \
          --match_escalation_threshold -1 \
          --sort_keys -_score
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                1
              ],
              [
                [
                  "_id",
                  "UInt32"
                ],
                [
                  "content",
                  "ShortText"
                ]
              ],
              [
                2,
                "Hello and Good-bye"
              ]
            ]
          ]
        ]

  - この機能は、よく使われる語を含めて検索しないとGroongaが正しい結果を返さない時に便利です。(例えば、曲名や店名等を検索する場合です。)

- [ノーマライザー][NormalizerNFKC] 新しいオプション `remove_new_line` を追加しました。

  - データを格納するテーブルのキーを正規化したい場合、そのテーブルにノーマライザーを設定しますが、通常ノーマライザーは改行を削除します。

  - ノーマライザーが改行を削除してしまうと、Groongaは改行のみのキーを扱えません。

  - このオプションを使うことで、改行のみのデータをキーとして登録できます。

- [string_slice] 新しい関数 `string_slice()` を追加しました。 [Github#1177][Takashi Hashidaさんがパッチ提供]

  - `string_slice()` は、文字列の部分文字列を抽出します。

  - この関数を使うには、 `functions/string` プラグインの登録が必要です。

  - 以下のように、引数によって２つの異なる抽出方法を使えます。

    - 位置を使った抽出:

          plugin_register functions/string
          table_create Memos TABLE_HASH_KEY ShortText

          load --table Memos
          [
            {"_key": "Groonga"}
          ]
          select Memos --output_columns '_key, string_slice(_key, 2, 3)'
          [
            [
              0,
              1337566253.89858,
              0.000355720520019531
            ],
            [
              [
                [
                  1
                ],
                [
                  [
                    "_key",
                    "ShortText"
                  ],
                  [
                    "string_slice",
                    null
                  ]
                ],
                [
                  "Groonga",
                  "oon"
                ]
              ]
            ]
          ]

    - 正規表現を使った抽出:

          plugin_register functions/string
          table_create Memos TABLE_HASH_KEY ShortText

          load --table Memos
          [
            {"_key": "Groonga"}
          ]
          select Memos --output_columns '_key, string_slice(_key, "(Gro+)(.*)", 2)'
          [
            [p
              0,
              1337566253.89858,
              0.000355720520019531
            ],
            [
              [
                [
                  1
                ],
                [
                  [
                    "_key",
                    "ShortText"
                  ],
                  [
                    "string_slice",
                    null
                  ]
                ],
                [
                  "Groonga",
                  "nga"
                ]
              ]
            ]
          ]

- [Ubuntu] Ubuntu 16.04 LTS (Xenial Xerus)のサポートをやめました。

- Visual Studio 用の EditorConfig を追加しました。[GitHub#1191][Takashi Hashidaさんがパッチ提供]

  - 多くの設定は、Visual Studio 専用です。

- [httpd] バンドルしているnginxのバージョンを1.20.1に更新しました。

  - CVE-2021-23017のセキュリティの修正が含まれています。

### 修正

- オプションをサポートしているトークナイザーやノーマライザー、トークンフィルターが使われている際、多くの検索クエリーを送信するとGroongaが応答を返さなくなることがある問題を修正しました。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。

### 感謝

- Takashi Hashidaさん

## 11.0.2リリース - 2021-05-10

### 改良

- [Documentation] `ruby_load` コマンドのリファレンスを削除しました。　[GitHub#1172][Anthony M. Cookさんがパッチ提供]

  - このコマンドはすでに削除されているためです。

- [Debian GNU/Linux] Debian 11(Bullseye) をサポートしました。

- [select] `--post_filter` をサポートしました。

  - 以下のように、 `filtered` ステージの動的カラムに対して `post_filter` を使ってフィルターできます。

        table_create Items TABLE_NO_KEY
        column_create Items price COLUMN_SCALAR UInt32

        load --table Items
        [
        {"price": 100},
        {"price": 150},
        {"price": 200},
        {"price": 250},
        {"price": 300}
        ]

        select Items \
          --filter "price >= 150" \
          --columns[price_with_tax].stage filtered \
          --columns[price_with_tax].type UInt32 \
          --columns[price_with_tax].flags COLUMN_SCALAR \
          --columns[price_with_tax].value "price * 1.1" \
          --post_filter "price_with_tax <= 250"
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                2
              ],
              [
                [
                  "_id",
                  "UInt32"
                ],
                [
                  "price_with_tax",
                  "UInt32"
                ],
                [
                  "price",
                  "UInt32"
                ]
              ],
              [
                2,
                165,
                150
              ],
              [
                3,
                220,
                200
              ]
            ]
          ]
        ]

- [select] `--slices[].post_filter` をサポートしました。

  - 以下のように、 `--slices[].filter` の条件でフィルターされた結果に対して `post_filter` を使えます。

        table_create Items TABLE_NO_KEY
        column_create Items price COLUMN_SCALAR UInt32

        load --table Items
        [
        {"price": 100},
        {"price": 200},
        {"price": 300},
        {"price": 1000},
        {"price": 2000},
        {"price": 3000}
        ]

        select Items \
          --slices[expensive].filter 'price >= 1000' \
          --slices[expensive].post_filter 'price < 3000'
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                6
              ],
              [
                [
                  "_id",
                  "UInt32"
                ],
                [
                  "price",
                  "UInt32"
                ]
              ],
              [
                1,
                100
              ],
              [
                2,
                200
              ],
              [
                3,
                300
              ],
              [
                4,
                1000
              ],
              [
                5,
                2000
              ],
              [
                6,
                3000
              ]
            ],
            {
              "expensive": [
                [
                  2
                ],
                [
                  [
                    "_id",
                    "UInt32"
                  ],
                  [
                    "price",
                    "UInt32"
                  ]
                ],
                [
                  4,
                  1000
                ],
                [
                  5,
                  2000
                ]
              ]
            }
          ]
        ]

- [select] `--sort_keys` 内に式を記述できるようになりました。

  - `--sort_keys` に式を記述できます。

    - 存在しないキーが sort_keys の式に含まれていた場合、それらは、無視され警告がログに出力されます。

  - これによって、例えば、 `--sort_keys` に `VECTOR_COLUMN` の要素を指定し、 `VECTOR_COLUMN` の要素で結果をソートできます。

  - 以前のバージョンでも動的カラムを使えば、 `VECTOR COLUMN` の要素で結果をソートできましたが、この機能によって、動的カラムを使わずに `VECTOR_COLUMN` の要素で結果をソートできます。

        table_create Values TABLE_NO_KEY
        column_create Values numbers COLUMN_VECTOR Int32
        load --table Values
        [
        {"numbers": [127, 128, 129]},
        {"numbers": [126, 255]},
        {"numbers": [128, -254]}
        ]
        select Values --sort_keys 'numbers[1]' --output_columns numbers
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                3
              ],
              [
                [
                  "numbers",
                  "Int32"
                ]
              ],
              [
                [
                  128,
                  -254
                ]
              ],
              [
                [
                  127,
                  128,
                  129
                ]
              ],
              [
                [
                  126,
                  255
                ]
              ]
            ]
          ]
        ]

- [トークンフィルター] オプションをつけた複数のトークンフィルターを使えるようにしました。

  - `--token_filters 'TokenFilterStopWord("column", "ignore"), TokenFilterNFKC130("unify_kana", true)'` のように複数のトークンフィルターを指定できます。 [Github#mroonga/mroonga#399][MASUDA Kazuhiroさんが報告]

- [query] 複雑な式で `result_set` ステージの動的カラムを扱えるようにしました。

  - 複雑な式とは、以下のように一時的な内部結果セットが必要な式です。

        '(true && query("name * 10", "ali", {"score_column": "ali_score"})) || \
         (true && query("name * 2", "li", {"score_column": "li_score"}))'

    - 上記の式では、 `true` の評価結果を格納するために一時的な結果セットを使用します。

    - したがって、例えば、以下の式では、式の中で `result_set` ステージの動的カラムの値を使えます。以下の式では、一時的な内部結果セットが必要ないためです。

          '(query("name * 10", "ali", {"score_column": "ali_score"})) || \
           (query("name * 2", "li", {"score_column": "li_score"}))'

  - 今回のリリースでは、以下のように `li_score` に値を設定できます。(以前のバージョンでは、2番めの式が動的カラムの値を取得出来なかったため、 `li_score` の値は `0` になっていました。)

        table_create Users TABLE_NO_KEY
        column_create Users name COLUMN_SCALAR ShortText

        table_create Lexicon TABLE_HASH_KEY ShortText \
          --default_tokenizer TokenBigramSplitSymbolAlphaDigit \
          --normalizer NormalizerAuto
        column_create Lexicon users_name COLUMN_INDEX|WITH_POSITION Users name

        load --table Users
        [
        {"name": "Alice"},
        {"name": "Alisa"},
        {"name": "Bob"}
        ]

        select Users \
          --columns[ali_score].stage result_set \
          --columns[ali_score].type Float \
          --columns[ali_score].flags COLUMN_SCALAR \
          --columns[li_score].stage result_set \
          --columns[li_score].type Float \
          --columns[li_score].flags COLUMN_SCALAR \
          --output_columns name,_score,ali_score,li_score \
          --filter '(true && query("name * 10", "ali", {"score_column": "ali_score"})) || \
                    (true && query("name * 2", "li", {"score_column": "li_score"}))'
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                2
              ],
              [
                [
                  "name",
                  "ShortText"
                ],
                [
                  "_score",
                  "Int32"
                ],
                [
                  "ali_score",
                  "Float"
                ],
                [
                  "li_score",
                  "Float"
                ]
              ],
              [
                "Alice",
                14,
                10.0,
                2.0
              ],
              [
                "Alisa",
                14,
                10.0,
                2.0
              ]
            ]
          ]
        ]

  - 以下のように、 `result_set` ステージの動的ベクターカラムも扱えるようにしました。

        table_create Users TABLE_NO_KEY
        column_create Users name COLUMN_SCALAR ShortText

        table_create Lexicon TABLE_HASH_KEY ShortText \
          --default_tokenizer TokenBigramSplitSymbolAlphaDigit \
          --normalizer NormalizerAuto
        column_create Lexicon users_name COLUMN_INDEX|WITH_POSITION Users name

        load --table Users
        [
        {"name": "Alice"},
        {"name": "Alisa"},
        {"name": "Bob"}
        ]

        select Users \
          --columns[tags].stage result_set \
          --columns[tags].type ShortText \
          --columns[tags].flags COLUMN_VECTOR \
          --output_columns name,tags \
          --filter '(true && query("name", "al", {"tags": ["al"], "tags_column": "tags"})) || \
                    (true && query("name", "sa", {"tags": ["sa"], "tags_column": "tags"}))'
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                2
              ],
              [
                [
                  "name",
                  "ShortText"
                ],
                [
                  "tags",
                  "ShortText"
                ]
              ],
              [
                "Alice",
                [
                  "al"
                ]
              ],
              [
                "Alisa",
                [
                  "al",
                  "sa"
                ]
              ]
            ]
          ]
        ]

    - 動的ベクターカラムを使う場合、格納する値は各要素が追加された値になります。

- [Ubuntu] Ubuntu 21.04 (Hirsute Hippo)をサポートしました。

- [httpd] バンドルしているnginxのバージョンを1.19.10に更新しました。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

- [ブラウザーベースの管理ツール] 現在Groongaには、レコード一覧の管理モードのチェックボックスにチェックを入れても、非管理モードに入力された検索クエリーが送信されるという問題があります。 [Github#1186][potiさんが報告]

### 感謝

- Anthony M. Cookさん

- MASUDA Kazuhiroさん

- potiさん

## 11.0.1リリース - 2021-03-31

### 改良

- [Debian GNU/Linux] ARM64向けのパッケージをサポートしました。

- [select] キーワードごとに重みをカスタマイズできるようにしました。

  - 今まで、スコアーを調整するには、すべてのキーワードに対して `<` や `>` を指定する必要がありました。デフォルトのスコアの重み(1)より、デフォルトの重みの調整量(6 または 4)のほうが大きいためです。

    - 例えば、`A <B` では、 "A" の重みは1、 "B" の重みは4になります。デクリメントされていない "A" の重み(1)より、デクリメントされた "B" の重み(4)のほうが大きくなります。これは、期待した動作ではありません。"B" に "A" より小さい重みを使うためには、 `>A <B` と指定する必要があります。`>A <B` では、 "A" の重みは6、 "B" の重みは4になります。

  - 今回のリリースから、対象のキーワードに `<${WEIGHT}` または `>${WEIGHT}` と指定するだけで、キーワードごとに重みの調整量をカスタマイズできます。例えば、 `A <0.1B` では、 "A" の重みは1、 "B" の重みは0.9になります。("B" の重みを0.1デクリメントしています。)

  - ただし、これらの形式(`>${WEIGHT}...`, `<${WEIGHT}...`, and `~${WEIGHT}...`)は互換性が無いことに注意してください。

- [select] Apache Arrow形式で `Float` と `Float32` の値を出力できるようにしました。

  - 例えば、以下のように出力します。

      table_create Data TABLE_NO_KEY
      column_create Data float COLUMN_SCALAR Float

      load --table Data
      [
      {"float": 1.1}
      ]

      select Data \
        --command_version 3 \
        --output_type apache-arrow

        return_code: int32
        start_time: timestamp[ns]
        elapsed_time: double
        -- metadata --
        GROONGA:data_type: metadata
         return_code                    start_time       elapsed_time
        0                  0     1970-01-01T09:00:00+09:00           0.000000
        ========================================
        _id: uint32
        float: double
        -- metadata --
        GROONGA:n_hits: 1
         _id          float
        0          1       1.100000

- [select] 結果出力時に、インデックスカラム経由で参照先のデータを取得できるようになりました。

  - 今までは、 出力値に `index_column.xxx` のように指定すると、Groongaは意図しない値を返していました。例えば以下の例では、 `--columns[tags].value purchases.tag` の値は、 `["apple",["many"]],["banana",["man"]],["cacao",["man"]]` になりました。このケースでは、期待される値は、 `["apple",["man","many"]],["banana",["man"]],["cacao",["woman"]]` でした。今回のリリースから、以下のように、インデックスカラム経由で参照先の正しい値を取得できます。

        table_create Products TABLE_PAT_KEY ShortText

        table_create Purchases TABLE_NO_KEY
        column_create Purchases product COLUMN_SCALAR Products
        column_create Purchases tag COLUMN_SCALAR ShortText

        column_create Products purchases COLUMN_INDEX Purchases product

        load --table Products
        [
        {"_key": "apple"},
        {"_key": "banana"},
        {"_key": "cacao"}
        ]

        load --table Purchases
        [
        {"product": "apple",  "tag": "man"},
        {"product": "banana", "tag": "man"},
        {"product": "cacao",  "tag": "woman"},
        {"product": "apple",  "tag": "many"}
        ]

        select Products \
          --columns[tags].stage output \
          --columns[tags].flags COLUMN_VECTOR \
          --columns[tags].type ShortText \
          --columns[tags].value purchases.tag \
          --output_columns _key,tags
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                3
              ],
              [
                [
                  "_key",
                  "ShortText"
                ],
                [
                  "tags",
                  "ShortText"
                ]
              ],
              [
                "apple",
                [
                  "man",
                  "many"
                ]
              ],
              [
                "banana",
                [
                  "man"
                ]
              ],
              [
                "cacao",
                [
                  "woman"
                ]
              ]
            ]
          ]
        ]

- [select] ネストされたインデックスの一部として直接インデックスカラムを指定できるようになりました。

  - `index_column.except_source_column` を使ってフィルター後にソーステーブルを検索できます。例えば、以下の例では、検索時に `comments.content` を指定しています。この場合、最初に、このクエリは `Comments` テーブルの `content` カラムを全文検索し、次に `Comments` テーブルを検索した結果のレコードを参照する `Articles` テーブルのレコードを取得します。

        table_create Articles TABLE_HASH_KEY ShortText

        table_create Comments TABLE_NO_KEY
        column_create Comments article COLUMN_SCALAR Articles
        column_create Comments content COLUMN_SCALAR ShortText

        column_create Articles content COLUMN_SCALAR Text
        column_create Articles comments COLUMN_INDEX Comments article

        table_create Terms TABLE_PAT_KEY ShortText \
          --default_tokenizer TokenBigram \
          --normalizer NormalizerNFKC130
        column_create Terms articles_content COLUMN_INDEX|WITH_POSITION \
          Articles content
        column_create Terms comments_content COLUMN_INDEX|WITH_POSITION \
          Comments content

        load --table Articles
        [
        {"_key": "article-1", "content": "Groonga is fast!"},
        {"_key": "article-2", "content": "Groonga is useful!"},
        {"_key": "article-3", "content": "Mroonga is fast!"}
        ]

        load --table Comments
        [
        {"article": "article-1", "content": "I'm using Groonga too!"},
        {"article": "article-3", "content": "I'm using Mroonga!"},
        {"article": "article-1", "content": "I'm using PGroonga!"}
        ]

        select Articles --match_columns comments.content --query groonga \
          --output_columns "_key, _score, comments.content
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                1
              ],
              [
                [
                  "_key",
                  "ShortText"
                ],
                [
                  "_score",
                  "Int32"
                ],
                [
                  "comments.content",
                  "ShortText"
                ]
              ],
              [
                "article-1",
                1,
                [
                  "I'm using Groonga too!",
                  "I'm using PGroonga!"
                ]
              ]
            ]
          ]
        ]

- [load] オブジェクトリテラルを使って、参照先のレコードをロードできるようにしました。

  - 例えば、以下のように、 `"key" : "[ { "key" : "value", ..., "key" : "value" } ]"` のようなデータをロードできます。

        table_create Purchases TABLE_NO_KEY
        column_create Purchases item COLUMN_SCALAR ShortText
        column_create Purchases price COLUMN_SCALAR UInt32

        table_create Settlements TABLE_HASH_KEY ShortText
        column_create Settlements purchases COLUMN_VECTOR Purchases
        column_create Purchases settlements_purchases COLUMN_INDEX Settlements purchases

        load --table Settlements
        [
        {
          "_key": "super market",
          "purchases": [
             {"item": "apple", "price": 100},
             {"item": "milk",  "price": 200}
          ]
        },
        {
          "_key": "shoes shop",
          "purchases": [
             {"item": "sneakers", "price": 3000}
          ]
        }
        ]

  - この機能によって、参照カラムにJSONデータを追加しやすくなります。

  - 現状、この機能は、JSONの入力のみをサポートしています。

- [load] JSON文字列で、参照先のレコードをロードできるようにしました。

  - 以下のように、JSONテキストを使ってソーステーブルから参照ベクターへデータをロードできます。

        table_create Purchases TABLE_HASH_KEY ShortText
        column_create Purchases item COLUMN_SCALAR ShortText
        column_create Purchases price COLUMN_SCALAR UInt32

        table_create Settlements TABLE_HASH_KEY ShortText
        column_create Settlements purchases COLUMN_VECTOR Purchases

        column_create Purchases settlements_purchases COLUMN_INDEX Settlements purchases

        load --table Settlements
        [
        {
          "_key": "super market",
          "purchases": "[{\"_key\": \"super market-1\", \"item\": \"apple\", \"price\": 100}, {\"_key\": \"super market-2\", \"item\": \"milk\",  \"price\": 200}]"
        },
        {
          "_key": "shoes shop",
          "purchases": "[{\"_key\": \"shoes shop-1\", \"item\": \"sneakers\", \"price\": 3000}]"
        }
        ]

        dump \
          --dump_plugins no \
          --dump_schema no
        load --table Purchases
        [
        ["_key","item","price"],
        ["super market-1","apple",100],
        ["super market-2","milk",200],
        ["shoes shop-1","sneakers",3000]
        ]

        load --table Settlements
        [
        ["_key","purchases"],
        ["super market",["super market-1","super market-2"]],
        ["shoes shop",["shoes shop-1"]]
        ]

        column_create Purchases settlements_purchases COLUMN_INDEX Settlements purchases

  - 現状、この機能は、ネストされた参照レコードをサポートしていません。

- [Windows] `time_classify_*` 関数で UNIXエポック を扱えるようになりました。

  - Groongaは、現地時刻でタイムスタンプを扱っています。したがって、例えば日本でUNIXエポックを入力した場合、入力した時刻はUNIXエポックの9時間前になります。

  - WindowsのAPIはUNIXエポックより前の時間が入力されるとエラーになります。

  - 今回のリリースから、以下のようにUNIXエポックを `time_classify_*` 内で使えるように

        plugin_register functions/time

        table_create Timestamps TABLE_PAT_KEY Time
        load --table Timestamps
        [
        {"_key": 0},
        {"_key": "2016-05-06 00:00:00.000001"},
        {"_key": "2016-05-06 23:59:59.999999"},
        {"_key": "2016-05-07 00:00:00.000000"},
        {"_key": "2016-05-07 00:00:00.000001"},
        {"_key": "2016-05-08 23:59:59.999999"},
        {"_key": "2016-05-08 00:00:00.000000"}
        ]

        select Timestamps \
          --sortby _id \
          --limit -1 \
          --output_columns '_key, time_classify_day_of_week(_key)'
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                7
              ],
              [
                [
                  "_key",
                  "Time"
                ],
                [
                  "time_classify_day_of_week",
                  null
                ]
              ],
              [
                0.0,
                4
              ],
              [
                1462460400.000001,
                5
              ],
              [
                1462546799.999999,
                5
              ],
              [
                1462546800.0,
                6
              ],
              [
                1462546800.000001,
                6
              ],
              [
                1462719599.999999,
                0
              ],
              [
                1462633200.0,
                0
              ]
            ]
          ]
        ]

- [query_parallel_or] クエリーを並行して実行できる新しい関数を追加しました。

  - `query_parallel_or` でクエリーを並行で処理するためにはApache Arrowが必要です。Apache Arrowが無い場合は、 `query_parallel_or` はクエリーをシーケンシャルに実行します。

  - `query_parallel_or` は、 `match_columns` と `query_string` の組み合わせを平行に処理します。

  - `query_parallel_or` の構文は以下の通りです。:

        query_parallel_or(match_columns, query_string1,
                                         query_string2,
                                         .
                                         .
                                         .
                                         query_stringN,
                                         {"option": "value", ...})

- [select] 存在しないソートキーを無視するようにしました。

  - 今まで、存在しないソートキーを指定したとき、Groongaはエラーを出力していましたが、今回のリリースから存在しないソートキーを無視します。(エラーを出力しなくなります。)

  - この機能は、一貫性のために実装しています。`output_columns` も無効な値を無視します。また、 `sort_keys` も無効な値のほとんどを無視します。

- [select] drilldowns[].table で存在しないテーブルを無視します。 [GitHub#1169][naoaさんが報告]

  - 今まで、 `drilldowns[].table` で存在しないテーブルを指定したとき、Groongaはエラーを出力していましたが、今回のリリースから存在しないテーブルを無視します。(エラーを出力しなくなります。)

  - この機能は、一貫性のために実装しています。`output_columns` も無効な値を無視します。また、 `sort_keys` も無効な値のほとんどを無視します。

- [httpd] バンドルしているnginxのバージョンを1.19.8に更新しました。

### 修正

- [reference_acquire] 参照の自動リリースが発生する前に、テーブルにカラムが追加されテーブルの参照が獲得されたときにGroongaがクラッシュする問題を修正しました。

  - 追加されたカラムの参照は獲得されませんが、自動リリースの対象になるためです。

- [Windows] 別のスレッドで、他のバックトレースロギングプロセスが動作しているときに、新しくバックトレースロギングプロセスが開始されると、1つ以上のプロセスがSEGVのバックトレースの出力に失敗する問題を修正しました。

### 既知の問題

- 現在Groongaには、ベクターカラムに対してデータを大量に追加、削除、更新した際にデータが破損することがある問題があります。

### 感謝

- naoaさん

## 11.0.0リリース - 2021-02-09

メジャーバージョンアップです! メジャーバージョンアップですが、互換性は壊れていないので、データベースを再構築することなく11.0.0へアップグレードできます。

### 改良

- [select] ネストされたインデックス経由でスカラーカラムとベクターカラムの値を出力できるようになりました。

  - ネストされたインデックスとは、以下のような構造です。

        table_create Products TABLE_PAT_KEY ShortText

        table_create Purchases TABLE_NO_KEY
        column_create Purchases product COLUMN_SCALAR Products
        column_create Purchases tag COLUMN_SCALAR ShortText

        column_create Products purchases COLUMN_INDEX Purchases product

  - 上記の例では、 `Products.purchases` カラムは `Purchases.product` カラムのインデックスです。また、 `Purchases.product` カラムは、 `Products` テーブルへの参照です。

  - 今までは、ネストされたインデックス経由の検索は、期待した結果を返しませんでした。

  - 今までの結果は以下のようになっていました。 `{"product": "apple", "tag": "man"}` が出力されていないことがわかります。

        table_create Products TABLE_PAT_KEY ShortText

        table_create Purchases TABLE_NO_KEY
        column_create Purchases product COLUMN_SCALAR Products
        column_create Purchases tag COLUMN_SCALAR ShortText

        column_create Products purchases COLUMN_INDEX Purchases product

        load --table Products
        [
        {"_key": "apple"},
        {"_key": "banana"},
        {"_key": "cacao"}
        ]

        load --table Purchases
        [
        {"product": "apple",  "tag": "man"},
        {"product": "banana", "tag": "man"},
        {"product": "cacao",  "tag": "woman"},
        {"product": "apple",  "tag": "many"}
        ]

        select Products \
          --output_columns _key,purchases.tag
        [
          [
            0,
            1612504193.380738,
            0.0002026557922363281
          ],
          [
            [
              [
                3
              ],
              [
                [
                  "_key",
                  "ShortText"
                ],
                [
                  "purchases.tag",
                  "ShortText"
                ]
              ],
              [
                "apple",
                "many"
              ],
              [
                "banana",
                "man"
              ],
              [
                "cacao",
                "man"
              ]
            ]
          ]
        ]

  - このリリースから、結果は以下のようになります。 `{"product": "apple", "tag": "man"}` が出力されているのがわかります。

        select Products \
          --output_columns _key,purchases.tag
        [
          [
            0,
            0.0,
            0.0
          ],
          [
            [
              [
                3
              ],
              [
                [
                  "_key",
                  "ShortText"
                ],
                [
                  "purchases.tags",
                  "Tags"
                ]
              ],
              [
                "apple",
                [
                  [
                    "man",
                    "one"
                  ],
                  [
                    "child",
                    "many"
                  ]
                ]
              ],
              [
                "banana",
                [
                  [
                    "man",
                    "many"
                  ]
                ]
              ],
              [
                "cacao",
                [
                  [
                    "woman"
                  ]
                ]
              ]
            ]
          ]
        ]

- [Windows] Linux上のMinGWを使ってクロスコンパイルしていたWindows向けパッケージの提供をやめました。

  - おそらく、ほとんどの人がこのパッケージを使っていないためです。

  - 上記のパッケージはこれまで、以下のような名前で提供されていたものです。

    - `groonga-x.x.x-x86.exe`

    - `groonga-x.x.x-x86.zip`

    - `groonga-x.x.x-x64.exe`

    - `groonga-x.x.x-x86.zip`

  - これからは、以下のパッケージを使用してください。

    - `groonga-latest-x86-vs2019-with-vcruntime.zip`

    - `groonga-latest-x64-vs2019-with-vcruntime.zip`

  - 既に Microsoft Visual C++ Runtime Library がインストール済みのシステムの場合は、以下のパッケージを使用してください。

    - `groonga-latest-x86-vs2019.zip`

    - `groonga-latest-x64-vs2019.zip`

### 修正

- インデックス内のデータを大量に追加、削除、更新した際にインデックスが破損することがある問題を修正しました。

  - この問題は、インデックス内のデータを大量に削除しただけでも発生します。ただ、インデックスにデータを追加しただけでは発生しません。

  - この問題によって破損したインデックスは、インデックスを再構築することで修復できます。

  - この問題は、壊れたインデックスを参照しない限り発覚しません。したがって、既にインデックスが破損しているかもしれません。

  - [index_column_diff] コマンドを使うことで、インデックスが破損しているかどうかを確認できます。