Elasticsearch 的配置与使用,为了全文搜索

file

最近公司项目要使用全文搜索引擎,之前使用过的 sphInx ,似乎没有那么好用了,而且中文分词也没有合适的 ,所以准备换个其它的来试试,老项目使用的是 thinkphp 3.1 框架,虽然框架老了点。但是新的想法还是可以用上的,这里只是简单演示下 elasticsearch 的上手体难,实际项目中还需要完善。

Elasticsearch 安装

因本文环境为 Laradock ,所以直接使用 elasticsearch的镜像即可,这里省略了 java 环境的安装及 elasticsearch 软件的安装,网上教程很多,请自行查找,后期会补上一个,这里默认已经安装好了。
浏览器打开 http://localhost:9200/ 或者终端执行

curl 'http://localhost:9200/?pretty'

你会看到如下响应

{
        name: "g2ODObY",
        cluster_name: "laradock-cluster",
        cluster_uuid: "w8Hhov2bQDi_Wo2DEx044Q",
        version: {
        number: "6.2.3",
        build_hash: "c59ff00",
        build_date: "2018-03-13T10:06:29.741383Z",
        build_snapshot: false,
        lucene_version: "7.2.1",
        minimum_wire_compatibility_version: "5.6.0",
        minimum_index_compatibility_version: "5.0.0"
        },
        tagline: "You Know, for Search"
}

如果响应正常显示,说明你安装成功了。
这里需注意一点,查看elasticsearch配置:vi config/elasticsearch.yml

network.host: 0.0.0.0  //这里默认不绑定,但安全起见,在实际项目中,请绑定本机 IP。

Elasticsearch 中文插件

这里使用的是 analysis-ik 中文插件,项目地址,需根据不同的 Elasticsearch 版本选择插本版本,本项目使用的最新的 6.2.3 版本。
进入 elasticsearch 目录

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.3/elasticsearch-analysis-ik-6.2.3.zip

查看是否安装成功(注意你的 elasticsearch 版本,版本不同命令不同)

./bin/elasticsearch-plugin list

返回结果

analysis-ik
ingest-geoip
ingest-user-agent
...

看到 analysis-ik 证明你插件安装成功了,你也可以到 plugins 目录下查看插件是否存在

$ cd plugins 

drwxr-xr-x  2 root          root 4096 Apr 17 03:08 analysis-ik
drwxrwxr-x  2 elasticsearch root 4096 Mar 13 11:35 ingest-geoip
drwxrwxr-x  2 elasticsearch root 4096 Mar 13 11:35 ingest-user-agent
drwxrwxr-x 11 elasticsearch root 4096 Mar 13 11:35 x-pack

Elasticsearch 索引的使用

Thinkphp 没有 laravel 那么方便,但是用个 trait 还是可以的。
新建一个traits

<?php
use Elasticsearch\ClientBuilder;
trait Elastic
{
    private $client;
    public function __construct()
    {
        $hosts=[
            env('ELASTICSEARCH_URL','localhost:9200')
        ];
        $this->client = ClientBuilder::create()
            ->setHosts($hosts)  //因 docker的特殊性,这里需指定 IP 地址,env 文件中配置
            ->build();
    }
}        

laravelenv 实现其它是用的 vlucas/phpdotenv,所以我在 Thinkphp中也把他拿了过来。
这里首先实例化一个 client

创建索引

        $params = [
            'index' => $index,//索引名(相当于mysql的数据库)
            'body' => [
                'settings' => [
                    'number_of_shards' => 1, //一个索引中含有的主分片的数量
                    'number_of_replicas' => 0 //每一个主分片关联的副本分片的数量
                ],
                'mappings' => [
                    $type => [  //类型名(相当于mysql的表)
                        '_all'=>[   //  是否开启所有字段的检索
                            'enabled' => 'false'
                        ],
                        '_source' => [ //  存储原始文档
                            'enabled' => true
                        ],
                        'properties' => [   //文档类型设置(相当于mysql的数据类型)
                            'id' => [
                                'type' => 'integer', // //类型 string、integer、float、double、boolean、date,text,keyword
                                //'index'=> 'not_analyzed',//索引是否精确值  analyzed not_analyzed

                            ],
                            'title' => [
                                'type' => 'text', // 字段类型为全文检索,如果需要关键字,则修改为keyword,注意keyword字段为整体查询,不能作为模糊搜索
                                "analyzer"=> "ik_max_word",
                                "search_analyzer"=> "ik_max_word",
                            ],
                            'body'  =>  [
                                'type'  => 'text',
                                "analyzer"=> "ik_max_word",
                                "search_analyzer"=> "ik_max_word",
                            ]
                        ]
                    ]
                ]
            ]
        ];
        return $this->client->indices()->create($params);

