はじめに
Solr には Result Grouping という機能があり、検索結果を何らかの条件でグループ化できます。Solr リファレンスガイドで挙げられている例が Result Grouping の動作を知るにはいまいちな感じだったので、別のドキュメントを使って試してみました。
準備
サンプルとして大阪の施設情報を利用 します。
デフォルトの configset でコレクション osaka_shisetsu を作成し、施設情報のデータを投入します。
$ cd server/solr/configsets
$ cp -r _default osaka_shisetsu
$ ../../scripts/cloud-scripts/zkcli.sh -zkhost localhost:9983 -cmd upconfig -confdir osaka_shisetsu/conf -confname osaka_shisetsu
$ curl -s 'http://localhost:8983/solr/admin/collections?action=CREATE&name=osaka_shisetsu&numShards=2&maxShardsPerNode=2&replicationFactor=2&collection.configName=osaka_shisetsu'
$ curl 'http://localhost:8983/solr/osaka_shisetsu/update?commit=true&indent=true' --data-binary @/tmp/data.json -H 'Content-Type: application/json'
$ curl -s 'http://localhost:8983/solr/osaka_shisetsu/select?q=*%3A*&rows=2'
{
"responseHeader":{
"zkConnected":true,
"status":0,
"QTime":15,
"params":{
"q":"*:*",
"rows":"2"}},
"response":{"numFound":9238,"start":0,"maxScore":1.0,"numFoundExact":true,"docs":[
{
"id":"10",
"type":["官公庁"],
"area":["住之江区"],
"name":["軽自動車検査協会大阪主管事務所"],
"address":["住之江区南港東3-4-62"],
"address_p":"34.6164938333333,135.438210722222",
"_version_":1692768899944153088},
{
"id":"11",
"type":["官公庁"],
"area":["住之江区"],
"name":["大阪陸運支局なにわ自動車検査登録事務所"],
"address":["住之江区南港東3-1-14"],
"address_p":"34.6190439722222,135.442191833333",
"_version_":1692768899950444544}]
}}
グルーピング検索
施設名に「事務所」を含むものをエリアでグループ化する検索を実行します。
$ curl -s 'http://localhost:8983/solr/osaka_shisetsu/select?group.field=area_str&group=true&q=name%3A%E4%BA%8B%E5%8B%99%E6%89%80'
{
"responseHeader":{
"zkConnected":true,
"status":500,
"QTime":12,
"params":{
"q":"name:事務所",
"group.field":"area_str",
"group":"true"}},
"error":{
"metadata":[
"error-class","org.apache.solr.common.SolrException",
"root-error-class","org.apache.solr.client.solrj.impl.BaseHttpSolrClient$RemoteSolrException"],
"msg":"org.apache.solr.client.solrj.SolrServerException: No live SolrServers available to handle this request:[http://127.0.1.1:8983/solr/osaka_shisetsu_shard2_replica_n6, http://127.0.1.1:7574/solr/osaka_shisetsu_shard2_replica_n4, http://127.0.1.1:8983/solr/osaka_shisetsu_shard1_replica_n2]",
(略)
"code":500}}
500エラーになってしまいました。
solr.log のエラーメッセージから、グループ化の対象のフィールドが multi-valued となっているのが原因であることが分かりました。
2021-02-26 13:59:29.828 ERROR (qtp691691381-15) [c:osaka_shisetsu s:shard2 r:core_node7 x:osaka_shisetsu_shard2_replica_n4] o.a.s.h.RequestHandlerBase java.lang.IllegalStateException: unexpected docvalues type SORTED_SET for field 'area_str' (expected=SORTED). Re-index with correct docvalues type.
at org.apache.lucene.index.DocValues.checkField(DocValues.java:317)
at org.apache.lucene.index.DocValues.getSorted(DocValues.java:369)
at org.apache.lucene.search.grouping.TermGroupSelector.setNextReader(TermGroupSelector.java:57)
at org.apache.lucene.search.grouping.FirstPassGroupingCollector.doSetNextReader(FirstPassGroupingCollector.java:349)
リファレンスにも確かに single-valued で indexed なフィールドでないといけないと書かれていました。
group.field
The name of the field by which to group results. The field must be single-valued, and either be indexed or a field type that has a value source and works in a function query, such as ExternalFileField. It must also be a string-based field, such as StrField or TextField
そこで、ダイナミックフィールド *_str の定義を multi-valued な strings 型から single-valued な string 型に変更してインデックスを作り直しました。
<fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true" />
<fieldType name="strings" class="solr.StrField" sortMissingLast="true" multiValued="true" docValues="true" />
<dynamicField name="*_str" type="strings" stored="false" docValues="true" indexed="false" useDocValuesAsStored="false"/>
改めて検索を実行します。
$ curl -s 'http://localhost:8983/solr/osaka_shisetsu/select?group.field=area_str&group=true&rows=3&q=name%3A%E4%BA%8B%E5%8B%99%E6%89%80'
{
"responseHeader":{
"zkConnected":true,
"status":0,
"QTime":27,
"params":{
"q":"name:事務所",
"rows":"3",
"group.field":"area_str",
"group":"true"}},
"grouped":{
"area_str":{
"matches":685,
"groups":[{
"groupValue":"浪速区",
"doclist":{"numFound":6,"start":0,"maxScore":5.533971,"numFoundExact":true,"docs":[
{
"id":"23",
"type":["官公庁"],
"area":["浪速区"],
"name":["難波年金事務所"],
"address":["浪速区敷津東1-6-16"],
"address_p":"34.6588191388888,135.49922225",
"_version_":1692817725524541443}]
}},
{
"groupValue":"西区",
"doclist":{"numFound":15,"start":0,"maxScore":5.533971,"numFoundExact":true,"docs":[
{
"id":"37",
"type":["官公庁"],
"area":["西区"],
"name":["堀江年金事務所"],
"address":["西区北堀江3-10-1"],
"address_p":"34.6750045555555,135.488194555555",
"_version_":1692817725525590019}]
}},
{
"groupValue":"東成区",
"doclist":{"numFound":13,"start":0,"maxScore":5.533971,"numFoundExact":true,"docs":[
{
"id":"78",
"type":["官公庁"],
"area":["東成区"],
"name":["今里年金事務所"],
"address":["東成区大今里西2丁目1番8号"],
"address_p":"34.6714081388888,135.539616805555",
"_version_":1692817725528735750}]
}}]}}}
名前に「事務所」を含む施設がエリア名でグループ化されて、それぞれのグループの検索結果が6件、15件、13件であることが分かります。
rows を指定することでグループ数を、group.limit を指定することでグループ毎のドキュメントの件数を変更できます。
$ curl -s 'http://localhost:8983/solr/osaka_shisetsu/select?group.field=area_str&group=true&rows=2&group.limit=3&q=name%3A%E4%BA%8B%E5%8B%99%E6%89%80'
{
"responseHeader":{
"zkConnected":true,
"status":0,
"QTime":30,
"params":{
"q":"name:事務所",
"group.limit":"3",
"rows":"2",
"group.field":"area_str",
"group":"true"}},
"grouped":{
"area_str":{
"matches":685,
"groups":[{
"groupValue":"浪速区",
"doclist":{"numFound":6,"start":0,"maxScore":5.533971,"numFoundExact":true,"docs":[
{
"id":"23",
"type":["官公庁"],
"area":["浪速区"],
"name":["難波年金事務所"],
"address":["浪速区敷津東1-6-16"],
"address_p":"34.6588191388888,135.49922225",
"_version_":1692817725524541443},
{
"id":"552",
"type":["学校・保育所"],
"area":["浪速区"],
"name":["大阪市立広田保育所"],
"address":["浪速区日本橋西2-8-11"],
"address_p":"34.6561468333333,135.50211125",
"_version_":1692817725561241605},
{
"id":"3653",
"type":["警察・消防"],
"area":["浪速区"],
"name":["浪速消防署立葉出張所"],
"address":["浪速区桜川2-14-12"],
"address_p":"34.6636563055555,135.489044111111",
"_version_":1692817725818142729}]
}},
{
"groupValue":"西区",
"doclist":{"numFound":15,"start":0,"maxScore":5.533971,"numFoundExact":true,"docs":[
{
"id":"37",
"type":["官公庁"],
"area":["西区"],
"name":["堀江年金事務所"],
"address":["西区北堀江3-10-1"],
"address_p":"34.6750045555555,135.488194555555",
"_version_":1692817725525590019},
{
"id":"200",
"type":["官公庁"],
"area":["西区"],
"name":["環境局河川事務所"],
"address":["西区新町4-20-3"],
"address_p":"34.6778531944444,135.483258083333",
"_version_":1692817725531881486},
{
"id":"95",
"type":["官公庁"],
"area":["西区"],
"name":["なにわ西府税事務所"],
"address":["西区本田1-6-16"],
"address_p":"34.6784530277777,135.479935888888",
"_version_":1692817725529784322}]
}}]}}}