PHP 上 Elasticsearch 應用


What is Elasticsearch?

Elasticsearch 是一套基於 Apache Lucene(TM) 的開源搜尋引擎。無論在開源或專有禮遇,Lucene 被認為至今最先進、性能最好、功能最齊全的搜尋引擎。
不過 Elasticsearch 不僅僅是 Lucene 和全文搜尋,我們還能這樣去描述它:
  • 分佈式的實時文進存儲,每個字段都被索引並可被搜尋。
  • 分佈式的實時分析搜尋引擎。
  • 可拓展至上百台服務器,處理PB級結構化或非結構化數據。

環境建置

第一步安裝 Elasticsearch 環境

安裝 ES 前請先確認 JAVA 環境已建置完成,可至 CMD 下指令 java -version
若無可至 JAVA官網 進行安裝及環境建置。
Elasticsearch 安裝有需多不同方式(待補其他方式)。
此處筆者至 Elasticsearch官網 直接下載最新版並解壓縮使用。
檔案包安裝好後由CMD直接進入該專案包下指令喚醒 Elasticsearch bin/elasticsearch
請注意喚醒 ES 不可使用 root 身份進行,並確認專案內部分檔案開放該身份存取(logs 等…)
如何確認 ES 是否安裝成功?
可直接使用網頁連結 http://localhost:9200/ 若出現畫面。
{
  "name" : "BSQmiuK",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "d6gTLQ0-Svy1WbTWSvKmig",
  "version" : {
    "number" : "6.2.4",
    "build_hash" : "ccec39f",
    "build_date" : "2018-04-12T20:37:28.497551Z",
    "build_snapshot" : false,
    "lucene_version" : "7.2.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}
代表大致上沒有問題。

如何在 php 上應用 Elasticsearch?

筆者此處使用 composer 檔案管理套件進行ES-PHP檔案安裝。
若無此套件可先進行環境建置。

PHP 上使用Elasticsearch

先在專案內新增 composer.json 檔並包含 elasticsearch-php (筆者編寫時最新版本需配合 php7,固取得版本 5.0 配合 PHP 5.6)。
composer.json 檔內容
{
  "require": {
    "elasticsearch/elasticsearch": "~5.0"
  }
}
完成後 cmd 到專案目錄下進行 composer install 進行套件安裝。
於專案內新增 index.php ,使用自動加載並實例化客戶端。
require 'vendor/autoload.php';
use Elasticsearch\ClientBuilder;

$client = ClientBuilder::create()->build();

建立索引資歷。將以下 code 添加至 index.php

$params = [
    'index' => 'my_index',
    'type'  => 'my_type',
    'id'    => 'my_id',
    'body'  => ['testField' => 'abc'],
];

$response = $client->index($params);
var_dump($response);
於網頁執行應該回得到頁面訊息
array (size=8)
  '_index' => string 'my_index' (length=8)
  '_type' => string 'my_type' (length=7)
  '_id' => string 'my_id' (length=5)
  '_version' => int 6
  'result' => string 'updated' (length=7)
  '_shards' => 
    array (size=3)
      'total' => int 2
      'successful' => int 1
      'failed' => int 0
  '_seq_no' => int 5
  '_primary_term' => int 1
返回 response 為 Elasticsearch 創建索引返回的 JSON 解碼關聯表。

依條件取得索引資料

$params = [
    'index' => 'my_index',
    'type' => 'my_type',
    'id' => 'my_id',
];

$response = $client->get($params);
var_dump($response);
我們可以依照索引設置及 get 取得文檔資料
array (size=6)
  '_index' => string 'my_index' (length=8)
  '_type' => string 'my_type' (length=7)
  '_id' => string 'my_id' (length=5)
  '_version' => int 1
  'found' => boolean true
  '_source' => 
    array (size=1)
      'testField' => string 'abc' (length=3)

依索引刪除資料

$deleteParams = [
    'index' => 'my_index',
];
$response = $client->indices()->delete($deleteParams);
print_r($response);
得到確認回應
Array ( [acknowledged] => 1 )

關鍵字搜尋

Elasticsearch 可對入稿之資料內容做比對搜尋。
關鍵字若下”後山大火雞”,那”後”、”山”、”大”、”火”、”雞”、”後山”、”火雞”(請自行依此類推)皆會成為搜尋條件並找出一定比重之資料。
讓我們來看看該怎麼做。
首先幫我入稿基本資料:
$params = [
    'index' => 'my_index2',
    'type'  => 'my_type',
    'body'  => ['count' => '後山大火雞'],
];
$client->index($params);

$params = [
    'index' => 'my_index',
    'type' => 'my_type',
    'body' => ['count' => '火雞'],
];
$client->index($params);

$params = [
    'index' => 'my_index',
    'type'  => 'my_type',
    'body'  => ['count' => '後山'],
];
$client->index($params);

$params = [
    'index' => 'my_index',
    'type' => 'my_type',
    'body' => ['count' => '後大'],
];
$client->index($params);
搜尋功能應用(其中僅入稿資料”後山” 不再搜尋關鍵字 “大火雞” 拆分比對資料內因故沒被顯示出來)。
$paramsSeach['body'] = array(
    'query' => array(
        'match' => array(
            'count' => '大火雞',
        ),
    )
);
$results = $client->search($paramsSeach);
var_dump($results['hits']['hits']);
上述指令會取得結果
array (size=3)
  0 => 
    array (size=5)
      '_index' => string 'my_index' (length=8)
      '_type' => string 'my_type' (length=7)
      '_id' => string 'S2fIPmMBYbAZcmkxgr3x' (length=20)
      '_score' => float 1.3862944
      '_source' => 
        array (size=1)
          'count' => string '火雞' (length=6)
  1 => 
    array (size=5)
      '_index' => string 'my_index2' (length=9)
      '_type' => string 'my_type' (length=7)
      '_id' => string 'SmfIPmMBYbAZcmkxgr2d' (length=20)
      '_score' => float 0.8630463
      '_source' => 
        array (size=1)
          'count' => string '後山大火雞' (length=15)
  2 => 
    array (size=5)
      '_index' => string 'my_index' (length=8)
      '_type' => string 'my_type' (length=7)
      '_id' => string 'TWfIPmMBYbAZcmkxg70I' (length=20)
      '_score' => float 0.2876821
      '_source' => 
        array (size=1)
          'count' => string '後大' (length=6)
搜尋應用還可對指定索引進行搜尋,於上方資料後山大火雞可發現其陣列內 index 欄位與其他幾項不同。
此時只要添加索引條件:
$paramsSeach['index'] = 'my_index';
由於此處指定搜尋 index,故會發現搜尋搜尋結果以濾掉 ['index' => 'my_index2']後山大火雞

————————————

以上為 Elasticsearch-php 基本應用。
其實有許多功能筆者皆未提到。
例: Elasticsearch 監聽程式(Elasticsearch-head)、或全域搜尋功能。還有許多分析功能。
待筆者日後對 Elasticsearch 掌握度更加精進後,在進行文件補充動作

————————————-

以下附上 DB 相關進階應用:
DB 與 ES 數據庫連結(conn.php)
<?php
require_once 'vendor/autoload.php';

//連結 MySql 資料庫
function get_conn()
{
    @$conn = mysql_connect("localhost", "root", "root") or die("error connecting");
    mysql_select_db("DBName", $conn); # *DB 請自行填入
    mysql_query("SET NAMES 'UTF8'");
    return $conn;
}

//由 DB 取得資料並回存ES中
function create_index($maxId, $client)
{
    //取捯 DB 資料
    $sql = "SELECT * FROM tableName where id > $maxId limit 0,300"; # *資料表名稱請自行填入
    get_conn();
    @$result_bugs = mysql_query($sql);
    while (@$row = mysql_fetch_assoc(@$result_bugs)) {
        $rtn[] = $row;
    }

    foreach ($rtn as $val) {
        $params = array();
        $params['body'] = array(
            'id'    => $val['id'],
            'count' => $val['count'], # *請自行修改比對欄位及名稱
        );
        $params['index'] = 'index';
        $params['type'] = 'title';

        $client->index($params);
    }

    return (count($rtn) == 300) ? $val['id'] : false;
}

// set_time_limit(0);
$client = Elasticsearch\ClientBuilder::create()->setHosts(['localhost'])->build();
//刪除所有數據
// $client->indices()->delete(['index' => 'index']);

$a = true;
$maxId = 0;
while ($a) {
    $maxId = create_index($maxId, $client);
    if (empty($maxId)) {
        $a = false;
    }
}
執行php(testConn.php)
<?php
//引入 DB 連結
require 'conn.php';
require_once 'vendor/autoload.php';
function search($keyword, $page = 0, $size = 20)
{
    //實體化對象
    $client = Elasticsearch\ClientBuilder::create()->setHosts(['localhost'])->build();
    //查詢數據庫拼裝
    $params = array();
    $params['index'] = 'index';
    $params['type'] = 'title';
    $params['body']['query']['match']['count'] = $keyword;
    $params['from'] = $page;
    $params['size'] = $size;

    //查詢
    $rtn = $client->search($params)['hits'];

    //結果組裝
    $data['total'] = $rtn['total'];
    $data['lists'] = array_column($rtn['hits'], '_source');
    $data['lists'] = formartData(array_column($data['lists'], 'id'));

    return $data;
}

function formartData($ids)
{
    $ids = implode($ids, ',');
    $sql = "select * from articles where id in($ids)";
    $data = mysql_query($sql);

    $rtn = [];
    while (@$row = mysql_fetch_assoc(@$data)) {
        $rtn[] = $row;
    }

    return $rtn;
}

$q0 = isset($_GET['q']) ? $_GET['q'] : 'SQL注入';
$num = "15"; //每頁顯示比數
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$offset = ($page - 1) * $num;
$esData = search($q0, $offset, $num);

var_dump($esData);
使用方式:呼叫程式並賦予搜尋值。
http://localhost/testConn.php?q=
  • 注意1:索引(index)型態(type) 設定時必須小寫。
聲明:此篇教學僅提供學習與交流使用,請勿用於商業用途。

參考資料


留言

這個網誌中的熱門文章

HTML JS 動態顯示 input file 選取內容

PHP OO 基礎教學