在Laravel项目中使用Elasticsearch

Elasticsearch概念

Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。

Elastic 的底层是开源库 Lucene。但是,你没法直接用 Lucene,必须自己写代码去调用它的接口。Elastic 是 Lucene 的封装,提供了 REST API 的操作接口,开箱即用。

安装Elasticsearch

参考安装Elasticsearch
我使用centos服务器,安装命令如下

# Download and install the public signing key:
$ rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

# 添加 yum 源
$ vim /etc/yum.repos.d/elasticsearch-7.x.repo
# 要添加的内容 ##################################################################
[elasticsearch-7.x] 
name=Elasticsearch repository for  7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1 
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1 
autorefresh=1 
type=rpm-md
###############################################################################

# 安装 es
$ yum install -y elasticsearch

# 管理 es
$ systemctl start elasticsearch.service
$ systemctl stop elasticsearch.service

# 测试 elasticsearch
# 参考 https://www.elastic.co/guide/cn/elasticsearch/guide/current/running-elasticsearch.html
$ curl http://127.0.0.1:9200?pretty 

外网访问Elasticsearch

参考这两篇文章elasticsearch外网访问设置elasticsearch安装及启动异常解决

默认情况下安装elasticsearch之后是无法进行外网访问的,可以通过设置来完成这一目的

1、编辑elasticsearch配置文件:vim /etc/config/elasticsearch.yml

找到network.host这一行,更改为network.host: 0.0.0.0

2、重启服务,发现报错

ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

3、以root账户更改/etc/sysctl.conf文件,添加如下内容

vm.max_map_count=655360

4、以root账户执行下面命令

[***@elk01 ~]$ sudo sysctl -p
vm.max_map_count = 655360

5、再次启动服务,开放9200端口。依然会报错ERROR: [1] bootstrap checks failed
[1]: the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured

6、在 elasticsearch.yml中添加配置项:

bootstrap.memory_lock: false
bootstrap.system_call_filter: false

cluster.initial_master_nodes: [“node-1”]
这个的话,这里的node-1是上面一个默认的记得打开就可以了

7、再次重启服务,成功开启es服务

在我的Laravel项目中使用Elasticsearch

通过网址也可以直接访问elasticsearch,表示我们已经可以成功从外网访问,当然在正式环境中,还是需要配置ip限制访问的,我这边是方便测试,所有暂不限制。

在我的Laravel项目中使用Elasticsearch

Elasticsearch基础概念和使用

内容参考Elasticsearch基础概念全文搜索引擎 Elasticsearch入门教程
Elasticsearch 本质上是一个数据库,但并不是 MySQL 这种关系型数据库,查询语言也不是 SQL,而是 Elasticsearch 自己的一套查询语言。

既然是数据库,有一些概念是互通的,如下表:

MySQL Elasticsearch
数据库(Database) 索引(Index)
表(Table) 类型(Type)
记录(Row) 文档(Document)
字段(Column) 字段(Fields)

创建索引

创建名为test的索引

curl -X PUT http://localhost:9200/test

服务器返回一个 JSON 对象,里面的acknowledged字段表示操作成功。

{"acknowledged":true,"shards_acknowledged":true,"index":"test"}

删除索引

curl -X DELETE 'localhost:9200/test'

服务器返回值为,里面的acknowledged字段表示操作成功

{"acknowledged": true}

查看索引

curl -X GET 'http://localhost:9200/_cat/indices?v'

服务器会列出所有索引

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   test  y6f8oZitQEKE3blusrH_7g   1   1          0            0       208b           208b

新增数据

向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。比如,向/accounts/person发送请求,就可以新增一条人员记录。

curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
  "user": "张三",
  "title": "工程师",
  "desc": "数据库管理"
}'

服务器响应后返回json对象如下:会给出 Index、Type、Id、Version 等信息

{
    "_index":"accounts",
    "_type":"person",
    "_id":"1",
    "_version":1,
    "result":"created",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":0,
    "_primary_term":1
}

如果你仔细看,会发现请求路径是/accounts/person/1,最后的1是该条记录的 Id。它不一定是数字,任意字符串(比如abc)都可以。

新增记录的时候,也可以不指定 Id,这时要改成 POST 请求。

curl -H 'Content-Type: application/json' -X POST 'localhost:9200/accounts/person' -d '
{
  "user": "李四",
  "title": "工程师",
  "desc": "系统管理"
}'

服务器返回json如下:_id字段就是一个随机字符串

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":1,
    "result":"created",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":1,
    "_primary_term":1
}

查看记录

/Index/Type/Id发出 GET 请求,就可以查看这条记录。

curl 'localhost:9200/accounts/person/1?pretty=true'

服务器返回json对象如下:URL 的参数pretty=true表示以易读的格式返回。

返回的数据中,found字段表示查询成功,_source字段返回原始记录。

{
  "_index" : "accounts",
  "_type" : "person",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "user" : "张三",
    "title" : "工程师",
    "desc" : "数据库管理"
  }
}

如果 Id 不正确,就查不到数据,found字段就是false

curl 'localhost:9200/accounts/person/2?pretty=true'

服务器返回json对象如下:

{
  "_index" : "accounts",
  "_type" : "person",
  "_id" : "2",
  "found" : false
}

删除记录

curl -X DELETE 'localhost:9200/accounts/person/0JO3x3MBEaLkjdo6ypYV'

服务器返回json对象如下:

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":2,
    "result":"deleted",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":2,
    "_primary_term":1
}

若删除的数据不存在,我们再执行一次上面的命令

curl -X DELETE 'localhost:9200/accounts/person/0JO3x3MBEaLkjdo6ypYV'

