この記事は朝日新聞社 Advent Calendar 2022の17日目の記事です🌞
朝日新聞のエンジニアがバラエティに富んだテック記事をお届けしております!
本日は開発部・中阪が担当です。
PHPとOpenSearchの公式PHPクライアントであるOfficial PHP Client for OpenSearchを使って、AWSが提供しているフルマネージドのAmazon OpenSearch Serviceにアクセスし、データを取得するための方法を初心者でも分かるように解説します。
環境
・Amazon OpenSearch Service
・EC2 (Amazon Linux 2)
・PHP 8.0 (インストール済み)
・Apache/2.4.54 (インストール済み)
・Official PHP Client for OpenSearch https://github.com/opensearch-project/opensearch-php
PHPとOfficial PHP Client for OpenSearchをインストールしたEC2からOpenSearchに登録した新聞記事データの見出しを取得しに行くことを目指します。
Composerのインストール
ComposerはPHPのパッケージ依存管理ツールです。Official PHP Client for OpenSearchを使用するためには、まずComposerをインストールする必要があります。
# php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
# php composer-setup.php
# ./composer.phar -v
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
上記のようにComposerのロゴが表示されればインストール成功です。
composerコマンドをどこからでも使えるようにするためにcomposer.pharを/usr/local/bin配下に移動させます。
# mv composer.phar /usr/local/bin/composer
Official PHP Client for OpenSearchのインストール
次のコマンドで一発でインストールできます。
composer require opensearch-project/opensearch-php
使い方
ページの上部でComposerをインストールした際にできた/vendor/autoload.phpを指定し、
次にOpenSearchのエンドポイントとプロトコル、ポート番号を記述します。
ここでschemeとportを記述しないと後々「No alive nodes found in your cluster」とエラーが出ますので気を付けましょう。
次の$clientの箇所でOpenSearchとのコネクションを確立しています。OpenSearchへのアクセスにBasic認証を使用している場合、
ここで突破するためのユーザー名とパスワードを記述しておいてください。
require __DIR__ . '/vendor/autoload.php';
$opensearchHost = [
'host' => '***********.ap-northeast-1.es.amazonaws.com',
'scheme' => 'https',
'port' => '443'
];
$client = (new \OpenSearch\ClientBuilder())
->setHosts([$opensearchHost])
->setBasicAuthentication('username', 'password') //BASIC認証のユーザー名とパスワードを記載
->setSSLVerification(false)
->build();
ここから、OpneSearchへのクエリを記述していきます。今回はmidashiというインデックスに対して、simple_query_stringを用いてデータ取得を行っています。勿論、matchクエリを用いていただいても全く問題ないです。
フィールドにはkuromojiを用いた形態素解析用のMIDASHI_kuromojiとN-gram用のMIDASHI_ngの2つを用意し、そのOR検索を行うことで2通りの手法で全文検索を実現しています。
ただし、N-gramについては形態素解析の補完の意味合いが強いのでboostを用いて傾斜をかけています。
$indexName = 'midashi';
$params = [
'index' => $indexName,
'body' => [
'size' => 50,
'query' => [
"bool"=>[
'should'=>[
['simple_query_string' => [
'query' => $_POST["keyword"],
"fields" => ["MIDASHI_kuromoji"],
"default_operator" => "and",
"boost"=>2
]],
['simple_query_string' => [
'query' => $_POST["keyword"],
"fields" => ["MIDASHI_ng"],
"default_operator" => "and",
"boost"=>1
]],
]
]
]
]
];
今、記述したクエリを以下のように投げることでデータを取得できます。
$result = $client->search($params);
最後に簡単ではありますが、取得したデータを表形式で並べてみます。
<table border="1">
<tr>
<th>見出し</th>
<th>日付</th>
<th>発行社</th>
<th>刊種</th>
</tr>
<?php
for($i=0; $i<count($result["hits"]["hits"]); $i++){
echo "<tr>";
echo "<td>".$result["hits"]["hits"][$i]["_source"]["MIDASHI_kuromoji"]."</td>";
echo "<td>".$result["hits"]["hits"][$i]["_source"]["DATE"]."</td>";
echo "<td>".$result["hits"]["hits"][$i]["_source"]["SHA"]."</td>";
echo "<td>".$result["hits"]["hits"][$i]["_source"]["KIND"]."</td>";
echo "</tr>";
}
?>
</table>
以下余談。Kuromoji用とN-gram用のフィールドを定義するには、インデックス作成の段階で次のようなクエリを投げ、マッピング時に解析方法を指定すればOKです。analyzerとしてja_morphological_analyzerとngram_analyzerの2つを定義していることがポイントです。
PUT midashi/
{
"settings": {
"analysis": {
"analyzer": {
"ja_morphological_analyzer": {
"type": "custom",
"char_filter":[
"icu_normalizer"
],
"tokenizer": "kuromoji_tokenizer",
"filter": [
"kuromoji_baseform",
"kuromoji_part_of_speech",
"ja_stop",
"kuromoji_number",
"kuromoji_stemmer"
]
},
"ngram_analyzer": {
"tokenizer": "ngram_compounder"
}
},
"tokenizer": {
"ngram_compounder": {
"type": "ngram",
"min_gram": 2,
"max_gram": 3,
"token_chars": [
"letter",
"digit",
"punctuation",
"symbol",
"whitespace"
]
}
}
}
}
}
PUT midashi/_mapping/
{
"dynamic":"strict",
"properties": {
・
・
・
"MIDASHI_kuromoji": {"type": "text", "analyzer": "ja_morphological_analyzer"},
"MIDASHI_ng": {"type": "text", "analyzer": "ngram_analyzer"},
・
・
・
}
}