ฟีเจอร์ใน Elasticsearch กับงาน Laravel ตอนที่1 (แบบพื้นฐาน)

Patchara Chukiatkajohn
I GEAR GEEK
Published in
5 min readJul 22, 2019

บล็อกนี้ผมจะอธิบายเรื่องพื้นฐาน, หน้าที่การทำงาน, การเรียกใช้งาน และฟังก์ชันพื้นฐานต่างๆของ Elasticsearch ที่ผู้สนใจจะทำโปรแกรมค้นหาต้องรู้เมื่อจะนำมาใช้ในโปรเจคที่เขียนด้วยภาษา PHP ผ่านไลบรารี่หลักอย่าง elasticsearch/elasticsearch กับไลบรารี่อีกตัวหนึ่งที่มีชื่อว่า cviebrock/laravel-elasticsearch

เริ่มต้นใช้งานแบบง่ายๆ

แน่นอนคุณต้องลงตัว composer กับ laravel พร้อมกับสร้างโปรเจค laravel ขึ้นมาก่อน แต่ผมขอข้ามส่วนนี้ไปนะครับ สามารถดูได้จาก Laravel installation document

หลังจากสร้างโปรเจค laravel ขึ้นมาแล้ว ก็เริ่มที่การลงไลบรารี่หลักเพื่อใช้งาน service ต่างๆของ elasticsearch ได้ผ่านคำสั่ง

composer require elasticsearch/elasticsearch

จากนั้นก็ทำการลงตัวไลบรารี่นี้ที่ช่วยให้การเขียนคำสั่งใช้งานฟีเจอร์ต่างๆของ elasticsearch ในโปรเจค laravel ให้เขียนง่าย อ่านง่าย เข้าใจแบบง่ายๆเหมือนปอกกล้วย ด้วยคำสั่ง

composer require cviebrock/laravel-elasticsearch

จากนั้นถ้าก็ทำการตั้งค่าโฮสสำหรับ elasticsearch ในโปรเจคของเราที่ไฟล์ “.env” ซึ่งก่อนอื่นเลยเราต้องทำการ publish ตัว ServiceProvider เพื่อที่จะดึงส่วนของการตั้งค่ามาเปลี่ยนแปลงการตั้งค่าพื้นฐานต่างๆที่เราต้องการ ด้วยคำสั่ง

php artisan vendor:publish --provider= "Cviebrock\LaravelElasticsearch\ServiceProvider"

จากนั้นก็กำหนดชื่อโฮส, เลขพอร์ต, โปรโตรคอล(http/https) และชื่อผู้ใช้งานกับรหัสผ่าน ของเราเองได้เลย (พอร์ตพื้นฐานของ elasticsearch คือ 9200) แต่ถ้าเราสร้างโฮสและจัดการ elasticsearch ของเราผ่าน Bonsai ( บริการโฮส elasticsearch ) ก็สามารถนำค่าต่างๆของ URL ที่สร้างไว้ของเรามาใส่การตั้งค่าที่ไฟล์ “.env” นี้ได้

ELASTICSEARCH_HOST=localhost
ELASTICSEARCH_PORT=9200
ELASTICSEARCH_SCHEME=http
ELASTICSEARCH_USER=
ELASTICSEARCH_PASS=

เสร็จแล้วก็ไปเริ่มใช้งาน Elasticsearch ในโปรเจค laravel ของเรา

ลักษณะของ Elasticsearch

Elasticsearch นั้นคือเครื่องมือที่ใช้สร้างโปรแกรมค้นหา ซึ่งมีลักษณะการทำงานที่ดีเยี่ยม คือการให้บริการในรูปแบบ REST APIs(POST, PUT, GET, DELETE) ที่ครอบคลุมมากพอที่จะตอบสนองความต้องการในการสร้างระบบค้นหาของในแบบของคุณ มีความสามารถตั้งแต่ การเก็บรวบรวม, การจัดการ และการค้นหาข้อมูล

มาเริ่มดูฟีเจอร์ของตัว elasticsearch กันเลย

ขอแนะนำอีกเครื่องมือหนึ่งก่อน คือ “Kibana” (เครื่องมือ Visualize สำหรับแสดงผลข้อมูลจาก Elasticsearch)