服务器返回json对象如下:result返回结果为not_found

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":1,
    "result":"not_found",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":3,
    "_primary_term":1
}

更新数据

更新记录就是使用 PUT 请求,重新发送一次数据。

curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
  "user": "张三",
  "title": "工程师",
  "desc": "数据库管理,软件开发"
}' 

服务器响应后返回json对象如下:result结果返回updated,表示更新数据成功

{
    "_index":"accounts",
    "_type":"person",
    "_id":"1",
    "_version":2,
    "result":"updated",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":4,
    "_primary_term":1
}

数据查询

返回所有记录

curl 'localhost:9200/accounts/person/_search?pretty=true'

服务器会返回索引类型下的所有数据,返回json如下:

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "person",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user" : "张三",
          "title" : "工程师",
          "desc" : "数据库管理,软件开发"
        }
      }
    ]
  }
}

上面代码中,返回结果的 took字段表示该操作的耗时(单位为毫秒),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下。
返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

全文搜索

Elastic 的查询非常特别,使用自己的查询语法,要求 GET 请求带有数据体。

curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search?pretty=true'  -d '
{
  "query" : { "match" : { "desc" : "软件" }}
}'

服务器返回json对象如下:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "person",
        "_id" : "1",
        "_score" : 0.5753642,
        "_source" : {
          "user" : "张三",
          "title" : "工程师",
          "desc" : "数据库管理,软件开发"
        }
      }
    ]
  }
}

上面代码使用 Match 查询,指定的匹配条件是desc字段里面包含”软件”这个词。返回结果如下。

Elastic 默认一次返回10条结果,可以通过size字段改变这个设置。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "管理" }},
   "size": 1
 }'

上面代码指定,每次只返回一条结果。

还可以通过from字段,指定位移。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "管理" }},
   "from": 1,
   "size": 1
 }'

上面代码指定,从位置1开始(默认是从位置0开始),只返回一条结果。

如果有多个搜索关键字, Elastic 认为它们是or关系。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "软件 系统" }}
 }'

上面代码搜索的是软件 or 系统

如果要执行多个关键词的and搜索,必须使用布尔查询

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query": {
     "bool": {
       "must": [
         { "match": { "desc": "软件" } },
         { "match": { "desc": "系统" } }
       ]
     }
   }
 }'

在laravel中使用Elasticsearch

通过composer引入使用Elasticsearch第三方包

composer require babenkoivan/scout-elasticsearch-driver

Laravel 提供了一个扩展包 Scout 用来提供全文搜索的功能,通过安装扩展包我们是可以使用 Elasticsearch 作为驱动的。

文档可查看这里babenkoivan/scout-elasticsearch-driver,在使用扩展包之前,需要进行相关配置

配置

首先将Scout和扩展包的配置文件发布出来。

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
php artisan vendor:publish --provider="ScoutElastic\ScoutElasticServiceProvider"

修改一下配置,可以配置到 env 中。

SCOUT_DRIVER=elastic 
SCOUT_ELASTIC_HOST=elasticsearch:9200

使用

创建索引

该命令会创建一个GoodsIndexConfigurator.php文件,在文件中可以指定索引名称和设置

php artisan make:index-configurator Es/GoodsIndexConfigurator

该命令会在elasticsearch中创建相应的索引名,相当于创建在mysql中创建表

php artisan elastic:create-index App\\Es\\GoodsIndexConfigurator

注意,每个可搜索模型都需要有自己的索引配置器。

在Elasticsearch 6.0.0或更高版本中创建的索引只能包含一个映射类型。在5.x中创建的具有多种映射类型的索引将继续像在Elasticsearch 6.x中一样工作。映射类型将在Elasticsearch 7.0.0中完全删除。

修改商品模型代码如下

<?php

namespace App\Models;

use App\Es\GoodsIndexConfigurator;
use Illuminate\Database\Eloquent\Model;
use ScoutElastic\Searchable;

class HGoodsEs extends Model
{
    use Searchable;

    protected $table='goods';
    public $timestamps = true;

    //使用es配置
    protected $indexConfigurator = GoodsIndexConfigurator::class;

    protected $mapping = [
        'properties' => [
            'title' => [
                'type' => 'text',
            ],
            'content' => [
                'type' => 'text',
            ],
        ]
    ];

    public function toSearchableArray()
    {
        return [
            'title'=> $this->title,
            'content' => strip_tags($this->content),
        ];
    }

}

数据导入

执行以下命令,导入商品数据

php artisan scout:import "App\Models\Goods"

在我的Laravel项目中使用Elasticsearch

导入完成后,我们直接在网页查看elasticsearch:9200/goods/goods/_sea...
可查看到如下:

说明数据已成功导入elasticsearch
在我的Laravel项目中使用Elasticsearch

测试搜索

使用tinker测试

Goods::search('boy')->get()

执行结果如下,说明已成功使用elasticsearch进行全文搜索,其余就根据业务需要去执行相应的查询就好了

在Laravel项目中使用Elasticsearch

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 3
bing8u

老版本的就不用发了吧,现在的7+版本早已经废弃 type。

用type来对比数据库的表,早已经不适合了。

如有文档应该使用高版本的使用案例。

1个月前 评论
oliver-l (楼主) 1个月前

赞,很不错的教程

1个月前 评论
oliver-l (楼主) 1个月前

这个只是最基本的查询吧,查询结果的列表优先级怎么弄?符合规则的排在前面,比如ab,aab,aabb 这种自己定义的规则

1个月前 评论
oliver-l (楼主) 1个月前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!