使用AMIMOTO + Amazon Elasticsearch Service增强WordPress的搜索功能
大家好,我是亿万AMIMOTO粉丝的全球朋友们!这是AMIMOTO Advent Calendar 2015的第13篇文章。
你还在WordPress搜索中耗费时间吗?
WordPress的搜索功能只是对标题和正文进行简单的like搜索,所以精确度并不高。
另外,有时候我们可能想要使用插件将自定义字段作为搜索目标,但是自定义字段由MySQL的表构成,列的数据类型为Text,如果将多个自定义字段作为Like搜索的目标,搜索会变得非常重。
这个搜索的准确性会受到网站类型的影响,可能会相当困扰吧。
在普通的博客上可能没有那么在意,但我认为在制作活动网站时,也有很多情况下会注重搜索功能。
所以,我最近将AWS的Amazon Elasticsearch Service和AMIMOTO连接起来,以便在Elasticsearch中执行默认的搜索。我将其制作成了WP Elasticsearch插件,并上传到Github上。
组成
構成非常简单。只需通过AMIMOTO的EC2实例向Amazon Elasticsearch Service发送请求。Elasticsearch通过Endpoint进行GET或POST交换数据。因此,本次不涉及API Gateway或Lambda等中间代理的存在。
Amazon Elasticsearch Service的配置
在Serverworks先生的博客中,提到了如何使用Amazon Elasticsearch Service和Fluentd。由于设置非常简单,所以在这里不再详细叙述,请参考相关文章。
我是IAM设置。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "es:*",
"Resource": "<作成したEalsticsearchのARN>",
"Condition": {
"IpAddress": {
"aws:SourceIp": "<IPアドレス>"
}
}
}
]
}
这次我们在IP上进行了限制。
但是,这样不太好。如果有眼力的人可能会明白,限制IP会导致无法实现自动扩展。而且将IP嵌入到IAM中也在管理上不可行。
最好在Amazon Elasticsearch Service中通过IAM角色实现访问控制。可以指定主体,仅允许自己账户的EC2实例访问。
希望的话,为了发送请求,我们需要实现带有签名的URL机制。
由于这个机制相当复杂,所以这次我们选择了通过IP限制来实现。
希望AWS能够提供一些方便的解决方案。
将WordPress的文章内容注册到Elasticsearch中。
实际上将映射过程编写成代码的部分如下所示。我们使用了一个名为Elastica的Elasticsearch库。
/**
* admin_init action. mapping to Elasticsearch
*
* @return true or WP_Error object
* @since 0.1
*/
private function _data_sync() {
try {
$options = get_option( 'wpels_settings' );
$client = $this->_create_client( $options );
if ( ! $client ) {
throw new Exception( 'Couldn\'t make Elasticsearch Client. Parameter is not enough.' );
}
$options = get_option( 'wpels_settings' );
$index = $client->getIndex( $options['index'] );
$index->create( array(), true );
$type = $index->getType( $options['type'] );
$mapping = array(
'post_title' => array(
'type' => 'string',
'analyzer' => 'kuromoji',
),
'post_content' => array(
'type' => 'string',
'analyzer' => 'kuromoji',
),
);
if ( ! empty( $options['custom_fields'] ) ) {
$custom_fields = explode( "\n", $options['custom_fields'] );
$custom_fields = array_map( 'trim', $custom_fields );
$custom_fields = array_filter( $custom_fields, 'strlen' );
foreach ( $custom_fields as $field ) {
$mapping[ $field ] = array(
'type' => 'string',
'analyzer' => 'kuromoji',
);
}
}
$type->setMapping( $mapping );
$my_posts = get_posts( array( 'posts_per_page' => -1 ) );
$docs = array();
foreach ( $my_posts as $p ) {
$d = array(
'post_title' => (string) $p->post_title,
'post_content' => (string) strip_tags( $p->post_content ),
);
if ( ! empty( $options['custom_fields'] ) ) {
foreach ( $custom_fields as $field ) {
$d[ $field ] = (string) strip_tags( get_post_meta( $p->ID, $field, true ) );
}
}
$docs[] = $type->createDocument( (int) $p->ID, $d );
}
$bulk = new Bulk( $client );
$bulk->setType( $type );
$bulk->addDocuments( $docs );
$bulk->send();
return true;
} catch (Exception $e) {
$err = new WP_Error( 'Elasticsearch Mapping Error', $e->getMessage() );
return $err;
}
}
在Elasticsearch中进行搜索。
搜索代码类似如下:
将投稿ID与Elasticsearch中的ID关联起来。简单地根据返回的值,在pre_get_posts挂钩中修改查询。
/**
* search query to Elasticsearch.
*
* @param $search_query
* @return true or WP_Error object
* @since 0.1
*/
public function search( $search_query ) {
try {
$options = get_option( 'wpels_settings' );
$client = $this->_create_client( $options );
if ( ! $client ) {
throw new Exception( 'Couldn\'t make Elasticsearch Client. Parameter is not enough.' );
}
$type = $client->getIndex( $options['index'] )->getType( $options['type'] );
$qs = new QueryString();
$qs->setQuery( $search_query );
$query_es = Query::create( $qs );
$resultSet = $type->search( $query_es );
$post_ids = array();
foreach ( $resultSet as $r ) {
$post_ids[] = $r->getID();
}
return $post_ids;
} catch (Exception $e) {
$err = new WP_Error( 'Elasticsearch Search Error', $e->getMessage() );
return $err;
}
}
执行结果
总结
使用AMIMOTO可以轻松与AWS内的服务进行连接,这是很好的。就用途而言,我认为有相当数量的网站可能会在WordPress搜索增强方面使用它。而且,考虑到多站点等,跨站点的横向搜索也会更加方便。
祝你拥有愉快的亚马逊 Elasticsearch 服务之旅!