起こった問題
Solr5.5 で構築した Solr Cloud 上で結果をグルーピングした時に、group.ngroupsの数が結果と一致しないという問題に当たる。Apache Solr Documentationでも、
Distributed Result Grouping Caveats
"group.ngroups and group.facet require that all documents in each group must be co-located on the same shard in order for accurate counts to be returned. Document routing via composite keys can be a useful solution in many situations."
とあり、同じshard内にあるdocumentをグルーピングしたカウント結果を返すことになっている。なので、グルーピングしたいKeyを持つdocumentを同じShardにindexするようにしないといけない。
Solr Cloud のDocument Routing概略
Solr CloudのDocument Routingには、collection作成時にdefaultで設定される"compositeId"と "implicit"の2種類がある。 "implicit"の方は、各Shardに名前をつけて、その名前とindexするfieldの値を使ってShardへのroutingを行うようだ。Shardの名前が決め打ちになり、今回の用途には合わないので、Defaultの"compositeId"でのRoutingを使うことにした。
"compositeId"は、Document ID, つまり uniqueKeyを使ってIndex先のShardを決める。
Shards and Indexing Data in SolrCloud
"If you use the (default) "compositeId" router, you can send documents with a prefix in the document ID which will be used to calculate the hash Solr uses to determine the shard a document is sent to for indexing. The prefix can be anything you'd like it to be (it doesn't have to be the shard name, for example), but it must be consistent so Solr behaves consistently. For example, if you wanted to co-locate documents for a customer, you could use the customer name or ID as the prefix. If your customer is "IBM", for example, with a document with the ID "12345", you would insert the prefix into the document id field: "IBM!12345". The exclamation mark ('!') is critical here, as it distinguishes the prefix used to determine which shard to direct the document to."
"!"で区切って、Document IDの前にPrefixをつければ、同じPrefixを持つDocumentは同じShardにIndexされることになる。方法例として、クエリーに_route_
のパラメータをつけてRoutingする方法が紹介されている。あえてクエリー側で対応せずに、solrconfig側にPrefix付きのDocument IDを生成する設定をして、update, dataimport時に生成するようにしてみた。
solrconfig.xmlへupdateRequestProcessorChainを設定
fieldに、グルーピングしたいKeyとなるgrouping_keyとDocumentのユニークなIDとなるdocument_keyがあるとする。作りたいprefix付のDocumentIDを "grouping_key!document_key" で生成して、id fieldに入れるようにする。そして、id fieldをuniqueKeyに設定しておく。
schemaの設定は、こんな感じ
<field name="document_key" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<field name="grouping_key" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="id" type="string" indexed="true" stored="true" required="false" multiValued="false" />
<uniqueKey>id</uniqueKey>
solrconfig.xmlは、以下の感じ
<requestHandler name="/update" class="solr.UpdateRequestHandler" >
<lst name="defaults">
<str name="update.chain">composite-id</str>
</lst>
</requestHandler>
<updateRequestProcessorChain name="composite-id">
<processor class="solr.CloneFieldUpdateProcessorFactory">
<str name="source">grouping_key</str>
<str name="dest">id</str>
</processor>
<processor class="solr.CloneFieldUpdateProcessorFactory">
<str name="source">document_key</str>
<str name="dest">id</str>
</processor>
<processor class="solr.ConcatFieldUpdateProcessorFactory">
<str name="fieldName">id</str>
<str name="delimiter">!</str>
</processor>
<processor class="solr.LogUpdateProcessorFactory" />
<processor class="solr.RunUpdateProcessorFactory" />
</updateRequestProcessorChain>
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
<str name="update.chain">composite-id</str>
</lst>
</requestHandler>
solr.CloneFieldUpdateProcessorFactoryを2個別々に設定している。最初、以下のように書いてみたが、
<processor class="solr.CloneFieldUpdateProcessorFactory">
<str name="source">grouping_key</str>
<str name="source">document_key</str>
<str name="dest">id</str>
</processor>
生成されたidの値が、"document_key!grouping_key"となってしまって、prefixにしたい値が先頭側にできなかった。色々と試した結果、solr.CloneFieldUpdateProcessorFactoryを個別に書くことで出来た。ここは詳しい仕組みは、まだわかっていない。
dataimport handler側にも、composite-id を設定することで、dataimport実行時にもupdate.chainが実行される。これで、updateクエリーとdataimport実行時に、prefix付きのDocument IDが生成される。
データをインポート後、group.ngroupsを実行して実際の結果と件数を照らし合わせたら、今度は一致した。ひとまず対応できたみたい。
終わりに
Apache Solr Documentationを色々と読み、Solr Document Routingや CompositeIdでググっては英文ドキュメントをなんとか読み下して、上記の方法にたどり着いた。たぶん、update query側で_route_
を付ける方が方法としては簡単です。