MariaDBとMroongaで作るn全言語対応n超高速全文検索システム¶ ↑
: author
須藤功平
: institution
クリアコード
: content-source
第一回 JPMUG DB勉強会
: date
2018-01-30
: start-time
2018-01-30T19:25:00+09:00
: end-time
2018-01-30T20:10:00+09:00
: theme
.
全文検索システムn対象¶ ↑
(('tag:center')) (('tag:large')) (('tag:margin-bottom * 2')) 大量のテキスト
* 例:Wikiのデータ * 例:オフィス文書のテキスト * 例:商品説明・口コミ
全文検索システムn目的¶ ↑
* 必要な情報を * 必要なときに * 活用
必要な情報を活用¶ ↑
* × * 探している情報が見つからない * ○ * 探している情報が見つかる * ◎ * 意識していなかったけど\n ((*実は欲しかった*))情報も見つかる!
必要なときに活用¶ ↑
* × * なかなか見つからない * ○ * すぐに見つかる * ◎ * すでに見つかっていた * 例:レコメンデーション
実装方法n選択肢¶ ↑
* 全文検索サーバーを使う * MariaDBでLIKEを使う
全文検索サーバー案nメリット¶ ↑
* 必要な機能が揃っている * +αの機能もある * 速い
全文検索サーバー案nデメリット¶ ↑
* 実装コスト大 * それぞれ独自の使い方だから * マスターデータの同期はどうする? * メンテナンスコスト大 * それぞれ独自の仕組みだから
MariaDBでLIKE案nメリット¶ ↑
* 実装コスト小 * 新しく覚えることが少ない * データの一元管理 * メンテナンスコスト小 * 既存の運用ノウハウを使える * データ少なら実用的な速度
MariaDBでLIKE案nデメリット¶ ↑
* 機能不足 * それっぽい順のソート不可 * 全文検索ではソート順が重要 * ユーザーは先頭n件しか見ない * SQLの表現力不足 * nクエリーで実現すると性能に影響
実現方法n第3の選択肢¶ ↑
* MariaDB経由(SQL)で\n 全文検索エンジンを使う
メリット¶ ↑
* 高速で豊富な機能 * それっぽい順のソート可 * 実装コスト小 * メンテナンスコスト小
デメリット¶ ↑
* MariaDBに拡張機能が必要 * RDS・Azure databaseで使えない\n (('note:(Azure database for MariaDBはまだリリース前)'))
オススメの選択肢n全文検索の知識ナシ¶ ↑
* まだ単純な機能で十分 * データ少:MariaDB単独でLIKE\n (('note:(数十万件とか)')) * データ中以上:\n MariaDB経由で全文検索エンジン * いまどきの全文検索機能が必要 * MariaDB経由で全文検索エンジン
オススメの選択肢n全文検索の知識アリ¶ ↑
* カリカリにチューニングしたい * MariaDBと全文検索サーバーを併用 * それ以外 * MariaDB経由で全文検索エンジン
説明する選択肢¶ ↑
MariaDB経由でn 全文検索n エンジン
全文検索エンジンnGroonga(ぐるんが)¶ ↑
* 組込可能な全文検索エンジン * MariaDB・MySQLに組込→Mroonga * PostgreSQLに組込→PGroonga * 全文検索サーバーとして\n 単独でも使用可能 * MariaDBと全文検索サーバーを併用\n もできる
Groongaの得意なこと¶ ↑
* データの追加・更新 * 新鮮な情報をすぐに検索可能に! * 更新中も検索性能を落とさない! * 日本語 * 開発者が日本人 * 便利機能が組み込み
GroongaとUnicode¶ ↑
* NFKCベースの正規化機能を組込 * Unicode 5.1ベースで古い * 2008年の仕様 * Unicode 10.0(最新)対応中\n (('note:正規化方式を変えるとインデックスの互換性がなくなる(=要インデックス再構築)のでデフォルトは変えない'))
Mroonga(むるんが)¶ ↑
* MariaDBのストレージエンジン * InnoDB・MyISAMなどと同じレイヤー * MariaDB 10.0.15から標準バンドル * 使用方法 * (({CREATE TABLE (...) ENGINE=Mroonga}))
照合順序:COLLATION¶ ↑
* 文字の並び順の規則 * 文字が同一かどうかの判定にも利用 * 適切な日本語規則なし * いわゆる🍣=🍺問題
(('note:MySQL 8では適切な日本語規則が追加される'))n (('note:utf8mb4_ja_0900_as_csなど'))
Mroongaの照合順序¶ ↑
* MariaDB互換のもの * (({utf8mb4_ja_0900_*}))互換は対応予定 * MariaDB互換を微調整したもの * 日本語でもいい感じ * Groonga提供のもの * NFKCベースのもの * 日本語でもいい感じ
Mroongaで照合順序¶ ↑
* MariaDB互換がよい! * 互換正規化処理を使用:デフォルト * MariaDB互換は気にしないから\n いい感じに! * Groonga提供のものを使用
全文検索性能n計測データ¶ ↑
* 対象:Wikipedia日本語版 * レコード数:約185万件 * データサイズ:約7GB * メモリー4GB・SSD250GB(('note:(ConoHa)'))
検索性能1¶ ↑
(('tag:center')) キーワード:テレビアニメn (('note:(ヒット数:約2万3千件)'))
# RT delimiter = [|] InnoDB ngram | 3m2s InnoDB MeCab | 6m20s Mroonga:((*1*)) | 0.11s
検索性能2¶ ↑
(('tag:center')) キーワード:データベースn (('note:(ヒット数:約1万7千件)'))
# RT delimiter = [|] InnoDB ngram | 36s InnoDB MeCab:((*1*)) | 0.03s Mroonga:((*2*)) | 0.09s
検索性能3¶ ↑
(('tag:center')) キーワード:PostgreSQL OR MySQLn (('note:(ヒット数:約400件)'))
# RT delimiter = [|] InnoDB ngram | N/A(Error) InnoDB MeCab:((*1*)) | 0.005s Mroonga:((*2*)) | 0.028s
検索性能4¶ ↑
(('tag:center')) キーワード:日本n (('note:(ヒット数:約63万件)'))
# RT delimiter = [|] InnoDB ngram | 1.3s InnoDB MeCab | 1.3s Mroonga:((*1*)) | 0.21s
全文検索性能まとめ¶ ↑
* Mroonga:安定して速い * ((*SQLで使えて機能豊富で速い!*)) * InnoDB FTS MeCab * ハマれば速い * InnoDB FTS ngram * 安定して遅い
普通の検索も速い¶ ↑
* カラムストアを活かした最適化 * ポイント1:余計なI/Oを減らす * ポイント2:I/Oを局所化
カラムストア¶ ↑
# image # src = images/column-store.svg # relative_width = 100
必要なカラムのみアクセス¶ ↑
# coderay sql -- aのみにアクセス SELECT a FROM table -- cのみにアクセス WHERE c = XXX; -- bにはアクセスしない
減ったI/O¶ ↑
# image # src = images/not-access-to-needless-columns.svg # relative_width = 100
行カウント¶ ↑
# coderay sql -- カラムの値は必要ない SELECT COUNT(*) FROM table -- cの全文検索インデックスにだけアクセス WHERE MATCH(c) AGAINST('+keyword' IN BOOLEAN MODE); -- a, b, cはアクセスしない
減ったI/O¶ ↑
# image # src = images/count-star.svg # relative_width = 100
(({ORDER BY LIMIT}))¶ ↑
# coderay sql SELECT a FROM table WHERE MATCH(c) AGAINST('+keyword' IN BOOLEAN MODE) -- MariaDBではなくMroongaがORDER BY LIMITを処理 -- →Mroongaは10レコードだけMariaDBに返す -- マッチしたレコードすべては返さない ORDER BY a LIMIT 10;
(({ORDER BY LIMIT}))の最適化¶ ↑
* Mroongaが検索 * カラム毎の処理でI/Oを局所化\n (('note:(索引非使用時)')) * Mroongaがソート * カラム毎の処理でI/Oを局所化 * Mroongaが(({OFFSET}))/(({LIMIT}))を処理
カラム毎の処理は速い¶ ↑
# image # src = images/per-column-processing.svg # relative_width = 100
condition push downの最適化¶ ↑
* 従来はMariaDBが処理していた検索条件をストレージエンジンが処理する仕組み * ストレージエンジンでの処理の方が高速なら全体として高速になる * Mroonga 7.10から実験的に対応 * デフォルトオフ
condition push downの効果¶ ↑
* 全文検索インデックスのみ * 等価条件:シーケンシャルスキャン * 全文検索:インデックススキャン * データ * シカゴの犯罪データ(('note:(651万レコード)'))
(('tag:xx-small')) 詳細:((<“github.com/kou/rabbit-slide-kou-jpmug-db-study-1/blob/master/memo.md”|URL:https://github.com/kou/rabbit-slide-kou-jpmug-db-study-1/blob/master/memo.md>))
等価条件:数値1つ¶ ↑
(('tag:center')) 26万件ヒットするケース
# RT delimiter = [|] InnoDB | 1.3s Mroonga\n(デフォルト) | 1.3s Mroonga\n(最適化ON) | 0.4s
等価条件:数値1つ+真偽値2つ¶ ↑
(('tag:center')) 7000件ヒットするケース
# RT delimiter = [|] InnoDB | 1.6s Mroonga\n(デフォルト) | 2.3s Mroonga\n(最適化ON) | 0.4s
全文検索+等価条件¶ ↑
(('tag:center')) 4000件ヒットするケース
# RT delimiter = [|] InnoDB | 18s Mroonga\n(デフォルト) | 0.4s Mroonga\n(最適化ON) | 0.4s
(('note:このパターンはデフォルトで最適化が効く'))
Mroongaの検索性能まとめ¶ ↑
* 最適化が効くと桁違いに速い * 全文検索のときはデフォルトで効く * 7.10からさらなる最適化が!\n (('note:まだ実験的扱いなのでデフォルトオフ')) * OLAP用途にも使える * MariaDB ColumnStoreを補完する\n 立ち位置も可
(('tag:xx-small')) 参考:((<“mroonga.org/ja/docs/reference/server_variables.html#mroonga-condition-push-down-type”|URL:http://mroonga.org/ja/docs/reference/server_variables.html#mroonga-condition-push-down-type>))
全文検索システムの実装¶ ↑
* 全文検索 * キーワードハイライト * 周辺テキスト表示 * オートコンプリート * 同義語展開 * 関連文書の表示
全文検索¶ ↑
# image # src = images/php-document-search-search.png # relative_height = 100
テーブル定義¶ ↑
# coderay sql CREATE TABLE entries ( title text, content text, -- 全文検索用インデックス -- よくわからないならデフォルトのまま使うこと! FULLTEXT INDEX (title, content) ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;
データ挿入¶ ↑
# coderay sql -- 普通に挿入するだけでよい INSERT INTO entries VALUES ('タイトル', '高速に全文検索したいですね!');
全文検索¶ ↑
# coderay sql SELECT title FROM entries WHERE -- MATCH AGAINSTで全文検索 MATCH (title, content) -- デフォルトORがMariaDBの仕様 -- 「検索」または「高速」を含むとマッチ AGAINST ('検索 高速' IN BOOLEAN MODE);
AND全文検索¶ ↑
# coderay sql MATCH (title, content) -- 各キーワードの前に「+」をつけるとAND -- 「検索」かつ「高速」を含むとマッチ AGAINST ('+検索 +高速' IN BOOLEAN MODE);
使いやすいAND全文検索¶ ↑
# coderay sql MATCH (title, content) -- 最初に「*D+」をつけるとデフォルトAND -- Mroonga独自機能 -- 「検索」かつ「高速」を含むとマッチ AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE);
それっぽい順のソート¶ ↑
# coderay sql SELECT title, -- ここのMATCH AGAINSTはスコアーを返す MATCH (title, content) AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE) AS score FROM entries WHERE -- ... -- それっぽさでソート ORDER BY score DESC LIMIT 10;
ハイライト¶ ↑
# image # src = images/php-document-search-search.png # relative_height = 100
ハイライト¶ ↑
# coderay sql SELECT mroonga_highlight_html( title, '*D+ 検索 高速' AS query) -- クエリーからハイライト対象のキーワードを抽出 FROM entries WHERE MATCH (title, content) AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE);
ハイライト結果例¶ ↑
# coderay html <Groonga>で高速全文検索! ↓ <Groonga>で ← タグをエスケープ <span class="keyword">高速</span> 全文 ↑↓キーワードはclass付け <span class="keyword">検索</span>!
周辺テキスト¶ ↑
# image # src = images/php-document-search-search.png # relative_height = 100
周辺テキスト¶ ↑
# coderay sql SELECT mroonga_snippet_html( content, '*D+ 検索 高速' AS query) -- クエリーから対象のキーワードを抽出 FROM entries WHERE MATCH (title, content) AGAINST ('*D+ 検索 高速' IN BOOLEAN MODE);
周辺テキスト結果例¶ ↑
# coderay html ...<Groonga>で高速全文検索!... ↓ <div class="snippet"> ←1つ目 ga>で ←タグをエスケープ <span class="keyword">高速</span> 全文 ↑↓キーワードはclass付け <span class="keyword">検索/span>! </div> <div class="snippet">...</div> ←2つ目
オートコンプリート¶ ↑
# image # src = images/php-document-search.png # relative_height = 100
オートコンプリート:必要なもの¶ ↑
* マスターテーブル * 候補(例:牛乳) * 候補のヨミ(カタカナ・複数可) * 例1:ギュウニュウ * 例2:ミルク
オートコンプリート:実装方法¶ ↑
* 以下の検索のOR * ヨミでの前方一致検索 * 候補を緩い全文検索 * 候補でソートして提示
オートコンプリート:テーブル定義¶ ↑
# coderay sql CREATE TABLE terms ( term varchar(256), -- 補完候補 reading varchar(256), -- ヨミガナ PRIMARY KEY (term, reading), FULLTEXT INDEX (term) -- 候補全文検索用 -- 緩い全文検索用トークナイザー COMMENT 'tokenizer "TokenBigramSplitSymbolAlpha"', FULLTEXT INDEX (reading) -- ヨミガナ前方一致用 COMMENT 'normalizer "NormalizerAuto", tokenizer "off"' -- トークナイザー不要 ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;
オートコンプリート:データ例¶ ↑
# coderay sql INSERT INTO terms VALUES ( '牛乳', -- 補完候補 'ギュウニュウ' --ヨミガナはカタカナで指定 ); INSERT INTO terms VALUES ( '牛乳', 'ミルク' -- 「ミルク」でも補完できるように );
オートコンプリートnデータ管理のポイント¶ ↑
* 普通のテーブルなので管理が楽 * 追加・削除・更新が楽 * ダンプ・リストアもいつも通り * レプリケーションもいつも通り
オートコンプリートn検索方法¶ ↑
# coderay sql SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索 AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape(${入力} AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索 AGAINST (CONCAT('*D+ ', mroonga_escape(${入力})) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
オートコンプリートn検索例:漢字1¶ ↑
# coderay sql -- ユーザーが「牛」を入力した場合 SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索 AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape('牛' AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索(ヒット) AGAINST (CONCAT('*D+ ', mroonga_escape('牛')) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
オートコンプリートn検索例:漢字2¶ ↑
# coderay sql -- ユーザーが「乳」を入力した場合 SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索 AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape('乳' AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索(ヒット) AGAINST (CONCAT('*D+ ', mroonga_escape('乳')) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
オートコンプリートn検索例:カタカナ¶ ↑
# coderay sql -- ユーザーが「ギュウ」を入力した場合 SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット) AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape('ギュウ' AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索 AGAINST (CONCAT('*D+ ', mroonga_escape('ギュウ')) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
オートコンプリートn検索例:ひらがな¶ ↑
# coderay sql -- ユーザーが「ぎゅう」を入力した場合 SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット) AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape('ぎゅう' AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索 AGAINST (CONCAT('*D+ ', mroonga_escape('ぎゅう')) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
オートコンプリートn検索例:ローマ字¶ ↑
# coderay sql -- ユーザーが「gyu」を入力した場合 SELECT DISTINCT(term) FROM terms WHERE MATCH (reading) -- ヨミガナ前方一致検索(ヒット) AGAINST (CONCAT('*SS prefix_rk_search(reading, ', mroonga_escape('gyu' AS script), ')') IN BOOLEAN MODE) OR MATCH (term) -- 候補を緩く全文検索 AGAINST (CONCAT('*D+ ', mroonga_escape('gyu')) IN BOOLEAN MODE) ORDER BY term LIMIT 10; -- ソート
同義語展開¶ ↑
* 同義語 * 同じ意味だが表記が異なる語 * 例:「刺身」と「お造り」 * どの表記でもヒットして欲しい * 同義語展開→同義語すべてでOR検索
同義語展開n実装方法¶ ↑
* 同義語管理テーブルを作成 * クエリー内の同義語を展開 * 展開後のクエリーで検索
同義語展開:Mroonganテーブル定義¶ ↑
# coderay sql CREATE TABLE synonyms ( term varchar(255), -- 展開対象の語 synonym varchar(255), -- 同義語 INDEX (term) -- 高速化と精度向上 COMMENT 'normalizer "NormalizerAuto"' ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;
同義語展開nデータ例¶ ↑
# coderay sql INSERT INTO synonyms -- 「刺身」を「刺身 OR お造り」に展開 VALUES ('刺身', '刺身'), ('刺身', 'お造り'), -- 「お造り」を「お造り OR 刺身」に展開 ('お造り', 'お造り'), ('お造り', '刺身');
同義語展開nデータ管理のポイント¶ ↑
* 普通のテーブルなので管理が楽 * 追加・削除・更新が楽 * ダンプ・リストアもいつも通り * レプリケーションもいつも通り
同義語展開:Mroongan確認方法¶ ↑
# coderay sql SELECT mroonga_query_expand( 'synonyms', -- テーブル名 'term', -- 展開対象のカラム名 'synonym', -- 対応する同義語のカラム名 '居酒屋 刺身' -- クエリー ); -- '居酒屋 ((刺身) OR (お造り))'
同義語展開:Mroongan検索方法¶ ↑
# coderay sql SELECT title FROM entries WHERE MATCH (title) -- '*D+ 居酒屋 OR ((刺身) OR (お造り))'になる AGAINST (mroonga_query_expand('synonyms', 'term', 'synonym', '*D+ 居酒屋 刺身') IN BOOLEAN MODE);
類似文書検索¶ ↑
* 検索クエリーは文書そのもの * キーワードではない * 関連エントリーの提示に使える * メタデータがあるなら組み合わせる\n →精度向上 * メタデータ:タグ・行動履歴など
類似文書検索:Mroonganインデックス定義¶ ↑
# coderay sql CREATE TABLE entries ( -- ... FULLTEXT INDEX (content) -- TokenMecabを使わないと精度がでない -- 必要なときだけカスタマイズ! COMMENT 'tokenizer "TokenMecab"' ) -- ...
類似文書検索:Mroongan検索方法¶ ↑
# coderay sql SELECT title FROM entries WHERE MATCH (content) -- ↓ 既存文書の内容をそのまま指定 AGAINST ('...Groongaで高速全文検索!...' IN NATURAL LANGUAGE MODE);
類似文書検索:Mroongan結果例¶ ↑
クエリー: ...Groongaで高速全文検索!... ヒット例: ...Mroongaで高速全文検索!...
全文検索システムの実装nまとめ¶ ↑
* 全文検索 * キーワードハイライト * 周辺テキスト表示 * オートコンプリート * 同義語展開 * 関連文書の表示
全文検索システムの実装n次の一歩¶ ↑
* 構造化データ対応 * オフィス文書・HTMLなど * 対応に必要な処理 * テキスト抽出 * メタデータ抽出(('note:(例:タイトル・更新日時)')) * スクリーンショット作成(('note:(なおよい)'))
抽出ツール¶ ↑
* Apache Tika * Apache Luceneのサブプロジェクト * 対応フォーマット数が多い * ChupaText * Groongaのサブプロジェクト * スクリーンショット作成対応
ChupaText¶ ↑
* 対応フォーマット * Word/Excel/PowerPoint * ODT/ODS/ODP(('note:(OpenDocument)')) * PDF/HTML/XML/CSV/... * インターフェイス * HTTPとコマンドライン
ChupaText:インストール¶ ↑
* DockerかVagrantを使うのが楽 * (('tag:xx-small')) https://github.com/ranguba/chupa-text-docker * (('tag:xx-small')) https://github.com/ranguba/chupa-text-vagrant
ChupaText:Docker¶ ↑
# coderay console % GITHUB=https://github.com % git clone \ ${GITHUB}/ranguba/chupa-text-docker.git % cd chupa-text-docker % docker-compose up --build
ChupaText:使い方¶ ↑
# coderay console % curl \ --form data=@XXX.pdf \ http://localhost:20080/extraction.json
ChupaText:結果例¶ ↑
# coderay json { "mime-type": "application/pdf", # 元データのMIMEタイプ "size": 147159, # メタデータ ..., "texts": [ # 抽出されたテキスト(N個) { "mime-type": "text/plain", # 抽出後のMIMEタイプ ..., "creator": "Adobe Illustrator CS3", # メタデータ "body": "This is sample PDF. ...", # 抽出したテキスト "screenshot": { "mime-type": "image/png", # スクリーンショットのMIMEタイプ "data": "iVBORw...", # Base64にした画像データ "encoding": "base64" # Base64であることを明記 } } ] }
ChupaText:Web UI¶ ↑
# image # src = images/chupa-text-web-ui-form.png # relative_height = 100
ChupaText:Web UI抽出例¶ ↑
# image # src = images/chupa-text-web-ui-extract-metadata.png # relative_height = 100
ChupaText:Web UI抽出例¶ ↑
# image # src = images/chupa-text-web-ui-extract-text-and-screenshot.png # relative_height = 100
ChupaText:Vagrant¶ ↑
# coderay console % GITHUB=https://github.com % git clone \ ${GITHUB}/ranguba/chupa-text-vagrant.git % cd chupa-text-vagrant % vagrant up
(('tag:center')) 使い方はDocker版と同じ
ChupaText:活用例¶ ↑
* 抽出したテキスト * Mroongaへ挿入 * 抽出したメタデータ * Mroongaへ挿入 * 絞り込みに活用 * 作成したスクリーンショット * 検索結果表示時に掲載
まとめ¶ ↑
* MariaDBの全文検索まわり * 全文検索システム実装例を紹介 * 構造化データの対応方法を紹介 * ChupaText
扱わなかった話題¶ ↑
* 運用について * 障害対策・レプリケーション * チューニング * Groongaの機能を直接使う方法
サポートサービス紹介¶ ↑
* 導入支援(('note:(設計支援・性能検証・移行支援・…)')) * 開発支援\n (('note:(サンプルコード提供・問い合わせ対応・…)')) * 運用支援(('note:(障害対応・チューニング支援・…)'))
問い合わせ先:
(('tag:x-small')) ((<“www.clear-code.com/contact/?type=groonga”|URL:https://www.clear-code.com/contact/?type=groonga>))