โดยถ้าเราโฮส elasticsearch ผ่าน Bonsai (บริการโฮส elasticsearch) เราสามารถสร้าง Clusters ภายใน Bonsai แล้วเข้าใช้งานในส่วนของ Kibana ซึ่งส่วนที่น่าสนใจเลยคือส่วนของ “Dev tools” ที่เราสามารถเขียน REST methods ตาม document ของ elasticsearch เพื่อทดลองการทำงานรูปแบบต่างๆได้ทันทีไม่ต้องลงอะไรเพิ่มเติม

มาเริ่มดูฟีเจอร์พื้นฐานของ elasticsearch กับการเขียนด้วย PHP กันเลยครับ !!!

การสร้าง Index

  • การสร้าง Index ในรูปแบบ REST ของ elasticsearch
PUT index_name
{
"settings" : {
"number_of_shards" : 1,
"number_of_replicas" : 2
},
"mappings" : {
"properties" : {
"field1" : { "type" : "text" },
"field2" : { "type" : "integer" }
}
}
}
  • การสร้าง Index ในรูปแบบ PHP
public function createIndex() {

$parameters = [
'index' => 'index_name',
'body' => [
'settings' => [
'number_of_shards' => 1,
'number_of_replicas' => 2,
],
'mappings' => [
'properties' => [
'field1' => [
'type' => 'text'
],
'field2' => [
'type' => 'integer'
]
]
]
]
];

Elasticsearch::indices()->create($parameters);
}

เกร็ดความรู้ คู่ elasticsearch:

  1. “settings” คือ ส่วนของการกำหนดค่าของ index เช่น จำนวนชุดข้อมูล
  2. “mapping” คือ ส่วนการกำหนดโครงสร้าง รวมถึงลักษณะของข้อมูลใน index
  3. “number_of_shards” คือ จำนวนเครื่อง, จำนวนเซิฟเวอร์ หรือ หนึ่งชุด elasticsearch ที่ได้สร้างเอาไว้ ที่เราจะเก็บ
  4. “number_of_replicas” คือ จำนวนตัวสำรอง หรือตัว backup ของแต่ละ “shard” ซึ่งถ้ามีตัวไหนล่ม หรือตายไป มันก็ยังสามารถทำงานได้อยู่จากชุดสำรองอื่นๆ

เกร็ดความรู้เสริม:

  1. Cluster คือ กลุ่มของ Node หรือเซิฟเวอร์ ที่อยู่ด้วยกัน
  2. Node คือ หนึ่งเซิฟเวอร์ที่มี Index อยู่ภายในตัวมันเอง (เกิดจากการสร้าง Index ของ elasticsearch)
  3. Shard คือ ตัวแบ่งข้อมูล Index

เช่น Index หนึ่งมีขนาด 500GB ซึ่งเราเห็นว่ามันใหญ่ไปสำหรับการเข้าไปค้นหาข้อมูลในทีเดียวสำหรับที่ที่เดียว เราก็สามารถแบ่งเป็น 2 shards ซึ่งมันก็คือ shard ละ 250 GB เพื่อให้การค้นหารวดเร็ว มีประสิทธิภาพมากยิ่งขึ้น

!! มันเร็วขึ้นยังไง !! การแยก shard จะทำให้ตัว elasticsearch ประมวลผลแบบคู่ขนานไปด้วย จากที่เราค้นหาจาก shard เดียว 500GB ซึ่งจะใช้เวลามากขึ้นไปอีกหากมีขนาดข้อมูลรวมที่มากขึ้น มาเปลี่ยนเป็น ค้นหาแบบคู่ขนานทีเดียวใน 2 shards ที่มีพื้นที่โดยรวมแต่ละ shard หารลดลงกันไปตามจำนวน 2 shard(250GB/shard) นั่นเอง

4. Replica คือ ตัวข้อมูลสำรอง หรือตัวก๊อปปี้ของข้อมูล Index ที่มีอยู่ทั้งหมดใน Node นั้นๆ

เช่น จากรูปด้านบนนี้ Node1 มีข้อมูล Index อยู่ทั้งหมด 1 shard และมี 2 replica ซึ่งในแต่ละ replica (ทั้ง replica1 และ replica2) นั้นคือข้อมูลเดียวกัน เหมือนกันกับใน shard นั้นทั้งหมด, หรือจากรูปด้านบน Node2 มีข้อมูล Index อยู่ทั้งหมด 2 shard และมี 2 replica โดยในแต่ละ replica ของทั้ง 2 replica จะมีข้อมูลของทั้ง 2 shard อยู่ (ใน 1 replica มีข้อมูลจากทั้ง 2 shard)

