ElasticSearch 在string类型的字段上做排序出错的解决方案
问题
有个简单的需求:列出图书信息,并按照图书标题的字典顺序排列。
原来认为会很简单:
builder.addSort(SortBuilders.fieldSort("title").order(SortOrder.ASC));
没想到确报这样的异常:
Can’t sort on string types with more than one value per doc, or more than one token per field
原因分析
根据异常信息的提示,有两个可能:
1) 文档里有两个string类型排序的字段
2) 排序的字段里有多个”token“
检查索引的文档,没有发现有多个”title“,那只能是第二个原因了。
一个字符串类型的字段,在mapping里通常是这样的:
"title":{ "type":"string" }
并没有制定要不要做tokenize,不过看起来Elasticsearch默认是做了tokenize的。这也符合常理,毕竟是要在这个字段上做搜索的。但是做排序的需要也要满足。
还好已经有人提供解决方案了:对title 做 multi mapping (http://blog.wiercinski.net/2011/uncategorized/elasticsearch-sorting-on-string-types-with-more-than-one-value-per-doc-or-more-than-one-token-per-field/)
具体来说就是更改文档的mapping 为:
"title" : { "type" : "multi_field", "fields" : { "title" : { "type" : "string" }, "untouched" : { "type" : "string", "index" : "not_analyzed", "include_in_all" : false } } }
在做排序的时候需要调整为:
builder.addSort(SortBuilders.fieldSort("title.untouched").order(SortOrder.ASC));
后续问题
到此问题应该就已经解决,可是重新调用功能,还是没有按照字典顺序排列。
怀疑是需要重新建立索引的问题。
github上有线程的插件可以重建索引:https://github.com/karussell/elasticsearch-reindex
可惜我使用下来并不顺利。
根据网上的php脚本做了些改动,实现了重建索引的功能,基本思路是这样的:
- 建立一个新的index
- 插入一条数据到新index中
- 修改elasticsearch默认生成的mapping
- 删除上步插入的记录(保留mapping)
- 通过scroll_search从旧index中读出数据到新index中。
- 删除旧index,然后重建
- 插入一条数据到旧index中
- 修改elasticsearch默认生成的mapping
- 删除上步插入的记录(保留mapping)
- 通过scroll_search从新index中读出数据到旧index中
这样,每个文档的title字段就有两个类型了,一个用于搜索,一个用于排序。