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脚本做了些改动,实现了重建索引的功能,基本思路是这样的:

  1. 建立一个新的index
  2. 插入一条数据到新index中
  3. 修改elasticsearch默认生成的mapping
  4. 删除上步插入的记录(保留mapping)
  5. 通过scroll_search从旧index中读出数据到新index中。
  6. 删除旧index,然后重建
  7. 插入一条数据到旧index中
  8. 修改elasticsearch默认生成的mapping
  9. 删除上步插入的记录(保留mapping)
  10. 通过scroll_search从新index中读出数据到旧index中

这样,每个文档的title字段就有两个类型了,一个用于搜索,一个用于排序。

相关推荐