首页 > 技术文章 > Elasticsearch中文分词加拼音

gomvc 2019-10-31 10:32 原文

网上可能有很多教程,我写这个只是记录一下自己学习的过程,给自己看的 。

中文分司网上搜了一下,用的IK分词器(https://github.com/medcl/elasticsearch-analysis-ik),拼音搜索插件用的是拼音分词器(https://github.com/medcl/elasticsearch-analysis-pinyin)。

IK分词器有两种分词模式:ik_max_word和ik_smart模式。

1、ik_max_word

会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。

2、ik_smart
会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。

 

安装IK分词器

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.1/elasticsearch-analysis-ik-7.4.1.zip

  我的es是7.4所以装了7.4版本

 

安装拼音分司器

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.4.1/elasticsearch-analysis-pinyin-7.4.1.zip

  

按装好分司器后,需在创建索引时指定所用的分词器。以下是PHP的示例代码

    /**
     * 获取创建索引时的分词配置信息
     * @param $indexName 索引名称
     * @return array
     */
    protected function getIndexSettings($indexName)
    {
        return [
            'index' => $indexName,
            'body' => [
                "settings" => [
                    "number_of_shards" => 1,
                    "number_of_replicas" => 1,
                    'analysis' => [
                        'analyzer' =>
                            [
                                'ik_pinyin_analyzer' =>
                                    [
                                        'type' => 'custom',
                                        'tokenizer' => 'ik_smart',
                                        'filter' => ['my_pinyin', 'word_delimiter',]
                                    ]
                            ],
                        'filter' =>
                            [
                                'my_pinyin' =>
                                    [
                                        'type' => 'pinyin',
                                        'keep_separate_first_letter' => false,
                                        'keep_full_pinyin' => true,
                                        'keep_original' => false,
                                        'limit_first_letter_length' => 10,
                                        'lowercase' => true,
                                        'remove_duplicated_term' => true
                                    ]
                            ]
                    ],
                ],
            ],
        ];
    }

public function createTestIndex()
{
        $client=$this->getElasticClient();
        //取索引的配置信息,创建的索引名称为test
        $settings = $this->getIndexSettings('test');
        //创建索引
        $response = $client->indices()->create($settings);
        return $response;
}

  

创建完索引后再创建映射。

{
    "index":"test",
    "body":{
        "news":{
            "_source":{
                "enabled":true
            },
            "properties":{
                "title":{
                    "type":"text"
                },
                "content":{
                    "type":"text"
                },
                "author":{
                    "type":"keyword"
                }
            }
        }
    }
}

  上面是创建映射的json(请求体),news是类型名称,网上看了很多,都是这种结构,我反复试了很多次,都没有成功。不知道怎么回事。可能是7.4版本有些不一样吧。最后改成了下面的结构:

{
    "index":"test",
    "body":{
        "_source":{
            "enabled":true
        },
        "properties":{
            "title":{
                "type":"text"
            },
            "content":{
                "type":"text"
            },
            "author":{
                "type":"keyword"
            }
        }
    }
}

  type:text 表示对该字段做全文索引。(analyzed)

  type:keyword 索引这个字段,使之可以被搜索,但是索引内容和指定值一样 (not_analyzed)

php代码如下:

   public function createMap()
    {
        $client=$this->getElasticClient();
        $params = [
            'index' => 'test',
            'body' => [
                '_source' => [
                    'enabled' => true
                ],
                'properties' => [
                    'title' => [
                        'type' => 'text'
                    ],
                    'content' => [
                        'type' => 'text'
                    ],
                    'author'=>[
                        'type'=>'keyword'

                    ]
                ]
            ]

        ];

        $response = $client->indices()->putMapping($params);
        return $response;
    }

  接下来索引一个文档,看能否用中文和拼音搜索

 public function index()
    {
        $client = $this->getElasticClient();
        $indexName = $this->request->query('index', 'test');

        $params = [
            'type' => '_doc',
            'index' => $indexName,
            'body' => 
                [
                'title' => '英国通过“12月12日提前大选”的议案',
                'content' => '当地时间29号晚上大约20:20左右、北京时间大约30日凌晨4:20,英国议会下院表决通过了首相鲍里斯·约翰逊提出的12月12日提前大选的简短议案。由于此前工党决定改变反对提前大选的立场,不出外界所料,约翰逊提出的“12月12日大选”的“一句话”议案在英国议会在英国议会下院以438票支持、20反对的表决结果,顺利获得通过。'
                ]
        ];
        $response = $client->index($params);

        return $response;
    }

  测试一下是否可以搜索得到:

POST:/test/_search
{
    "query": {
        "match": {
            "title": "英国"
        }
    }
}

  虽然能搜到,并不能说明就是按我设置的中文分司进行的分司。如果是默认的分把每个汉字拆开索引,也可以搜索得到。我又试了一拼音,发现什么也没有搜到。说明我的分词没有起做用。仔细想想,查查文档,发现在创建映射时,可以指定分词,于时删了索引,重建,在建映射时加入分司的设置:

{
    "index":"test",
    "body":{
        "_source":{
            "enabled":true
        },
        "properties":{
            "title":{
                "type":"text",
                "analyzer":"ik_pinyin_analyzer"
            },
            "content":{
                "type":"text",
                "analyzer":"ik_pinyin_analyzer"
            },
            "author":{
                "type":"keyword"
            }
        }
    }
}

  PHP代码如下:

public function createMap()
    {
        $client = $this->getElasticClient();
        $params = [
            'index' => 'test',
            'body' => [
                '_source' => [
                    'enabled' => true
                ],
                'properties' => [
                    'title' => [
                        'type' => 'text',
                        "analyzer" => "ik_pinyin_analyzer"
                    ],
                    'content' => [
                        'type' => 'text',
                        "analyzer" => "ik_pinyin_analyzer"
                    ],
                    'author' => [
                        'type' => 'keyword'

                    ]
                ]
            ]

        ];

        $response = $client->indices()->putMapping($params);
        return $response;
    }

  然后索引一段文章,再搜索试一下,发现一切正常,可以用拼音搜索了。

推荐阅读