这里需要注意的是 analyzer, IK插件目前只支持两种: ik_max_wordik_smart

ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
ik_smart : 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。

返回如下信息说明创建成功

array:3 ["acknowledged" => true
  "shards_acknowledged" => true
  "index" => "my_index"
]

这里使用的 dd打印的结果,之后的结果承现一样用 dd打印。

删除索引

$params = [
            'index' => 'my_index',
        ];
 return $this->client->indices()->delete($params);//删除索引设置

返回如下

array:1 ["acknowledged" => true
]

查看索引设置

// 查看一个索引的设置
$params = ['index' => 'my_index'];
$response = $client->indices()->getSettings($params);

// 查看多个索引的设置
$params = [
    'index' => [ 'my_index', 'my_index2' ]
];
$response = $client->indices()->getSettings($params);

返回信息如下

array:1 ["my_index" => array:1 ["settings" => array:1 ["index" => array:6 ["creation_date" => "1524037463950"
        "number_of_shards" => "1"
        "number_of_replicas" => "0"
        "uuid" => "okYiWK0WRiqebMAHUCsvzA"
        "version" => array:1 ["created" => "6020399"
        ]
        "provided_name" => "my_index"
      ]
    ]
  ]
]

查看 mapping 信息

//获取所有索引和类型的 mapping 信息
$response = $client->indices()->getMapping();

//获取 my_index 索引的所有类型的 mapping
$params = ['index' => 'my_index'];
$response = $client->indices()->getMapping($params);

//获取所有类型为 my_type 的 mapping信息,不管索引是什么
$params = ['type' => 'my_type' ];
$response = $client->indices()->getMapping($params);

//获取 my_index 索引 下 my_type 类型的 mapping 信息
$params = [
    'index' => 'my_index'
    'type' => 'my_type'
];
$response = $client->indices()->getMapping($params);

//获取多个索引的 mapping 信息
$params = [
    'index' => [ 'my_index', 'my_index2' ]
];
$response = $client->indices()->getMapping($params);

返回如下代码

array:1 ["my_index" => array:1 ["mappings" => array:1 ["my_type" => array:2 ["_all" => array:1 ["enabled" => false
        ]
        "properties" => array:3 ["body" => array:2 ["type" => "text"
            "analyzer" => "ik_max_word"
          ]
          "id" => array:1 ["type" => "integer"
          ]
          "title" => array:2 ["type" => "text"
            "analyzer" => "ik_max_word"
          ]
        ]
      ]
    ]
  ]
]

Elasticsearch 的增删改查

增加数据

1.增加单条数据

$data=[
            'title' => '我爱北京天安门',
            'body'  =>  '天安门上太阳升'
        ];
$params = [
            'index' => 'my_index',
            'type' => 'my_type',
           // 'id' => 'my_id', // 不填则会自动生成唯一的id
            'body' => $data
        ];
        return $this->client->index($params);

返回如下

array:8 ["_index" => "my_index"
  "_type" => "my_type"
  "_id" => "WKXd12IBwuLBOSSKe5k_" //当前数据的唯一id
  "_version" => 1
  "result" => "created"
  "_shards" => array:3 ["total" => 2
    "successful" => 1
    "failed" => 0
  ]
  "_seq_no" => 0
  "_primary_term" => 1
]

2.批量增加多条数据

$dataList =[
            [
                'id'    =>  '10001',
                'title' => '北京',
                'body' => '我们是首都',

            ],[
                'id'    =>  '10002',
                'title' => '上海',
                'body' => '啊啦是上海人',
            ],[
                'id'    =>  '10003',
                'title' => '广州',
                'body' => '我们有小蛮腰',

            ],[
                'id'    =>  '10004',
                'title' => '深圳',
                'body' => '我们啥也没有,来了就是深圳人。',
            ],
        ];

foreach($dataList as $value){
    $params['body'][] = [
        'index' => [
            '_index' => 'my_index',
            '_type' => 'my_type',
            '_id'  =>$value['id']
        ]
    ];
    $params['body'][] = [
        'id' => $value['id'],
        'title' => $value['title'],
        'body' => $value['body'],
    ];
}
return $this->client->bulk($params);