มาดูฟีเจอร์พื้นฐานต่อไปกันเลยครับ

การเพิ่มข้อมูลเข้า Index

หลังจากสร้าง Index ขึ้นมาแล้ว เราก็ต้องเพิ่มข้อมูลเข้าไปเพื่อการค้นหาของเรา

  • การเพิ่มข้อมูลทีละข้อมูลใน Index ในรูปแบบ REST ของ elasticsearch
POST /index_name
{
“field1”: “field1_value”,
“field2” : "field2_value"
}
  • การเพิ่มข้อมูลทีละข้อมูลใน Index ในรูปแบบ PHP
public function insertDataToIndex() {     $parameters = [
'index' => 'index_name',
'body' => [
'field1' => 'field1_value',
'field2' => 'field2_value'
]
];
Elasticsearch::index($parameters);
}

การลบ Index

  • การลบ Index ในรูปแบบ REST ของ elasticsearch
DELETE /index_name
  • การลบ Index ในรูปแบบ PHP
public function deleteIndex() {     $parameters = [
'index' => 'index_name'
];
Elasticsearch::indices()->delete($parameters);
}

การเรียก หรือค้นหา Index

  • การเรียก Index ในรูปแบบ REST ของ elasticsearch
GET /index_name    หรือ    GET /index_name/_search
  • การเรียก Index ในรูปแบบ PHP
public function searchIndex() {    $parameters = [
'index' => 'index_name'
];
$response = Elasticsearch::search($parameters);
}

การเรียก หรือค้นหา Index โดยการ query

สมมติว่า ผมต้องการค้นหาข้อมูล ของผู้ใช้งานที่

  1. ชื่อต้นคือ John
  2. ต้องการผลการค้นหาทั้งหมด 5 รายชื่อ
  3. ค้นหาจากก้อนข้อมูล index ชื่อ user ที่มีอยู่ หรือถูกสร้างเอาไว้แล้ว
  • การค้นหา Index โดยวิธี match จากการ query ในรูปแบบ REST ของ elasticsearch
GET /user/_search
{
"from" : 0,
"size" : 5,
"query": {
"match" : {
"firstname" : "John",
"boost": 2
}
}
}
  • การค้นหา Index โดยวิธี match จากการ query ในรูปแบบ PHP
public function searchIndexByFirstName() {$parameters = [
'index' => 'user',
'body' => [
'from' => 0,
'size' => 5,
'query' => [
'match' => [
'firstname' => [
'query' => 'John',
'boost': 2
]
]
]
]
];

Elasticsearch::search($parameters);
}

*** ข้อแนะนำ: โดยปกติแล้ว ถ้าเราไม่ได้ตั้งค่า “from” กับ “size” ไว้ ค่าพื้นฐานในการแสดงจำนวนข้อมูลผลลัพธ์จากการค้นหา จะได้ผลลัพธ์ออกมาเพียง 10 ข้อมูลเท่านั้น ฉะนั้นถ้าเราต้องการแสดงข้อมูลน้อย หรือมากกว่า 10 ข้อมูล ผมแนะนำให้ตั้งค่าในส่วนนี้เอาไว้นะครับ

เกร็ดความรู้เสริม:

“boost” ใช้เพื่อเพิ่มน้ำหนักในการค้นหาในแต่ละ field (สามารถใช้งานได้เฉพาะกับการทำ term query เท่านั้น ส่วนการทำ prefix, range and fuzzy queries จะไม่ถูก boost ได้) โดยถ้าเราไม่ได้ตั้งค่า boost จะเป็น 1.0

อย่างในตัวอย่างโค๊ดผมตั้งค่า boost เป็น 2 นั้นหมายความว่าผมเพิ่มน้ำหนักในการคิดคะแนนสำหรับการค้นหาของ elasticsearch ให้กับฟิลด์ข้อมูล “user” เป็น 2 เท่านั่นเอง

ต่อมาเป็นอีกฟีเจอร์ ที่การทำงานแทบจะเหมือนกันเลย

  • การค้นหา Index โดยวิธีใช้ term จากการ query ในรูปแบบ REST ของ elasticsearch
