为Elasticsearch添加中文分词,对比分词器效果

Elasticsearch中,内置了很多分词器(analyzers),例如standard (标准分词器)、english (英文分词)和chinese (中文分词)。其中standard 就是无脑的一个一个词(汉字)切分,所以适用范围广,但是精准度低;english 对英文更加智能,可以识别单数负数,大小写,过滤stopwords(例如“the”这个词)等;chinese 效果很差,后面会演示。这次主要玩这几个内容:安装中文分词ik,对比不同分词器的效果,得出一个较佳的配置。关于Elasticsearch,之前还写过两篇文章:Elasticsearch的安装,运行和基本配置 和 备份和恢复,需要的可以看下。


安装中文分词ik

Elasticsearch的中文分词很烂,所以我们需要安装ik。首先从github上下载项目,解压:

cd /tmp
wget https://github.com/medcl/elasticsearch-analysis-ik/archive/master.zip
unzip master.zip
cd elasticsearch-analysis-ik/

然后使用mvn package 命令,编译出jar包 elasticsearch-analysis-ik-1.4.0.jar。

mvn package

将jar包复制到Elasticsearch的plugins/analysis-ik 目录下,再把解压出的ik目录(配置和词典等),复制到Elasticsearch的config 目录下。然后编辑配置文件elasticsearch.yml ,在后面加一行:

index.analysis.analyzer.ik.type : "ik"

重启service elasticsearch restart 。搞定。

如果上面的mvn搞不定的话,你可以直接从 elasticsearch-rtf 项目中找到编译好的jar包和配置文件(我就是怎么干的)。

2014-12-14晚更新,今天是星期天,我在vps上安装ik分词,同样的步骤,总是提示MapperParsingException[Analyzer [ik] not found for field [cn]],然后晚上跑到公司,发现我公司虚拟机上Elasticsearch的版本是1.3.2,vps上是1.3.4,猜是版本问题,直接把vps重新安装成最新的1.4.1,再安装ik,居然ok了……】


准备工作:创建索引,录入测试数据

先为后面的分词器效果对比做好准备,我的Elasticsearch部署在虚拟机 192.168.159.159:9200 上的,使用chrome的postman插件直接发http请求。第一步,创建index1 索引:

PUT http://192.168.159.159:9200/index1
{
  "settings": {
     "refresh_interval": "5s",
     "number_of_shards" :   1, // 一个主节点
     "number_of_replicas" : 0 // 0个副本,后面可以加
  },
  "mappings": {
    "_default_":{
      "_all": { "enabled":  false } // 关闭_all字段,因为我们只搜索title字段
    },
    "resource": {
      "dynamic": false, // 关闭“动态修改索引”
      "properties": {
        "title": {
          "type": "string",
          "index": "analyzed",
          "fields": {
            "cn": {
              "type": "string",
              "analyzer": "ik"
            },
            "en": {
              "type": "string",
              "analyzer": "english"
            }
          }
        }
      }
    }
  }
}

为了方便,这里的index1 索引,只有一个shards,没有副本。索引里只有一个叫resource 的type,只有一个字段title ,这就足够我们用了。title 本身使用标准分词器,title.cn 使用ik分词器,title.en 自带的英文分词器。然后是用bulk api批量添加数据进去:

POST http://192.168.159.159:9200/_bulk
{ "create": { "_index": "index1", "_type": "resource", "_id": 1 } }
{ "title": "周星驰最新电影" }
{ "create": { "_index": "index1", "_type": "resource", "_id": 2 } }
{ "title": "周星驰最好看的新电影" }
{ "create": { "_index": "index1", "_type": "resource", "_id": 3 } }
{ "title": "周星驰最新电影,最好,新电影" }
{ "create": { "_index": "index1", "_type": "resource", "_id": 4 } }
{ "title": "最最最最好的新新新新电影" }
{ "create": { "_index": "index1", "_type": "resource", "_id": 5 } }
{ "title": "I'm not happy about the foxes" }
 

注意bulk api要“回车”换行,不然会报错。


各种比较

1、对比ik分词,chinese分词和standard分词

POST http://192.168.159.159:9200/index1/_analyze?analyzer=ik
联想召回笔记本电源线

ik测试结果:

{
    "tokens": [
        {
            "token": "联想",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "召回",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "笔记本",
            "start_offset": 4,
            "end_offset": 7,
            "type": "CN_WORD",
            "position": 3
        },
        {
            "token": "电源线",
            "start_offset": 7,
            "end_offset": 10,
            "type": "CN_WORD",
            "position": 4
        }
    ]
}

自带chinese和standard分词器的结果:

