今までずっとSolr(というかLucene)で、ワイルドカードによる後方一致はできないものだと思っていたのですが、どうやらそうではないようです。
最近、Solrで後方一致のクエリを投げている人が近くにいて、「使えるの?」と聞いてみたら「動いてますよー」とあっけらかんと返ってきたことで知りました…。
で、なんで後方一致ができないと思っていたかというのは、LuceneのQueryParserが元です。
Note: You cannot use a * or ? symbol as the first character of a search.
http://lucene.apache.org/core/5_4_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Wildcard_Searches
後方一致できないって書いてるやないかー。
現時点、Lucene 5.4.0でも。
で、これに対してWildcardQueryを見ると
Note this query can be slow, as it needs to iterate over many terms. In order to prevent extremely slow WildcardQueries, a Wildcard term should not start with the wildcard *
http://lucene.apache.org/core/5_4_0/core/org/apache/lucene/search/WildcardQuery.html
となっていて、遅いからやめた方がいいよくらいな感じになっています。
どういうことでしょう?
というわけで、ちょっと確認。Luceneで。
WildcardQuery。
it("prefix search, using WildcardQuery") { val analyzer = new StandardAnalyzer val directory = new RAMDirectory val indexWriter = new IndexWriter(directory, new IndexWriterConfig(analyzer)) val document1 = { val d = new Document() d.add(new TextField("name", "foo_bar", Store.YES)) d } val document2 = { val d = new Document() d.add(new TextField("name", "bar_foo", Store.YES)) d } indexWriter.addDocument(document1) indexWriter.addDocument(document2) indexWriter.commit() indexWriter.close() val query = new WildcardQuery(new Term("name", "*foo")) val reader = DirectoryReader.open(directory) val searcher = new IndexSearcher(reader) val topDocs = searcher.search(query, 100, Sort.RELEVANCE) val resultDoc = searcher.doc(topDocs.scoreDocs(0).doc) resultDoc.get("name") should be("bar_foo") directory.close() }
こちらはOKですね。
QueryParser。
it("prefix search, using QueryParser") { val analyzer = new StandardAnalyzer val directory = new RAMDirectory val indexWriter = new IndexWriter(directory, new IndexWriterConfig(analyzer)) val document1 = { val d = new Document() d.add(new TextField("name", "foo_bar", Store.YES)) d } val document2 = { val d = new Document() d.add(new TextField("name", "bar_foo", Store.YES)) d } indexWriter.addDocument(document1) indexWriter.addDocument(document2) indexWriter.commit() indexWriter.close() val queryParser = new QueryParser("name", analyzer) a[ParseException] should be thrownBy queryParser.parse("*_foo") directory.close() }
こっちは失敗するし…。
これに対して、Solrで
q=name:*A
みたいなクエリを投げても、確かにエラーになりません。普通のLuceneのQueryParserではないってことかな…。
気になって書いた検証コードは、こちらに置いています。
https://github.com/kazuhira-r/lucene-examples/tree/master/lucene-wildcard-query-suffix-match