elasticsearch 及分词使用
一,索引管理
1,创建索引
$client = ClientBuilder::create()->build();
$param = [
"index"=>'my_index'
];
$response = $client->indices()->create($param);
你也可以再一个创建索引api中指定任何参数。所有得参数通常会注入请求体中得body参数下:
$hosts = [
'192.168.1.1:9200', // IP + Port
'192.168.1.2', // Just IP
'mydomain.server.com:9201', // Domain + Port
'mydomain2.server.com', // Just Domain
'https://localhost', // SSL to localhost
'https://192.168.1.3:9200' // SSL to IP + Port
];
$client = ClientBuilder::create()
->setHost($host)// 指定主机域名
->setRetries(2) // 设置重连次数
->build();
$params = [
"index"=>'my_index',
"body"=>[
"settings"=>[
"number_of_shards"=>3,
"number_of_replicas"=>2
],
"mappings"=>[
"my_type"=>[
"_source"=>[
"enabled"=>true
],
"properties"=>[
"first_name"=>[
"type"=>"string"
"analyzer"=>"standard"
] ,
"age"=>[
"type"=>"integer"
]
]
]
]
]
];
$response = $client->indices()->create($params);
2,删除一个索引
$client = ClientBuilder::create()->build();
$params = ['index'=>'my_index'];
$response = $client->indice()->delete($params);
3,Put Settings APi
Put Setting APi允许你更改索引的配置参数
$params = [
"index"=>'my_index',
"body"=>[
"settings"=>[
"number_of_replicas"=>0,
"refresh_interval"=>-1
]
]
];
$response = $client->indices()->putSettings($params);
Get Settings APi
Get Settings API可以让你知道一个或多个索引的当前配置参数:
$params = ["index"=>"my_index"];
$response = $client->indices()->getSettings($params);
$params = [
"index"=>["my_index","my_index2"]
];
Put Mappings Api
Put Mappings Api允许你更改或增加一个索引的映射。
$param = [
"index"=>"my_index",
"type"=>"my_type2",
"body"=>[
"my_type2"=>[
"_source"=>[
"enabled"=>true
],
"properties"=>[
"first_name"=>[
"type"=>'string',
"analyzer"=>"standard"
],
"age"=>[
"type"=>"integer"
]
]
]
]
];
$client->indices()->putMapping($param);//Put Mappings API允许增加或增加一个索引的映射
Get Mappings API 返回索引和类型的映射细节。你可以指定一些索引和类型,取决于你希望索引什么映射。
$response = $client->indices()->getMapping();//获取所有映射
$params = ["index"=>"my_index"];
$response = $client->indices()->getMapping($params);//获取指定索引的映射
$params = [
"index"=>'my_index',
"type"=>"my_type"
];
$response = $client->indices()->getMapping($params);//获取指定索引指定type下的映射
$params = [
"index"=>["my_index",'my_index2']
];
$response = $client->indices()->getMapping($params);//获取多个索引的映射
二,索引文档
1,单一文档
当索引一个文档时,你可以提供一个ID或者让Elasticsearch自动生成
提供ID值:
$params = [
"index"=>'my_index',
"type"=>'my_type',
"id"=>'my_id',
"body"=>["testField"=>'abc']
];
$response = $client->index($params);
不提供ID值
$params = [
"index"=>'my_index',
"type"=>"my_type",
"body"=>['testField'=>['abc']]
];
$response = $client->index($params);
如果你需要设置其他的参数,如routing的值,你可以指定这些参数到index,type等参数后。例如,索引一个新的文档时设置rounting值和timestamp值:
//如果需要蛇者其他的参数,需要指定到index,type之后
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"routing"=>"company_xyz",
"timestamp"=>strtotime("-1d"),
"body"=>[
"testField"=>"abd"
]
];
$response = $client->index($params);
2,批量(bulk)索引
Elasticsearch也支持批量(bulk)索引对象。bulk API要求提供JSON格式的 action/元数据 键值对。再php中构建批量文档数据也是相似的。你首先要创建一个action数组对象(如index对象),
然后你还要创建一个body对象。而PHP程序则重复上述操作构建文档数据。
例子:
for($i=0; $i<10; $i++){
$params['body'][] = [
"index"=>[
"_index"=>"my_index",
"_type"=>"my_type"
]
];
$params['body'][] = [
"my_field"=>"my_value",
"second_field"=>"some more values"
];
}
$response = $client->bulk($params);//批量发送
实际上再一次bulk请求中发送数量会比文档实际数量少。如果时这种情况,你就要设置批量值然后周期性的发送:
//周期性的发送
$params = ["body"=>[]];
for($i=1;$i=123456;$i++){
$params['body'][] = [
"index"=>[
"_index"=>"my_index",
"_type"=>"my_type",
"_id"=>$i
]
];
$params['body'][] = [
"my_field"=>"my_value",
"second_field"=>"some more values"
];
if($i % 1000 ==0){
$response = $client->bulk($params);
$params = ["body"=>[]];
unset($response);
}
}
if(!empty($params['body'])){
$response = $client->bulk($params);
}
三,php处理JSON数组或对象
Elasticsearch JSON API 常见的数据格式
1,空对象
Elasticseach API再几个地方使用了空对象,这会对PHP造成影响。不像其他的语言,PHP没有一个简便的符号来表示空对象,而许多开发者还不知道如何指定一个空对象
{
"query" : {
"match" : {
"content" : "quick brown fox"
}
},
"highlight" : {
"fields" : {
"content" : {}
}
}
}
这个空对象便会引起问题
问题就在于php会自动把 "conteng":{} 转换为"content":[] ,在Elasticsearch DSL中这样的数据格式时非法的。我们需要告诉PHP那个空对象就是一个空对象而非空数据。为了在查询中定义空对象,需要这样做:
//定义空对象
$params['body'] = array(
"query"=>[
"match"=>[
"content"=>"quick brown fox"
]
],
"highlight"=>array(
"fields"=>[
"content"=>new \stdClass() // 定义空对象
]
)
);
$response = $client->search($params);
通过使用PHP的stdclass对象来代表空对象,现在就可以解析JSON数据了
通过使用一个stdclass对象,我们可以强制json_encode解析为空对象,而不是空数组。然而,这种冗余的写法是唯一解决PHP空对象的方法
2,对象数组
Elasticsearch DSL的另一种常见的数据格式时对象数组。例如假设在你的查询中增加排序
{ "query" : { "match" : { "content" : "quick brown fox" } }, "sort" : [ {"time" : {"order" : "desc"}}, {"popularity" : {"order" : "desc"}} ] }
sort内含JSON对象数组
嵌套数组
//对象数组
$params['body'] = [
"query"=>[
"match"=>[
"content"=>"quick brown fox"
]
],
"sort"=>[ // 这里encode 为 "sort":[]
['time'=>['order'=>"desc"]], //这里encode为:{"time":{"order":"desc"}}
["popularity"=>["order"=>"desc"]] //这里encode为 {"popularity":{"order":"desc"}}
]
];
$response = $client->search($params);
3,空对象数组
偶尔DSL需要上述两种数据格式。score查询便是一个很好的例子,该查询有时需要一个对象数组,而有一些对象数组,而有一些对象可能时一个空的JSON对象
请看如下查询
{
"query":{
"function_score":{
"functions":[
{
"random_score":{}
}
],
"boost_mode":"replace"
}
}
}
我们用下面的PHP代码来构建这个查询
//空对象数组
$params['body'] = [
"query"=>[
"function_score"=>[
"functions"=>[//encode为"functions""[]
[//encode为{"random_score":{}}
"random_score"=>new \stdClass()//encode为"random_score":{}
]
]
]
]
];
$result = $client->search($params);
四 搜索操作
1,Match查询
以下时Match查询的标准curl格式
curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{ "query" : { "match" : { "testField" : "abc" } } }'
而这里则时客户端构建的同样的查询
//搜索操作
$params = [
"index"=>"my_index",
"type"=>"my_type",
"body"=>[
"query"=>[
"match"=>[
"testField"=>"abc"
]
]
]
];
$result = $client->search($params);
这里要注意PHP数组得结构与层次是怎样与curl中得JSON请求体格式相对应得。这种方式使得JSON得写法转换为PHP得写法变得十分简单。一个快速检测PHP数组是否为预期结果得方法,就是encode为JSON格式,然后进行检查:
$params = [
"index"=>"my_index",
"type"=>"my_type",
"body"=>[
"query"=>[
"match"=>[
"testField"=>'abc'
]
]
]
];
print_r(json_encode($params['body']));
{"query":{"match":{"testField":"abc"}}}
使用原生JSON
有时使用原生JSON来进行测试会十分方便,或者原生JSON来进行不同系统的移植也同样方便。你可以在body中用原生JSON字符串,这样客户端会自动进行检查操作:
$json = '{
"query":{
"match":{
"testField":"abc"
}
}
}';
$params = [
"index"=>"my_index",
"type"=>"my_type",
"body"=>$json
];
$results = $client->search($params);
搜索结果与Elasticsearch的响应结果一致,唯一不同的是JSON格式会转换成PHP数组。处理这些数据与数组迭代一样简单:
$params = [
"index"=>"my_index",
"type"=>"my_type",
"body"=>[
"query"=>[
"match"=>[
"testField"=>'abc'
]
]
]
];
$results = $client->search($params);
$milliseconds = $results['took'];
$maxscore = $results['hits']['max_score'];
$score = $results['hits']['hits'][0]['_score'];
$doc = $results['hits']['hits'][0]['_source'];
Bool查询
利用客户端可以轻松构建Bool查询。例如以下查询
curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{
"query" : {
"bool" : {
"must": [
{
"match" : { "testField" : "abc" }
},
{
"match" : { "testField2" : "xyz" }
}
]
}
}
}'
会构建为这样了
$params = [
"index"=>"my_index",
"type"=>"my_type",
"body"=>[
"query"=>[
"bool"=>[
"must"=>[
["match"=>["testField"=>"abc"]],
]
]
]
]
];
$results = $client->search($params);
这里注意must语句接收的是数组。这里会转化为JSON数组,所以最后的响应结果与curl格式的响应结果一致。。
更为复杂的示例
这里构建一个有点复杂的例子:一个bool查询包含一个filter过滤器和一个普通查询。这在elasticsearch的查询中非常普通,所以这个例子会非常有用。
curl格式的查询:
curl -XGET 'localhost:9200/my_index/my_type/_search' -d '{ "query" : { "bool" : { "filter" : { "term" : { "my_field" : "abc" } }, "should" : { "match" : { "my_other_field" : "xyz" } } } } }'
而在PHP中
Scrolling(游标)查询
在用bulk时,经常要用scrolling功能对文档进行分页处理,如输出一个用户的所有文档。这比常规的搜索要高效,因为这里不需要对文档执行性能消耗较大的排序操作。
Scrolling会保留某个时间的索引快照数据,然后快照数据,然后用快照数据进行分页。游标查询窗口允许持续分页操作,即使后台正在执行索引文档、更新文档和删除文档。
首先,你要在发送搜索请求时增加scroll参数。然后就会返回一个文档页数信息,还有一个用来获取hits分页数据的scroll_id。
以下代码更为深入的操作的示例:
$params = [
"scroll"=>"30s",//滚动请求的间隔时间
"size"=>50,//每个碎片返回多少个结果
"index"=>"my_index",
"body"=>[
"query"=>[
"match_all"=>new \stdClass()
]
]
];
$response = $client->search($params);
//循环,直到滚动游标用完为止
while(isset($response['hits']['hits']) && count($response['hits']['hits'])>0 ){
$scroll_id = $response['_scroll_id'];//刷新scroll_id
$response = $client->scroll([//执行滚动请求并重复
"scroll_id"=>$scroll_id,
"scroll"=>"30s"
]);
}
5,获取文档
Elasticsearch提供实时获取文档的方法。这意味着只要文档被索引且客户端收到消息确认后,你就可以立即在任何的分片中检索文档。Get操作通过index/type/id方式方式请求一个文档信息:
$params = [
"index"=>'my_index',
"type"=>"my_type",
"id"=>"my_id"
];
$response = $client->get($params);
6,更新文档
更新文档操作既可以完全覆盖现存文档全部字段,又可以部分更新字段(更改现存字段,或添加新字段)
部分更新
如果你要部分更新文档(如更改现存字段,或添加新字段),你可以在body参数中指定一个doc参数。这样doc参数内的字段会与现存字段进行合并
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"body"=>[
"doc"=>[
"new_field"=>"abc"
]
]
];
//更新 /my_index/my_type/my_id 索引文档
$response = $client->update($params);
Script更新
有时你要执行一个脚本来进行更新操作,如字段进行自增操作或添加新字段。为了执行一个脚本更新,你要提供脚本命令和一些参数
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"body"=>[
"script"=>"ctx._source.counter += count",
"params"=>[
"count"=>4
]
]
];
$response = $client->update($params);
Upserts更新
Upserts操作是指“更新或插入”操作会执行script更新,如果文档不存在(或是你更新的字段不存在),则会插入一个默认值
$params=[
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"body"=>[
"script"=>"ct._source.counter += count",
"params"=>[
"count"=>4
],
'upsert'=>[
"counter"=>1
]
]
];
$response = $client->update($params);
六,命名空间
客户端有许多“命名空间”,通常是一些公开的可管理功能。命名空间对应Elasticsearch中各种可管理的endpoint。下面全部的命名空间:
命名空间 | 功能 |
indices() | 索引数据和显示索引信息 |
nodes() | 节点数据统计和显示节点信息 |
cluster | 集群数据统计和显示集群信息 |
snapshort | 对集群和索引进行拍摄快照或恢复数据 |
cart() | 执行Cat API命令(通常在命令行中使用) |
一些方法在不同的命名空间下均可使用。虽然返回的是同样的信息但是却属于不同的上下文环境。想知道命名空间如何运行,请看_stats的输出信息
$response = $client->indices()->stats();
$response = $client->nodes()->stats();
$response = $client->cluster()->stats();
上面展示了在三个不同命名空间下都调用了stats()方法,有时这些方法需要参数,这些参数的写法跟客户端中其他方法的参数写法相同。
例如我们可以请求索引或多个索引的统计信息:
$params_1['index'] = "my_index";
$response = $client->indices()->stats($params_1);
$params_2['index'] = ["my_index",'my_index2'];
$response = $client->indices()->stats($params_2);
/在现有索引中添加别名
$params_3['body'] = [
"actions"=>[
[
"add"=>[
"index"=>"my_index",
"alias"=>"myalias"
]
]
]
];
$response = $client->indices()->updateAliases($params_3);
注意上述例子中两个stats的调用和updateAlias的调用接收不同格式的参数,每个方法的参数格式由响应的API需求来决定。stats API只需要一个index名,而updateAlias则需要一个body,里面还要一个actions参数。
七 按请求配置
1,忽略异常
Elasticsearch-PHP的类库是会对普通的问题抛出异常的。这些异常跟Elasticsearch返回的HTTP响应码--对应。例如,获取一个不存在的文档抛出MissingDocument404Exception.
异常对于处理一些问题(如找不到文档,语法错误,版本冲突等)十分有用。但是有时候你只是想要处理返回的数据
如果你想忽略异常,你可以配置ignore参数。ignore参数要作为client的参数配置在请求体中。例如下面的实例会忽略MissingDocument404Exception,返回的是Elasticsearch提供的JSON数据
$params = [
"index"=>"test_missing",
"type"=>"test",
"id"=>1,
"client"=>['ignore'=>404]//忽略参数ignore需要作为client的参数配置在请求体中 这里忽略404
];
$client->get($params);
你可以通过数组的方式指定忽略多个HTTP状态码:
//忽略多个HTTP状态码
$params = [
"index"=>"test_missing",
"type"=>"test",
"id"=>1,
"client"=>["ignore"=>[400,404]] //ignore参数接收数组,BadRequest400Exception和MissingDocument404Exception都会忽略
];
$client->get($params);
注意,返回的数据是字符串格式,而不是JSON数据。而在第一个示例中返回的是JSON数据,客户端会decode该JSON数据为数组。
一旦客户端无法得知返回的异常数据格式,客户端就不会decode返回结果
2,自定义查询参数
有时候你要自己提供自定义参数,比如第三方插件或代理提供认证token。在Elasticsearch-php的白名单中存储着所有的查询参数,这是为了防止你指定一个参数,而Elasticsearch却不接受。
如果你要自定义参数,你就要忽略这种白名单机制。为了达到这种效果,请增加custom参数:
$params = [
"index"=>"test",
"type"=>"test",
"id"=>1,
"parent"=>'abc',//列入白名单的参数
"client"=>[
"custom"=>[
"customeToken"=>"abc",//用户定义,没有勾选
"otherToken"=>123
]
]
];
$exists = $client->exists($params);
if($exists){
echo "存在";
}else{
echo "不存在";
}
返回详细输出
客户端默认只返回响应体数据。如果你需要更多信息(如头信息,响应状态码等),你可以让客户端返回更多详细信息。通过verbose参数可以开启这个功能。
没有返回详细信息,你看到的返回是这样的:
$params = [
"index"=>"test",
"type"=>"test",
"id"=>1,
"client"=>[
"ignore"=>[400,404]
]
];
$response = $client->get($params);
如果加上参数:
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"client"=>[
"verbose"=>true
]
];
$response = $client->get($params);
5,curl超时设置
通过timeout和connect_timeout参数可以配置每个请求的curl超时时间。这个配置主要是控制客户端的超时之间。
connect_time 参数控制在连接阶段完成前,curl的等待时间。而timeout参数则控制整个请求完成前,最多等待多长时间。
如果超过超时时间,curl会关闭连接并返回一个致命错误,两个参数都要用 秒 作为参数
注意:客户端超时并 不 意味着Elasticsearch中止请求。Elasticsearch会继续执行请求直到请求完成。在慢查询或是bulk请求下,操作会在后台继续执行,对客户端来说这些动作是隐蔽的。
如果客户端在超市后立即断开连接,然后又立刻发送另外一个请求。由于客户端没有处理服务端回压的机制,这有可能会造成服务端过载。遇到这种情况,你会发现线程池队列会慢慢变大,当队列超出负荷,Elasticsearch会发送
EsRejectedException的异常
//curl超时设置
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"client"=>[
"timeout"=>10,//控制整个请求完成前,最多等待的时间
"connect_time"=>10//控制在连接阶段完成前,curl等待的时间
]
];
$response = $client->get($params);
6,开启Future模式
客户端支持异步方式批量发送请求。通过client选项的future参数可以开启(HTTP handle要支持异步模式)
//开启future模式
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"client"=>[
"future"=>"lazy"
]
];
$future = $client->get($params);
$results = $future->wait();//解决函数
future模式有两个参数可选:true或lazy。关于异步执行方法以及如何处理返回结果的详情,请到Future模式中查看
7,SSL加密
在创建客户端时,一般需要指定SSL配置,因为通常所有的请求都需要加密。然而,在每个请求中配置SSL加密也是有可能的。例如,如果你需要在某个特定的请求中使用自签名整数,你可以通过在client选项中配置verify参数:
$params = [
"index"=>"my_index",
"type"=>"my_type",
"id"=>"my_id",
"client"=>[
'verify'=>'path/to/cacert.pem'//使用自签名证书
]
];
$result = $client->get($params);