这里需注意,批量增加多条数据时并不是直接将数组扔进去,而是要进行处理,生成对应的数组后使用 bulk 方法批量创建。
返回如下

array:3 ["took" => 27
  "errors" => false
  "items" => array:4 [0 => array:1 ["index" => array:9 ["_index" => "my_index"
        "_type" => "my_type"
        "_id" => "10001"
        "_version" => 1
        "result" => "created"
        "_shards" => array:3 ["total" => 2
          "successful" => 1
          "failed" => 0
        ]
        "_seq_no" => 1
        "_primary_term" => 1
        "status" => 201
      ]
    ]
    1 => array:1 ["index" => array:9 ["_index" => "my_index"
        "_type" => "my_type"
        "_id" => "10002"
        "_version" => 1
        "result" => "created"
        "_shards" => array:3 ["total" => 2
          "successful" => 1
          "failed" => 0
        ]
        "_seq_no" => 2
        "_primary_term" => 1
        "status" => 201
      ]
    ]
        ...

这里使用了 $dataList 自带的 id,在实际项目中建议使用数据的 id,用做数据的唯一 id,方便通过 id 查询数据。

删除数据

删除文档只能单条删除,需指定数据 ID

$param = [
            'index' => 'my_index',
            'type' => 'my_type',
            'id'    => 'my_id' // 指定数据ID
        ];
        return  $this->client->delete($param);

返回如下

array:1 ["acknowledged" => true
]

查询数据

查询数据需指定数据 ID

$params = [
            'index' => 'my_index',
            'type' => 'my_type',
                            'id' => 'WKXd12IBwuLBOSSKe5k_' // 此 ID 为自动生成的 ID,项目中建议查询手动录入ID
        ];
        return $this->client->get($params);

返回如下

array:6 ["_index" => "my_index"
  "_type" => "my_type"
  "_id" => "WKXd12IBwuLBOSSKe5k_"
  "_version" => 1
  "found" => true
  "_source" => array:2 ["title" => "我爱北京天安门"
    "body" => "天安门上太阳升"
  ]
]

数据修改

$params = [
            'index' => 'my_index',
            'type' => 'my_type',
            'id' => 'WKXd12IBwuLBOSSKe5k_',//指定 id, 这里为之前录入时自动生成的 id
            'body' => [
                'doc' => [  // 必须带上doc.表示是数据操作
                    'age' => 150
                ]
            ]
        ];
return  $this->client->update($params);

返回如下

array:8 ["_index" => "my_index"
  "_type" => "my_type"
  "_id" => "WKXd12IBwuLBOSSKe5k_"
  "_version" => 2
  "result" => "updated"
  "_shards" => array:3 ["total" => 2
    "successful" => 1
    "failed" => 0
  ]
  "_seq_no" => 4
  "_primary_term" => 1
]

再次查询数据

array:6 ["_index" => "my_index"
  "_type" => "my_type"
  "_id" => "WKXd12IBwuLBOSSKe5k_"
  "_version" => 2
  "found" => true
  "_source" => array:3 ["title" => "我爱北京天安门"
    "body" => "天安门上太阳升"
    "age" => 150
  ]
]

发现返回数据中多了 age 字段, 修改成功。

搜索数据

先来个简单的.

$params = [
    'index' => 'my_index', //['my_index1', 'my_index2'],可以通过这种形式进行跨库查询
    'type' => 'my_type',    //['my_type1', 'my_type2'],
    'body' => [
        'query'=>[
            'match'=>[
                "title"    =>  '北京',
            ],
        ],
    ]
];
return  $this->client->search($params);

返回如下

array:4 ["took" => 188
  "timed_out" => false
  "_shards" => array:4 ["total" => 5
    "successful" => 5
    "skipped" => 0
    "failed" => 0
  ]
  "hits" => array:3 ["total" => 2
    "max_score" => 1.6451461
    "hits" => array:2 [0 => array:5 ["_index" => "my_index"
        "_type" => "my_type"
        "_id" => "10001"
        "_score" => 1.6451461
        "_source" => array:3 ["id" => "10001"
          "title" => "北京"
          "body" => "我们是首都"
        ]
      ]
      1 => array:5 ["_index" => "my_index"
        "_type" => "my_type"
        "_id" => "WKXd12IBwuLBOSSKe5k_"
        "_score" => 0.94175816
        "_source" => array:3 ["title" => "我爱北京天安门"
          "body" => "天安门上太阳升"
          "age" => 150
        ]
      ]
    ]
  ]
]

可以看到,title 中包含北京 的数据已经全部返回了。

Elasticsearch 最重要的也是最灵活的就是搜索了,你能想到的方法基本上Elasticsearch 都已经帮你做好,比如:
term,match,multi_matchrange.prefix,wildcard,regexp.fuzzy,match_phrase,match_phrase_prefix,exists等等,了解具体方法的使用请参考:

原文地址:http://www.qiehe.net/posts/4/the-use-and-c...

本作品采用《CC 协议》,转载必须注明作者和本文链接
Good Good Study , Day Day Up!!
本帖由系统于 5年前 自动加精
Jourdon
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 22

@Jourdon
Es确实好用,但是官方包只有一个数组型的参数传入,感觉好费劲,如果再加上各种条件搜索,简直就是灾难。

$params = [
            'index' => 'my_index',
            'type' => 'my_type',
                            'id' => 'WKXd12IBwuLBOSSKe5k_' // 此 ID 为自动生成的 ID,项目中建议查询手动录入ID
        ];
        return $this->client->get($params);

推荐一个更优雅、简单的写法

$build->type('my_type')->index('my_index')->whereTerm('id',$id)->first();

https://github.com/crcms/elasticsearch :smile:

5年前 评论
Jourdon

@itfooter 文档里比较全,可以直接看文档,或者使用别人封装的包,最近发的一个包,挺好用的,推荐一下。https://github.com/fly-studio/laravel-scou...

5年前 评论

已解决ES认证问题,修改请求地址信息即可,把用户认证信息放到URL即可。 ELASTICSEARCH_HOST=http://user:password@URL:port joy: 其实第一天就这么试过了,没成功,估计是IP白名单之类的限制,我还以为这样不行呢。。。。。哭。。。。。

5年前 评论

这个得+1啊,神器,有空去研究下~

5年前 评论
幽弥狂

怎么限制搜索结果的数量???就跟实际的 API 结果需要分页的那种形式。

5年前 评论

@Jourdon 这个是给ES 加认证,我是不知道怎么把认证信息 放到请求里? :joy: :joy:

file
建索引的时候,我是这样实现的。查询和插入等操作, 不知道怎么改造

5年前 评论
Jourdon

@莫无季 这种问题为什么不先尝试百度一下?

file

5年前 评论

想了解一下, laravel 使用ES 怎么进行 http_auth 认证,因为我使用的是阿里云的ES,需要认证,不知道认证信息加哪里?我用的扩展是:"tamayo/laravel-scout-elastic": "^3.0"

5年前 评论
Jourdon

@Arsenal_jie laradock中每个服务分别有自己的容器,你可以理解为每个服务有独立的IP域名,在访问外部时是没有问题的,但是在访问自身的情况下需要注意他们的IP是不同的,也就是说使用curl时需要在hosts中配置域名和IP 才可以使用curl,

5年前 评论

莫非是在workspace里,不访问9200的端口?还是说不能用127.0.0.1:9200这样子的访问方式

5年前 评论

我是在lardocker上开发,为什么我在workspace里跑php artisan es:init的时候,说我curl 127.0.0.1:9200连接失败,但是我已经安装好并且能成功启动了。莫非是在workspace里,访问9200的端口?

5年前 评论
Jourdon

@AGD 这就要看你看重的是什么喽,为了省钱,那就自己写呗,为了方便就用别人写好的。

5年前 评论

@Jourdon 配置要求太高了用不起啊

5年前 评论
Jourdon

@AGD elasticsearch对CPU、硬盘、内存要求会高一些,必竟Lucene会存储和使用大量的文件,有舍才有得的道理我们都懂的。
不过我个人到是觉得配置这个东西不在我们考虑范围里,就像laravel 的性能被人骂得要死,还不是很多人在用?

5年前 评论

elasticsearch 很吃配置吧

5年前 评论
Jourdon

刚刚发现标题的 Elasticsearch 写成了 Elastcisearch ,好尴尬啊。

5年前 评论

多个查询条件怎么使用,有例子吗

5年前 评论
Jourdon

@hiword 这篇文章只是介绍而已,真正使用的时候可以自己封装方法,也可以使用别人封装好的扩展包,社区里很多人分享过,使用时可以像 ORM 一样方便,可以搜索一下

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!