中国語学習×グラフデータベース(9) - 様々な検索

例えば、短文の中からキーワードを含むとか、特定の声調を含む単語みたいの検索するとなると、WHERE句でイコールしか使えないとかいう話では使い物にならない。さすがにCypherでもそのあたりは対応済みという事を整理しておきたい。

○○で始まるシリーズ

例えば、2声のshiという音で始まる単語を検索したいなんて場合は、こうやれば良い。

sw = graph.cypher.execute("MATCH (w:Word) WHERE w.pinyin STARTS WITH ('shi2') return w")
   | w                                                                                                               
----+------------------------------------------------------------------------------------------------------------------
  1 | (n737:Word {keyword:"石窟",meaning:"石窟",pinyin:"shi2 ku1",pinyinf:"shí kū"})                                      
  2 | (n738:Word {keyword:"实施",meaning:"実施する",pinyin:"shi2 shi1",pinyinf:"shí shī"})                                  
  3 | (n739:Word {keyword:"实事求是",meaning:"事実に即して問題を処理する",pinyin:"shi2 shi4 qiu2 shi4",pinyinf:"shí shì qiú shì "})    
  4 | (n740:Word {keyword:"拾金不昧",meaning:"金を拾っても着服しない、拾得物は届け出る",pinyin:"shi2 jin1 bu2 mei4",pinyinf:"shí jīn bú mèi"})
...

WHERE句でSTARTS WITHが使えるのが分かる。もちろんENDS WITHやCONTAINSも使える。そのあたりはドキュメントを参照されたし。

11.3. Where - - The Neo4j Manual v2.3.2

これでも工夫すれば、それなりに使えるのだが、正規表現をかじったことがある人なら、こっちの方が好きなはず(笑)。

正規表現を使った検索

WHERE句の=の代わりに=~を使ってあげると、JAVAのそれに準拠した正規表現を使うことが出来る。例えば、2文字で第1声+第4声という単語を抽出しようと思ったら、こんな感じに書けば良い。orderを使っているのは件数を数えようと思ったから追加した。

regw = graph.cypher.execute("MATCH (w:Word) WHERE w.pinyin =~ '[a-z]*1 [a-z]*4' RETURN w")
regw.to_subgraph().order
470

これには前提が必要で、ピンインの書き方が発音の後ろに声調番号を付加し、なおかつ音の間に半角スペースがあるという前提に基づいているから、こんな表現でOKとなる。もし、母音の上に声調記号をつけたフォーマットで保存している場合は、条件がもっと煩雑になる。

・・・そもそもキーボードから入れづらいでしょw

続いての例は、短文検索で「得了」か「不了」が含まれるモノを探そうとしている。

regs = graph.cypher.execute("MATCH (s:Sentence) WHERE s.sentence =~ '.*(不了|得了).*' RETURN s")
print("{} sentence(s) found in database".format(regs.to_subgraph().order))
32 sentence(s) found in database

.*は0文字以上の任意の文字という表現なのだが、いきなり不了で始まる文はあり得ないと思うので、実際には1文字以上の任意の文字を表す.+でも良いはずだ。

正規表現に関するドキュメントはこちら:
11.3. Where - - The Neo4j Manual v2.3.2

大量検索について

ところで、同じようなパターンだけど、WHERE句で指定している値だけが違うものを何回も繰り返すというケースもあるはず。そこはPythonからpy2neoを経由で呼び出すときに大きなメリットになる。例えば、こんな感じでformat()付きで作っておけば、汎用的に使い回せる。

vallist = ['不了', '得了', ....]
for val in vallist:
    regs = graph.cypher.execute("MATCH (s:Sentence) WHERE s.sentence =~ '.*{0}.*' RETURN s".format(val))
    ...

valの値だけ置き換わって何回でも実行される。ただ、ループの中でSQL(あ、Cypherか)を繰り返す呼び出すのは、パフォーマンスの観点からはやらない方がいいので、正規表現でなくて通常のWHERE = のパターンでやりたいなら、WHERE INを使った方がスマートなはず。こんな感じ。

vallist = ['熬夜', '懊恼', '奥运会']
regs = graph.cypher.execute("MATCH (w:Word) WHERE w.keyword in {0} RETURN w".format(str(vallist))
...

format()ついでに、属性指定の場合で{}を使う場合は、こんな感じで注意が必要。

regs = graph.cypher.execute("MATCH (w:Word {{keyword:'{0}'}}) RETURN w".format('熬夜')
...

format()に無視させるために、{{}}と二重でくくるのと、置き換える部分も'{0}'というようにシングルクオーテーションでくくってやる必要がある。自分の場合は、これに結構ハマったので書いておく。

(もう少しつづく)

グラフ型データベース入門 - Neo4jを使う

グラフ型データベース入門 - Neo4jを使う

グラフデータベース ―Neo4jによるグラフデータモデルとグラフデータベース入門

グラフデータベース ―Neo4jによるグラフデータモデルとグラフデータベース入門

A Programmatic Introduction to Neo4j

A Programmatic Introduction to Neo4j