Laravel 中优雅的使用 Elasticsearch
最近公司开始使用ES,发现没有特别好用的扩展。
Laravel官方提供了一个Scout
扩展,但这个扩展太局限,文档也含糊的要命,所以决定自己写一个扩展来适配Laravel。
首先发现了一个比较好用DSL,ongr-io/ElasticsearchDSL,这个扩展几乎涵盖了所有的ES操作。
那怎么才能在Laravel中像ORM一样使用呐,那就参考Scout
扩展了(这个低配版我真不想用),
源码我已经放在了GitHub上,这次的版本和之前做了结构上的调整,更加好用,而且已经在项目中使用,我会一直维护这个扩展包。
这次的重构,主要是借鉴了ORM中Builder
的实现,放弃了Scout
拗口的特性。
直接贴使用代码
使用说明
生成Mapping
通过读取注解实现
<?php
<?php
namespace App\Entities;
use Golly\Elastic\Hydrate\Entity;
use Golly\Elastic\Hydrate\Annotations\Mapping;
use Golly\Elastic\Exceptions\ElasticException;
/**
* Class UserEntity
* @package App\Entities
*/
class UserEntity extends Entity
{
/**
* @Mapping(type="long")
*
* @var int
*/
public $id;
/**
* @Mapping(type="text", analyzer="ik_smart")
*
* @var
*/
public $name;
/**
* @Mapping(type="date", format="Y-m-d")
*
* @var string
*/
public $date;
/**
* @Mapping(type="relation")
*
* @var array|UserAddressEntity[]
*/
public $address = [];
/**
* @param boolean $relation
* @return array
* @throws ElasticException
*/
public static function mapping($relation = true)
{
$mapping = parent::mapping($relation);
if ($relation) {
$mapping = array_merge($mapping, [
'address' => [
'properties' => UserAddressEntity::mapping($relation)
]
]);
}
return $mapping;
}
/**
* @param array $data
* @param boolean $original
* @return UserEntity
* @throws ElasticException
*/
public function toObject(array $data, $original = true)
{
$entity = parent::toObject($data, $original);
if ($entity->address) {
foreach ($entity->address as &$address) {
$address = UserAddressEntity::instance($address, $original);
}
}
return $entity;
}
}
获取
<?php
use App\Entities\UserEntity;
dd(UserEntity::mapping());
打印结构
array:4 [
"id" => array:1 [
"type" => "long"
]
"name" => array:2 [
"type" => "text"
"analyzer" => "ik_smart"
]
"date" => array:2 [
"type" => "date"
"format" => "yyyy-MM-dd"
]
"address" => array:1 [
"properties" => array:1 [
"city" => array:1 [
"type" => "keyword"
]
]
]
]
如何检索
在orm中,可直接用DB门面类来执行MySQL操作,相当于实例化Illuminate\Database\Query\Builder
类。
那么ES应该也可以这样操作,所以我也加入了一个ElasticBuilder
类
当然Model
模型依赖可以按原来的方式操作,最终都是ElasticBuilder
来构造查询参数
(new ElasticBuilder())
->select(['id'])
->from('index')
->where(function (ElasticBuilder $query) {
$query->where('name', '张三');
})->should(function (ElasticBuilder $query) {
$query->where(
'id', 1
)->where(
'id', 2
);
})->whereNotIn(
'name', ['李四']
)->dd();
User::elastic()
->where(function (Builder $query) {
$query->where('name', '张三');
})->should(function (Builder $query) {
$query->where(
'id', 1
)->where(
'id', 2
);
})->whereNotIn(
'name', ['李四']
)->dd();
最终打印的结果是(这里我仅贴了一个,唯一 的区别就是’index’不同)
array:3 [
"index" => "index"
"_source" => array:1 [
0 => "id"
]
"body" => array:1 [
"query" => array:1 [
"bool" => array:3 [
"must" => array:1 [
0 => array:1 [
"bool" => array:1 [
"must" => array:1 [
0 => array:1 [
"term" => array:1 [
"name" => "张三"
]
]
]
]
]
]
"should" => array:1 [
0 => array:1 [
"bool" => array:1 [
"must" => array:2 [
0 => array:1 [
"term" => array:1 [
"id" => "1"
]
]
1 => array:1 [
"term" => array:1 [
"id" => "2"
]
]
]
]
]
]
"must_not" => array:1 [
0 => array:1 [
"terms" => array:1 [
"name" => array:1 [
0 => "李四"
]
]
]
]
]
]
]
]
直接通过ES检索数据
User::elastic()->getRaw()
通过ES检索数据后,执行MySQL查询
User::elastic()->get();
该操作支持with
,withCount
直接通过ES检索数据后分页
User::elastic()->paginateRaw();
通过ES检索后,执行MySQL查询并分页
User::elastic()->paginate();
看上去不错 给lz 加油。
👍 支持下,下面这些分享给你,虽然我都没用过!😂
Community DSLs 社区提供的 DSL,第一个就是 ongr-io/ElasticsearchDSL
Community Integrations 社区整合包,里面有两个 Laravel 的
很赞 小心心送给你
在实际开发中,还是发现了一些问题,比如创建和更新时无法处理关联关系,查询时
orm
的一些特性无法优雅的结合起来,所以近期我会仔细考虑下如何处理这些问题,由于分词(ik)需要维护词库,所以like
查询将使用wildcard
查询。目前使用下来,在涉及到模糊查询的情况下,走es基本带来5-8倍的性能提升在orm中,当使用where(’name‘,'张三')->orWhere(’name‘, '李四'),最终sql是where name='张三' or name='李四',在ES中需要放在
bool
的should
下实现,个人能力有限,没有很好的拼接实现,所以我准备更多的使用es中的dsl作为方法名称:speak_no_evil:牛啊!楼主还会继续维护这个组件吗?