Apache Solrにおける特殊文字の意味と扱い方 | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

Apache Solrにはクエリ内に使う際に注意が必要な特殊文字が幾つかあります。


Apache Lucene - Query Parser Syntax


上記マニュアルによれば、下記の文字列が特殊文字に該当するようです。


+ - && || ! ( ) { } [ ] ^ " ~ * ? : \

管理ツール上のQuery機能を使って特殊文字をそのまま入れて検索してみてもSolrがエラーを返してきます。


org.apache.solr.search.SyntaxError: Cannot parse '&&': Encountered " <AND> "&& "" at line 1, column 0.

ということで、この辺の特殊文字の意味とこれらをクエリに使いたい場合の回避方法のまとめです。



Apache Solrの特殊文字


・ 丸括弧()


Solrで丸括弧(小括弧)の文字はグルーピングを意味します。
例えば、「Apache Solrのデモ環境を作ってみる 」で使った郵便番号データの場合、下記のような検索が行えます。


(prefecture:東京) AND 杉並

東京都の中の杉並という地名を探すという検索です。

ちなみに、普通に杉並だけで探すと北海道に杉並町というのがあるらしくそのデータもヒットします。

都道府県に東京を指定するという条件のグルーピングを()で指定しているわけです。


・ 角括弧[]


Solrで角括弧(大括弧)の文字はレンジを意味します。
先ほどの郵便番号データの例で言えば下記のように書くことで郵便番号が指定の範囲にあるデータを検索できます。


zip:[0100000 TO 0200000]

郵便番号が「0100000」から「0200000」の範囲にあるデータを検索するというものです。
細かいところですが開始(0100000)と終了(0200000)の値も範囲に含まれます(以上、以下)。
後述しますが、アスタリスクを使って「zip:01*」というような検索でも似たようなことはできたりするのですが、きちんと範囲を指定したい場合は[]を使った範囲指定のほうがクエリもわかりやすいでしょう。


・ 波括弧{}


Solrで波括弧(中括弧)の文字は角括弧と同じレンジを意味しますが、若干角括弧と仕様が異なります。
角括弧は指定した開始と終了の値も含みますが、波括弧はその値を含みません(より大きい、未満)


zip:{0100000 TO 0200000}

この場合「0100000」と「0200000」の郵便番号を持つデータはヒットしません。


・ アンパサンド(&&)


これはプログラムでよくある構文と同じでAND条件を指定するものです。
先ほどの例の


(prefecture:東京) AND 杉並
(prefecture:東京) && 杉並

上記は同じ意味となります。


・ バーティカルバー(||)


こちらもプログラムの構文と同じでOR条件を指定するものです。


(prefecture:東京) OR 杉並
(prefecture:東京) || 杉並

上記は同じ意味となります。


・ 感嘆符(!)


感嘆符はNOTを意味し、その条件を含まないという動作をします。


(prefecture:東京) !杉並

上記は、東京都で「杉並」という言葉を含まない地名を検索します。


もちろんNOTの言葉のままでも使えます。


(prefecture:東京) NOT 杉並

・ プラス(+)


プラスはその言葉が必ず含まれるという条件になります。


(prefecture:東京) +杉並

上記の場合、必ず「杉並」ということが含まれる条件で検索します。


・ マイナス(-)


マイナスはプラスの逆でその言葉を含まないという条件になります。


(prefecture:東京) -杉並

どうもこの結果はNOTと同じみたいです。


In Solr, what is the difference between the NOT and - (minus) operators? @ stackoverflow


・ ワイルドカード(* ?)


Apache Solrの検索であいまい検索をしたい場合、ワイルドカードを指定することで柔軟な検索ができます。


town:*新宿*

アスタリスクを指定した場合、その文字が含まれる(前方一致や後方一致、中間一致)データを検索します。
例の場合、西新宿や新宿町などがヒットします。

アスタリスクはその間に0個以上の文字があるものにヒットしますが、特定の1文字に限定したければ疑問符を指定します。


town:?新宿

・ あいまい検索(~)


ワイルドカードの指定でもあいまい検索ができるのですが、Solrお任せのあいまい検索も可能です。
単語の最後にチルダを指定すると、その単語に近いキーワードを持つデータを検索してくれます。


town:杉並~

例えば、上記の検索だと「日並」や「杉津」といった地名もヒットします。
これが近いか、といわれると微妙なのですがその言葉に近い(1文字違いなど)言葉を検索してくれるようです。

どこまで使えるか不明なのですが、チルダの値の間に数字を指定してあいまいさの調整ができるようです。


town:杉並~0.8

数字は0から1の間で指定するようでデフォルトは0.5だそうです。


・ キャレット(^)


検索キーワードのあとにキャレットをつけるとその文字を重要性を強調させることができます。
こちらの例はマニュアルから参照しますが、


jakarta^4 apache

というクエリを投げた場合、Apacheよりjakartaという文字が含まれるデータのほうが重視されます。


・ ダブルコーテーション(")


ダブルコーテーションは文章を指定できます。
特にスペースで区切られた文字列を検索したい場合などに有効です。


"jakarta apache" OR "Apache Lucene"

といった感じで、単語単語ではなくその言葉で検索したい場合に指定します。


・ その他


「:」はそもそも条件の区切りに指定する特殊文字になります。
また、「\」は後述もしますが、これらの特殊文字をエスケープするために指定する文字です。



Apache Solrの特殊文字をエスケープする


マニュアルにも記載があるのですが、前述した特殊文字をクエリ内で使いたい場合はエスケープする必要があります。
エスケープは\マークをその特殊文字の前に指定します。


\(1\+1\)\:2

「(1+1):2」という文字を検索したい場合は、上記のようにエスケープしてクエリに投げます。

最後にPHPでこれらSolrの特殊文字をエスケープする処理が下記サイトにまとめられていました。


Escaping Characters in a Solr Query / Solr URL @ Mats Lindh


ただ、これだとstr_replace()使うんじゃなくてaddcslashes()を使ってもいいんじゃないかと思ったりして、そのパターンの例も載せておきます。


static public function escapeSolrValue($string)
{
    $string = addcslashes($string, "\\+-&|!(){}[]^~*?:\";");
    return $string;
}


検索機能を提供する場合、これら特殊文字をユーザーに指定させるというような使い方はさせないと思いますので、しっかりとこの辺の文字を処理しておかないとクエリがエラーとなって問題が起きることになりそうです。