{
    "tokens": [
        {
            "token": "联",
            "start_offset": 0,
            "end_offset": 1,
            "type": "<IDEOGRAPHIC>",
            "position": 1
        },
        {
            "token": "想",
            "start_offset": 1,
            "end_offset": 2,
            "type": "<IDEOGRAPHIC>",
            "position": 2
        },
        {
            "token": "召",
            "start_offset": 2,
            "end_offset": 3,
            "type": "<IDEOGRAPHIC>",
            "position": 3
        },
        {
            "token": "回",
            "start_offset": 3,
            "end_offset": 4,
            "type": "<IDEOGRAPHIC>",
            "position": 4
        },
        {
            "token": "笔",
            "start_offset": 4,
            "end_offset": 5,
            "type": "<IDEOGRAPHIC>",
            "position": 5
        },
        {
            "token": "记",
            "start_offset": 5,
            "end_offset": 6,
            "type": "<IDEOGRAPHIC>",
            "position": 6
        },
        {
            "token": "本",
            "start_offset": 6,
            "end_offset": 7,
            "type": "<IDEOGRAPHIC>",
            "position": 7
        },
        {
            "token": "电",
            "start_offset": 7,
            "end_offset": 8,
            "type": "<IDEOGRAPHIC>",
            "position": 8
        },
        {
            "token": "源",
            "start_offset": 8,
            "end_offset": 9,
            "type": "<IDEOGRAPHIC>",
            "position": 9
        },
        {
            "token": "线",
            "start_offset": 9,
            "end_offset": 10,
            "type": "<IDEOGRAPHIC>",
            "position": 10
        }
    ]
}

结论不必多说,对于中文,官方的分词器十分弱。

2、搜索关键词“最新”和“fox”

测试方法:

POST http://192.168.159.159:9200/index1/resource/_search
{
  "query": {
    "multi_match": {
      "type":     "most_fields", 
      "query":    "最新",
      "fields": [ "title", "title.cn", "title.en" ]
    }
  }
}

我们修改queryfields 字段来对比。

1)搜索“最新”,字段限制在title.cn 的结果(只展示hit部分):

"hits": [
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "1",
        "_score": 1.0537746,
        "_source": {
            "title": "周星驰最新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "3",
        "_score": 0.9057159,
        "_source": {
            "title": "周星驰最新电影,最好,新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "4",
        "_score": 0.5319481,
        "_source": {
            "title": "最最最最好的新新新新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "2",
        "_score": 0.33246756,
        "_source": {
            "title": "周星驰最好看的新电影"
        }
    }
]

再次搜索“最新”,字段限制在titletitle.en 的结果(只展示hit部分):

"hits": [
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "4",
        "_score": 1,
        "_source": {
            "title": "最最最最好的新新新新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "1",
        "_score": 0.75,
        "_source": {
            "title": "周星驰最新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "3",
        "_score": 0.70710677,
        "_source": {
            "title": "周星驰最新电影,最好,新电影"
        }
    },
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "2",
        "_score": 0.625,
        "_source": {
            "title": "周星驰最好看的新电影"
        }
    }
]

结论:如果没有使用ik中文分词,会把“最新”当成两个独立的“字”,搜索准确性低。

2)搜索“fox”,字段限制在titletitle.cn ,结果为空,对于它们两个分词器,fox和foxes不同。再次搜索“fox”,字段限制在title.en ,结果如下:

"hits": [
    {
        "_index": "index1",
        "_type": "resource",
        "_id": "5",
        "_score": 0.9581454,
        "_source": {
            "title": "I'm not happy about the foxes"
        }
    }
]

结论:中文和标准分词器,不对英文单词做任何处理(单复数等),查全率低。


我的最佳配置

其实最开始创建的索引已经是最佳配置了,在title 下增加cnen 两个fields,这样对中文,英文和其他什么乱七八糟文的效果都好点。就像前面说的,title 使用标准分词器,title.cn 使用ik分词器,title.en 使用自带的英文分词器,每次搜索同时覆盖。


-学习的比较浅,又不对的地方,欢迎留言-



标签: ,

  1. 博主请教个问题:mvn package 时暴错,请问下载下来的 IK 需要修改pom.xml吗?
    执行mvc package 说pom格式不正确

  2. KeenWon好,請問給index1 mapping 的時候, title多了en和cn字段,這個屬於elasticsearch的什麼特性呢?google了很久,沒有找到相關的資料。

  3. 我照着试了一下,中文分词没问题,但是英文分词搜索无结果,不知道什么原因?直接用english分词结果是没问题的,但是对title.en进行搜索,总是返回空?

    • 自己写了一个小工具,用来把mongo的数据导入到es中:https://github.com/keenwon/mongodb-to-elasticsearch

      • 好的谢谢博主。还有问题:请问这个工具还有数据同步的上线?然后中文分词可以在mongodb中搜索中文吗

        • 1、上线?上限?没有上限?就是个简单的读取mongo插入到es的工具
          2、据我了解mongo没有中文分词,所以才把mongo中的某些数据导入到es中,在es中搜索

  4. 您好 我刚接触es 在用bulk 的时候 一直不成功 ,能帮我看看问题出在哪里么?
    curl -X POST ‘localhost:9200/zzy-index-1/zzy-type-1/_bulk?pretty’ -d ‘{“create”:{}}n{“t1″:”2″}n’

    这个命令执行报错 :
    {
    “error” : {
    “root_cause” : [ {
    "type" : "action_request_validation_exception",
    "reason" : "Validation Failed: 1: no requests added;"
    } ],
    “type” : “action_request_validation_exception”,
    “reason” : “Validation Failed: 1: no requests added;”
    },
    “status” : 400
    }

无觅相关文章插件,快速提升流量