
ショコラ
ElasticSearch のクラスを作ってみた
きっと調べれば出てくると思うけど、理解するために作っちゃいました。

もっさん先輩
コメントもバグってます。
①データをベクトル化するのに AWS の タイタンを使用しました。
(お金がかからずベクトル化ってできるんですかね?といってもタイタンも物凄く安価ですけど)
②あまりにデカイ画像を送ったらエラーになったので、1000 x 1000 にしました。
<?php
namespace App\Libs;
use Aws\Sdk;
use Gemini\Data\Blob;
use Gemini\Enums\MimeType;
class ElasticSearch
{
public $timeout;
public $els_host;
public $els_port;
public $aws_region;
public $aws_key;
public $aws_secret;
public function __construct()
{
$this->timeout = config('elasticsearch.timeout');
$this->els_host = config('elasticsearch.host');
$this->els_port = config('elasticsearch.port');
$this->aws_region = config('elasticsearch.aws_region');
$this->aws_key = config('elasticsearch.aws_key');
$this->aws_secret = config('elasticsearch.aws_secret');
}
// 1000px x 1000px の画像を生成する
public static function image( $inputImage )
{
if($inputImage instanceof Image) return $inputImage;
$img = new Image( $inputImage );
if(!$img()) return null;
$rotate = (6 == ($img->meta_data['Orientation'] ?? 0)) ? 270 : 0;
$img->set_bgcolor_white()
->resize_square( 1000 )
->rotate( $rotate );
return $img;
}
// AI は blobImage を使う
public static function blobImage( $inputImage )
{
$img = self::image( $inputImage );
return $img ? new Blob(mimeType:MimeType::IMAGE_JPEG, data:$img->base64()) : null;
}
public function createBedrockRuntime()
{
$sdk = new Sdk([
'region' => $this->aws_region,
'credentials' => [
'key' => $this->aws_key,
'secret' => $this->aws_secret,
],
]);
return $sdk->createBedrockRuntime();
}
public function invokeModelTitanEmbedImageV1( $inputText = null, $inputImage = null )
{
$body = [];
$inputText and $body['inputText'] = $inputText;
if ($inputImage) {
$body['inputImage'] = ($inputImage instanceof Blob) ? $inputImage : $this->image( $inputImage )->base64();
}
$result = $this->createBedrockRuntime()->invokeModel([
'modelId' => 'amazon.titan-embed-image-v1',
'contentType' => 'application/json',
'accept' => 'application/json',
'body' => json_encode($body),
]);
return json_decode( $result['body'], true );
}
public function post( $table, $id, $request )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_doc/{$id}?pretty";
$headers = ['Content-Type: application/json'];
$post = json_encode( $request, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_POST ,true ); // POSTリクエストとして設定(明示的に)
curl_setopt($ch,CURLOPT_HTTPHEADER ,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS ,$post ); // ← ここでボディを送る
$json = curl_exec($ch);
return json_decode($json,true);
}
public function put( $table, $request )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}";
$headers = ['Content-Type: application/json'];
$post = json_encode( $request, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_CUSTOMREQUEST,'PUT' );
curl_setopt($ch,CURLOPT_HTTPHEADER ,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS ,$post ); // ← ここでボディを送る
$json = curl_exec($ch);
return json_decode($json,true);
}
public function get( $table, $id )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_doc/{$id}";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_HTTPGET ,true ); // GETリクエストとして設定(明示的に)
$json = curl_exec($ch);
$result = json_decode($json,true);
return ($result['found'] ?? true) ? $result : null;
}
public function getAll( $table )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_search";
$headers = ['Content-Type: application/json'];
$post = json_encode([
'query' => [
'match_all' => new \stdClass,
],
]);
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_HTTPGET ,true ); // GETリクエストとして設定(明示的に)
curl_setopt($ch,CURLOPT_HTTPHEADER ,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS ,$post ); // ← ここでボディを送る
$json = curl_exec($ch);
$result = json_decode($json,true);
return $result['hits']['hits'] ?? null;
}
public function del( $table, $id )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_doc/{$id}";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,'DELETE'); // GETリクエストとして設定(明示的に)
$json = curl_exec($ch);
return json_decode($json,true);
}
public function delAll( $table )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_CUSTOMREQUEST ,'DELETE'); // GETリクエストとして設定(明示的に)
$json = curl_exec($ch);
return json_decode($json,true);
}
public function mapping( $table )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_mapping?pretty";
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_HTTPGET ,true ); // GETリクエストとして設定(明示的に)
$json = curl_exec($ch);
return json_decode($json,true);
}
public function seach( $table, $request )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_search";
$headers = ['Content-Type: application/json'];
$post = json_encode( $request, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_HTTPGET ,true ); // GETリクエストとして設定(明示的に)
curl_setopt($ch,CURLOPT_HTTPHEADER ,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS ,$post ); // ← ここでボディを送る
$json = curl_exec($ch);
$result = json_decode($json,true);
return $result['hits']['hits'] ?? null;
}
public function knnSearch( $table, $request )
{
$url = "http://{$this->els_host}:{$this->els_port}/{$table}/_knn_search?pretty";
$headers = ['Content-Type: application/json'];
$post = json_encode( $request, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL ,$url );
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true ); // 結果を文字列で返す
curl_setopt($ch,CURLOPT_TIMEOUT, $this->timeout); // タイムアウト 10秒
curl_setopt($ch,CURLOPT_HTTPGET ,true ); // GETリクエストとして設定(明示的に)
curl_setopt($ch,CURLOPT_HTTPHEADER ,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS ,$post ); // ← ここでボディを送る
$json = curl_exec($ch);
$result = json_decode($json,true);
return $result['hits']['hits'] ?? null;
}
public function knnSearchByVector( $table, $query_vector, $k = 20, $num_candidates = 100 )
{
return $this->knnSearch($table,[
'knn' => [
'field' => 'vector',
'query_vector' => $query_vector,
'k' => $k, // 取得数
'num_candidates' => $num_candidates,
]
]);
}
public function knnSearchByMultimodal( $table, $text = null, $image = null, $k = 20, $num_candidates = 100 )
{
$vec = $this->invokeModelTitanEmbedImageV1( $text, $image );
return isset($vec['embedding']) ? $this->knnSearchByVector($table,$vec['embedding'],$k,$num_candidates) : null;
}
}
以上