<?php
require 'vendor/autoload.php';
class MyElasticSearch
{
private $client;
// 构造函数
public function __construct()
{
$params = array(
'127.0.0.1:9200'
);
$this->client = ClientBuilder::create()->setHosts($params)->build();
}
/**
* 创建索引
* */
public function create_index($index_name) { // 只能创建一次
$params = [
'index' => $index_name, //索引名称
'body' => [
'settings' => [ //配置
'number_of_shards' => 5, // 分片数
'number_of_replicas' => 0 // 备份数
]
]
];
try {
return $this->client->indices()->create($params);
} catch (\Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg,true);
return $msg;
}
}
/**
* 删除索引
* $index_name 索引名称
*/
public function delete_index($index_name) {
$params = ['index' => $index_name];
try {
$response = $this->client->indices()->delete($params);
return ['', $response];
} catch (\Exception $exception) {
return ['error',''];
}
}
/**
* 创建索引
* properties.type //类型 text、integer、float、double、boolean、date
*/
public function create_mappings($index_name) {
$params = [
'index' => $index_name, //索引名称
'body' => [
'properties'=> [ // 配置数据结构与类型
'name'=> [ // 字段1
'type'=>'text',
'index'=> 'true', //是否索引
'analyzer' => 'ik_max_word' // 分词
],
'age'=> [ // 字段2
'type'=>'integer',
],
'sex'=> [ // 字段3
'type'=>'keyword',
'index'=> 'false',
],
]
],
];
$response = $this->client->indices()->putMapping($params);
return $response;
}
/**
* 查看映射
* $index_name 索引名称
*/
public function get_mapping($index_name) {
$params = [
'index' => $index_name,
];
try {
$response = $this->client->indices()->getMapping($params);
return ['',$response];
} catch (\Exception $exception) {
return ['error',''];
}
}
/**
* 添加文档
* $index_name 索引名称
* $type_name _type 非必填 默认创建
* $id 非必填 默认创建
* $doc 添加的内容
*/
public function add_doc($index_name,$type_name = null,$id,$doc) {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id,
'body' => $doc
];
$response = $this->client->index($params);
return $response;
}
/**
* 批量创建文档
* $index_name 索引名称
* $type_name _type 非必填 默认创建
* $body 内容
* $body例:[['create' => [ //index 与 create一致都是创建文档'_id' => $i,],'name' => 'PHPerJiang','age' => 1,'sex' => 2,]];
*/
public function bulk_create_another($index_name, $type_name = null, $body = [])
{
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => $body,
];
$response = $this->client->bulk($params);
return $response;
}
/**
* 判断文档存在
* $index_name 索引名称
* $type_name _type 非必填 默认创建
*/
public function exists_doc($index_name,$id,$type_name = null) {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
$response = $this->client->exists($params);
return $response;
}
/**
* 获取文档
* $index_name 索引名称
* $type_name _type
* $id 文档id
*/
public function get_doc($index_name,$id = null,$type_name = null) {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
try {
$response = $this->client->get($params);
} catch (\Exception $exception) {
$response = [];
}
return $response;
}
/**
* 一次获取多个文档
* @param $ids
* @return array
*/
public function getDocs($index_name, array $ids, $type_name = null) {
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => ['ids' => $ids]
];
return $this->client->mget($params);
}
/**
* 更新文档
* $type_name _type
* $id 文档id
* $doc 更新的内容
*/
public function update_doc($index_name = null,$id = null,$type_name = null, $doc = []) {
$redisKey = "es_{$index_name}_{$type_name}_$id";
if ( ! Redis::setNx($redisKey,1) ) {
return ["error"=>'正在处理'];
}
try {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id,
'body' => [
'doc' => $doc
]
];
$response = $this->client->update($params);
return $response;
} catch (\Exception $exception) {
throw $exception;
} finally {
Redis::del($redisKey);
}
}
/**
* 批量更新
* body = [['update' => ['_id' => $i],'doc'=> ['name' => 'PHPerJiang','age' => 1,'sex' => 2,]]];
*/
public function bulk_update_another($index_name, $type = null,$body = []){
$params = [
'index' => $index_name,
'type' => $type,
'body' => $body
];
$response = $this->client->bulk($params);
return $response;
}
/**
* 删除文档
* $id 文档id
*/
public function delete_doc($index_name,$id,$type_name = null) {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
$response = $this->client->delete($params);
return $response;
}
/**
* 批量删除
* body = [['delete' => ['_id' => $id,]];
* */
public function bluk_delete_another($index_name, $type_name = null, $body = []){
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => $body,
];
$response = $this->client->bulk($params);
return $response;
}
/**
* 查询文档 (分页,排序,权重,过滤)
* $should = [['should' => [[ 'match' => [ 'name' => ['query' => $keywords,'boost' => 3, // 权重大]]],[ 'match' => [ 'sex' => ['query' => $keywords,'boost' => 2,]]],],]],
* sort = ['age'=>['order'=>'desc']]
*/
public function search_doc($index_name,$type_name = null,$search = [], $sort = [],$from = 0, $size = 20) {
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => [
'query' => [
'bool' => $search,
],
'sort' => $sort,
'from' => $from, 'size' => $size
]
];
$results = $this->client->search($params);
$maxScore = $results['hits']['max_score'];
$score = $results['hits']['hits'][0]['_score'];
$doc = $results['hits']['hits'][0]['_source'];
return $results;
}
}
一、精准查询term
term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
1、term单值
字段只有一个值时候,用term关键词查询
查询goods_id值为190的记录
curl -XGET http://127.0.0.1:9200/xyerp/order/_search -H 'Content-Type: application/json' -d '
{
"query": {
"term": {
"goods_id": "190"
}
}
}
进一步优化查询,因为是精准查询,不需要查询进行评分计算,只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。推荐如下查询
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"goods_id" : "190"
}
}
}
}
}'
2、terms多值
字段有一多个值时候,用terms关键词查询,后跟数组
{
"query":{
"terms":{
"goods_id":["190"]
}
}
}'
constant_score 以非评分模式查询,推荐如下查询
{
"query" : {
"constant_score" : {
"filter" : {
"terms" : {
"goods_id" : ["190","e1909"]
}
}
}
}
}'
3、term多个字段
{
"query": [{
"term": {
"goods_id": "190"
}
}, {
"term": {
"name": "zhangsan"
}
}]
}
二、匹配查询match
match和term的区别是,match查询的时候,elasticsearch会根据你给定的字段提供合适的分析器,而term查询不会有分析器分析的过程,match查询相当于模糊匹配,只包含其中一部分关键词就行
1、match
进行full text search或者exact value(非string字段或not_analyzed的字段),进行匹配
查询字段merchant_id为2501的值,并按时间倒序
{
"query": {
"match": {
"merchant_id": "2501"
}
},
"sort": [
{
"trade_finished_time": {
"order": "desc"
}
}
]
}'
2、match_all
{ "match_all": {}} 匹配所有的, 当不给查询条件时,默认全查。
{
"query": {
"match_all": {}
}
}
3、multi_match
同时对查询的关键词,多个字段同时进行匹配,即多个字段是AND的关系
{
"query":{
"multi_match":{
"query":"2501",
"fields":["merchant_id","_id"]
}
}
}
同时field还支持更为丰富的查询
在在fields中,按brandName(品牌名)、sortName(分类名)、productName(商品名)productKeyword(商品关键字),搜索“牛仔 弹力”关键词,brandName源值、拼音值、关键字值都是100分,sortName源值、拼音值80分,productName源值60分,productKeyword值20分,分值由高到低优先级搜索
{
"query": {
"multi_match": {
"query": "牛仔 弹力",
"fields": [
"brandName^100",
"brandName.brandName_pinyin^100",
"brandName.brandName_keyword^100",
"sortName^80",
"sortName.sortName_pinyin^80",
"productName^60",
"productKeyword^20"
],
"type": <multi-match-type>,
"operator": "AND"
}
}
}
4、match_phrase
match_phrase查询分析文本,并从分析文本中创建短语查询。
类似 match 查询, match_phrase 查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置与搜索词项相同的文档
如下,查询 quick brown、quick brown fox、 brown fox可以查询到,quick fox 查询不到
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}
如下, 查询 a,b,啊和b之间隔3个字符可以查询到,隔不是3个查询不到
{
"query":{
"match_phrase" :{
"query":"a,b",
"slop":3
}
}
}
三、bool查询
bool查询包含四种操作符,分别是must,should,must_not,query。它们均是一种数组,数组里面是对应的判断条件
must: 必须匹配,与and等价。贡献算分
must_not:必须不匹配,与not等价,常过滤子句用,但不贡献算分
should: 选择性匹配,至少满足一条,与 OR 等价。贡献算分
filter: 过滤子句,必须匹配,但不贡献算分
{
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"merchant_id" : 100}},
{ "term" : {"pay_type" : "3"}}
],
"must_not" : {
"term" : {"trade_type" : "2"}
}
}
}
}
}
}'
四、filter查询
过滤器,会查询对结果进行缓存,不会计算相关度,避免计算分值,执行速度非常快。
如下, 查询出status为active的状态
{
"query": {
"bool": {
"filter": {
"term": {
"status": "active"
}
}
}
}
}
filter也常和range范围查询一起结合使用,range范围可供组合的选项
gt : 大于
lt : 小于
gte : 大于等于
lte :小于等于
如下,查询merchant_id值为2501下的交易数据
{
"query": {
"bool": {
"must": {
"term": {
"merchant_id": "2501"
}
},
"filter": {
"range": {
"trade_finished_time": {
"from": "2019-09-01T00:00:00",
"to": "2019-09-30T23:59:59"
}
}
}
}
}
}
如下查询,must下匹配,filter进行过滤,range定义范围
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "Search"
}
},
{
"match": {
"content": "Elasticsearch"
}
}
],
"filter": [
{
"term": {
"status": "1"
}
},
{
"range": {
"publish_date": {
"gte": "2015-01-01"
}
}
}
]
}
}
}
五、常见查询场景
1、查询商户ID为3582,订单号为3601021990030726,按时间范围过滤,按下单时间倒序,每次查询100条
{
"query": {
"bool": {
"must": [{
"term": {
"merchant_id": "3582"
}
}, {
"term": {
"order_num": "3601021990030726"
}
}],
"filter": [{
"range": {
"order_time": {
"from": "2019-11-01T17:00:00+08:00",
"to": "2019-11-01T20:00:00+08:00"
}
}
}]
}
},
"size": 100,
"sort": [{
"order_time": "desc"
}]
}