GET /user/_search
{
"query": {
"term": {
"firstname": {
"value": "John",
"boost": 1.2
}
}
}
}
  • การค้นหา Index โดยวิธี match จากการ query ในรูปแบบ PHP
public function searchIndexByFirstName() {$parameters = [
'index' => 'user',
'body' => [
'from' => 0,
'size' => 15,
'query' => [
'term' => [
'firstname' => [
'query' => 'John',
'boost': 1.2
]
]
]
]
];

Elasticsearch::search($parameters);
}

เกร็ดความรู้เสริม:

การทำ query โดยใช้ “match” กับ “term” มีลักษณะที่แตกต่างกัน (ผมขออธิบายเพียงนิยามความต่างก่อนนะครับ เดี๋ยวบล็อกนี้จะยาวเกินไป)

แตกต่างยังไง → การใช้ term จะทำให้การค้นหา ค้นหาเฉพาะในฟิลด์ข้อมูล ที่มีค่านั้น ในรูปแบบนั้นเลย แต่!!! การใช้ match นั้นเราจะสามารถสร้างการ query ภายใน match ได้อีก(ถ้าเราเก็บข้อมูลเป็นอ๊อปเจ็กที่มีฟิลด์ข้อมูลย่อยในนั้นด้วย) รวมถึงยังสามารถใช้การ analyze ข้อมูลนั้นๆได้ด้วย

ถ้าต้องการค้นหาทั้งหมดที่มีล่ะ

  • การค้นหา Index โดยวิธี match_all หรือก็คือ การเรียกดูข้อมูลทั้งหมดใน elasticsearch ในรูปแบบ REST ของ elasticsearch
GET /user/_search
{
"from" : 0,
"size" : 100,
"query": {
"match_all": { "boost" : 1.2 }
}
}
  • การค้นหา Index การค้นหา Index โดยวิธี match_all ในรูปแบบ PHP
public function searchAllIndex() {$parameters = [
'index' => 'user',
'body' => [
'from' => 0,
'size' => 100,
'query' => [
'match_all' => [
'boost' => 1.0
]
]
]
];

Elasticsearch::search($parameters);
}

ถ้าต้องการค้นหาจากช่วงค่าจำนวนหนึ่งล่ะ

  • การค้นหา Index โดยวิธีแบบใช้ range จากการ query ในรูปแบบ REST ของ elasticsearch

สมมติต้องการหาคนที่มีอายุอยู่ในช่วง 15 ถึง 20 ปีมา 100 คนจากก้อนข้อมูลใน elasticsearch ของเรา

GET /user/_search
{
"from" : 0,
"size" : 100,
"query": {
"range" : {
"age" : {
"gte" : 15,
"lte" : 20
}
}
}
}
  • การค้นหา Index โดยวิธีแบบใช้ range จากการ queryในรูปแบบ PHP
public function searchIndexByAgeRange() {$parameters = [
'index' => 'user',
'body' => [
'from' => 0,
'size' => 100,
'query' => [
'range' => [
'age' => [
'gte' => 15,
'lte' => 20
]
]
]
]
];

Elasticsearch::search($parameters);
}

*** เราสามารถใช้การกำหนดค่าได้ 4 แบบจาก range คือ gt(มากกว่า), lt(น้อยกว่า), gte(มากกว่าเท่ากับ) และ lte(น้อยกว่าเท่ากับ)

!!! สำหรับบล็อกนี้ก็ขอจบแต่เพียงเท่านี้นะครับ !!!

ในตอนต่อไปผมจะพูดถึง

การเพิ่มข้อมูลทีละมากๆด้วยการ bulk,

การ query ข้อมูลหลากหลายประเภทในครั้งเดียวโดยการใช้ bool และ การประเมินคะแนน ในการค้นหา,

การใช้ และทำ analyzer, filterกับ tokenizer

รวมไปถึงการทำ Index Template เพื่อการสร้าง Index หลายๆ Index ได้โดยไม่ต้องมานั่งตั้งค่า setting, mapping, analyzer, tokenizer, filterให้ทีละ Index

รอติดตามการใช้งาน รวมถึงฟีเจอร์เชิงลึก ที่คุณสามารถนำมาพัฒนาระบบค้นหาที่สามารถค้นหาข้อมูลที่ซับซ้อนได้ดีมากขึ้น ในตอนถัดไปนะครับ

--

--