
ในยุคดิจิทัลที่ข้อมูลหลั่งไหลเข้ามามหาศาล การค้นหาข้อมูลที่ตรงใจและรวดเร็วกลายเป็นหัวใจสำคัญของประสบการณ์ผู้ใช้งานที่ดีเยี่ยมครับ ไม่ว่าจะเป็นเว็บไซต์อีคอมเมิร์ซ, แพลตฟอร์มข่าวสาร, ระบบจัดการเอกสาร หรือแม้กระทั่งแอปพลิเคชันมือถือ ระบบค้นหา (Search System) ที่มีประสิทธิภาพ ไม่ใช่แค่การจับคู่คำ (keyword matching) ธรรมดาอีกต่อไป แต่ต้องสามารถเข้าใจบริบท, จัดลำดับความสำคัญ, และมอบผลลัพธ์ที่ “ฉลาด” และแม่นยำที่สุดครับ
วันนี้เราจะมาเจาะลึกถึงหนึ่งในเครื่องมือที่ทรงพลังที่สุดสำหรับการสร้างระบบ Full-text Search อัจฉริยะ นั่นคือ Elasticsearch ครับ Elasticsearch ไม่ได้เป็นเพียงแค่ฐานข้อมูลธรรมดา แต่เป็น Search Engine แบบกระจาย (Distributed Search Engine) ที่ออกแบบมาเพื่อการค้นหาข้อความเต็มรูปแบบ (Full-text Search) โดยเฉพาะ ด้วยความสามารถในการประมวลผลข้อมูลปริมาณมหาศาลได้อย่างรวดเร็ว มีความยืดหยุ่นสูง และรองรับฟีเจอร์การค้นหาขั้นสูงมากมาย ทำให้ Elasticsearch กลายเป็นตัวเลือกอันดับต้นๆ สำหรับองค์กรและนักพัฒนาที่ต้องการสร้างระบบค้นหาที่เหนือกว่าครับ
บทความนี้จะพาคุณไปทำความเข้าใจตั้งแต่พื้นฐานของ Full-text Search, เหตุผลว่าทำไม Elasticsearch ถึงเป็นตัวเลือกที่ยอดเยี่ยม, ลงลึกในแนวคิดหลัก, วิธีการใช้งานขั้นพื้นฐานไปจนถึงฟีเจอร์ขั้นสูง เช่น การปรับแต่ง Analyser สำหรับภาษาไทย, การสร้างระบบแนะนำคำค้นหา (Suggester), การจัดลำดับความเกี่ยวข้อง (Relevance Scoring) และอื่นๆ อีกมากมาย พร้อมตัวอย่างโค้ดที่ใช้งานได้จริง เพื่อให้คุณสามารถนำไปประยุกต์ใช้สร้างระบบค้นหาอัจฉริยะของคุณเองได้ทันทีครับ
สารบัญ
- Full-text Search คืออะไร? และทำไมถึงสำคัญ?
- ทำไมต้อง Elasticsearch สำหรับการค้นหาแบบ Full-text?
- แนวคิดหลักของ Elasticsearch ที่คุณควรรู้
- การติดตั้งและเริ่มต้นใช้งาน Elasticsearch
- การค้นหาข้อมูลเบื้องต้น: Match และ Term Query
- ยกระดับการค้นหาด้วยฟีเจอร์ขั้นสูง
- การปรับแต่ง Analyzer สำหรับภาษาไทยโดยเฉพาะ
- Relevance Scoring และการเพิ่มประสิทธิภาพการจัดอันดับ
- Aggregations และการสร้าง Faceted Search
- Best Practices สำหรับการสร้างระบบค้นหาด้วย Elasticsearch
- เปรียบเทียบ Elasticsearch กับ Full-text Search ใน RDBMS
- Use Cases: ตัวอย่างการนำ Elasticsearch ไปใช้งานจริง
- FAQ: คำถามที่พบบ่อยเกี่ยวกับ Elasticsearch Full-text Search
- สรุปและ Call-to-Action
Full-text Search คืออะไร? และทำไมถึงสำคัญ?
Full-text Search หรือการค้นหาข้อความเต็มรูปแบบ คือเทคนิคการค้นหาข้อมูลในชุดเอกสารขนาดใหญ่ (Corpus) โดยไม่จำกัดอยู่แค่การค้นหาคำที่ตรงกันทั้งหมด (Exact Match) เพียงอย่างเดียวครับ แต่จะพยายามค้นหาเอกสารที่มีเนื้อหาที่ “เกี่ยวข้อง” กับคำค้นหาของผู้ใช้งานมากที่สุด
ลองนึกภาพการค้นหาในฐานข้อมูลแบบดั้งเดิม (RDBMS) เช่น SQL Query ที่ใช้ LIKE '%คำค้นหา%' ครับ วิธีนี้มีข้อจำกัดหลายอย่าง:
- ประสิทธิภาพต่ำ: การใช้
LIKE '%...'มักจะไม่สามารถใช้ Index ได้อย่างเต็มที่ ทำให้ต้องสแกนข้อมูลทั้งหมด (Full Table Scan) ซึ่งช้ามากเมื่อมีข้อมูลจำนวนมาก - ไม่เข้าใจความหมาย: ไม่สามารถจัดการกับคำพ้องความหมาย, คำที่ใกล้เคียงกัน, หรือคำที่สะกดผิดได้
- ไม่มีการจัดอันดับความเกี่ยวข้อง: ผลลัพธ์ที่ได้จะไม่มีการบอกว่าเอกสารไหนมีความเกี่ยวข้องกับคำค้นหามากน้อยแค่ไหน
- ไม่รองรับฟีเจอร์ขั้นสูง: เช่น การแนะนำคำ (Suggestion), การเน้นคำ (Highlighting), การค้นหาแบบ Faceted (แบ่งกลุ่มตามหมวดหมู่)
ในทางกลับกัน Full-text Search ที่ดีจะทำงานโดยการสร้าง Inverted Index ซึ่งเป็นโครงสร้างข้อมูลพิเศษที่เก็บตำแหน่งของทุกๆ คำในทุกๆ เอกสารไว้ ทำให้สามารถค้นหาได้อย่างรวดเร็วและมีประสิทธิภาพสูง นอกจากนี้ยังมีการวิเคราะห์ข้อความ (Text Analysis) เพื่อปรับแต่งคำค้นหาและข้อมูลให้เหมาะสมกับการค้นหา เช่น การแปลงคำเป็นตัวพิมพ์เล็ก, การลบคำที่ไม่สำคัญ (Stop Words), การแยกคำ (Tokenization) สำหรับภาษาที่ไม่มีการเว้นวรรคอย่างภาษาไทย, และการจัดอันดับความเกี่ยวข้องด้วยอัลกอริทึมต่างๆ ครับ
ความสำคัญของ Full-text Search จึงอยู่ที่ความสามารถในการเปลี่ยนข้อมูลดิบให้กลายเป็นข้อมูลที่ “ค้นหาได้” และ “เข้าถึงได้” อย่างชาญฉลาด ทำให้ผู้ใช้สามารถค้นหาสิ่งที่ต้องการได้อย่างรวดเร็วและแม่นยำ ส่งผลให้ประสบการณ์การใช้งานดีขึ้นอย่างเห็นได้ชัดครับ
ทำไมต้อง Elasticsearch สำหรับการค้นหาแบบ Full-text?
Elasticsearch ได้รับความนิยมอย่างแพร่หลายและกลายเป็นมาตรฐานสำหรับการทำ Full-text Search ด้วยเหตุผลหลายประการดังนี้ครับ
-
ความเร็วและประสิทธิภาพสูง:
Elasticsearch ถูกสร้างขึ้นบน Apache Lucene ซึ่งเป็นไลบรารี Full-text Search ที่มีประสิทธิภาพสูงที่สุดตัวหนึ่งในโลกครับ ด้วยสถาปัตยกรรมแบบกระจาย (Distributed Architecture) และการใช้ Inverted Index ทำให้สามารถค้นหาและวิเคราะห์ข้อมูลปริมาณมหาศาล (Big Data) ได้ในระดับใกล้เคียงกับ Real-time (Near Real-time) เลยทีเดียวครับ
-
รองรับการขยายขนาด (Scalability) ได้อย่างง่ายดาย:
คุณสามารถเพิ่ม Node เข้าไปใน Cluster ได้อย่างง่ายดายเพื่อรองรับปริมาณข้อมูลและการค้นหาที่เพิ่มขึ้น Elasticsearch จะจัดการการกระจายข้อมูล (Sharding) และการทำสำเนา (Replication) ให้โดยอัตโนมัติ ทำให้ระบบมีความทนทานต่อความผิดพลาด (Fault Tolerance) และสามารถขยายขนาดได้ตามความต้องการครับ
-
ความยืดหยุ่นของ Data Model:
Elasticsearch เป็น NoSQL Database ที่เป็นแบบ Schema-less (หรือ Schema-on-write) นั่นหมายความว่าคุณไม่จำเป็นต้องกำหนดโครงสร้างข้อมูลที่ตายตัวล่วงหน้า คุณสามารถเพิ่มฟิลด์ใหม่ๆ เข้าไปใน Document ได้ตลอดเวลา ซึ่งช่วยให้นักพัฒนาสามารถปรับเปลี่ยนหรือเพิ่มเติมข้อมูลได้ง่ายและรวดเร็วครับ
-
ฟีเจอร์การค้นหาขั้นสูงครบครัน:
นอกจากการค้นหาแบบ Match ธรรมดาแล้ว Elasticsearch ยังมาพร้อมฟีเจอร์ที่ทำให้ระบบค้นหาของคุณ “ฉลาด” ยิ่งขึ้น เช่น
- Fuzzy Search: ค้นหาได้แม้สะกดผิดเล็กน้อย
- Phrase Search: ค้นหาวลีที่ตรงกัน
- Autocomplete/Suggesters: แนะนำคำค้นหาขณะพิมพ์
- Highlighting: เน้นคำที่ค้นหาเจอในผลลัพธ์
- Aggregations: การรวมกลุ่มและวิเคราะห์ข้อมูลเพื่อสร้าง Faceted Search หรือ Dashboards
- Geo-spatial Search: ค้นหาข้อมูลตามพิกัดทางภูมิศาสตร์
- Relevance Scoring: จัดอันดับความเกี่ยวข้องของผลลัพธ์ได้อย่างแม่นยำและปรับแต่งได้
-
ระบบนิเวศ (Ecosystem) ที่แข็งแกร่ง:
Elasticsearch เป็นส่วนหนึ่งของ Elastic Stack (หรือเดิมคือ ELK Stack) ซึ่งประกอบด้วย:
- Kibana: เครื่องมือ Visualization และ UI สำหรับการจัดการ, ค้นหา, และวิเคราะห์ข้อมูลใน Elasticsearch
- Logstash: เครื่องมือสำหรับรวบรวม, ประมวลผล, และส่งข้อมูลจากแหล่งต่างๆ เข้าสู่ Elasticsearch
- Beats: Agent ขนาดเล็กสำหรับส่งข้อมูลจาก Server ไปยัง Logstash หรือ Elasticsearch โดยตรง
ทั้งหมดนี้ทำงานร่วมกันได้อย่างราบรื่น ทำให้การจัดการข้อมูลและการสร้างระบบค้นหาทำได้ง่ายขึ้นมากครับ
-
รองรับภาษาธรรมชาติ (Natural Language Processing – NLP) และภาษาไทย:
Elasticsearch มี Analyser ที่สามารถปรับแต่งให้เหมาะสมกับภาษาต่างๆ ได้ รวมถึงภาษาไทยที่มีความซับซ้อนในการแยกคำ ทำให้สามารถสร้างระบบค้นหา Full-text Search สำหรับภาษาไทยที่มีประสิทธิภาพสูงได้ครับ
-
ชุมชนผู้ใช้งานที่ใหญ่และแข็งแกร่ง:
มีเอกสารประกอบ, บทความ, และฟอรัมช่วยเหลือมากมาย ทำให้คุณสามารถหาคำตอบและแก้ไขปัญหาต่างๆ ได้อย่างรวดเร็วครับ
ด้วยเหตุผลทั้งหมดนี้ ทำให้ Elasticsearch เป็นมากกว่าแค่เครื่องมือค้นหา แต่เป็นแพลตฟอร์มที่สมบูรณ์แบบสำหรับการสร้างระบบค้นหาอัจฉริยะที่สามารถรองรับความต้องการทางธุรกิจในยุคปัจจุบันและอนาคตได้อย่างแท้จริงครับ
แนวคิดหลักของ Elasticsearch ที่คุณควรรู้
ก่อนจะลงมือใช้งาน Elasticsearch สิ่งสำคัญคือต้องทำความเข้าใจแนวคิดพื้นฐานบางอย่างที่แตกต่างจากฐานข้อมูลเชิงสัมพันธ์ (Relational Database) ครับ การทำความเข้าใจสิ่งเหล่านี้จะช่วยให้คุณออกแบบและใช้งานระบบได้อย่างมีประสิทธิภาพ
Cluster, Node, Shard, Replica
-
Cluster:
คือกลุ่มของ Node หนึ่งตัวหรือมากกว่าที่ทำงานร่วมกันเพื่อเก็บข้อมูลทั้งหมดและให้ความสามารถในการทำ Index และ Search โดย Cluster แต่ละ Cluster จะมีชื่อเฉพาะ (Cluster Name) ใช้สำหรับระบุตัวตนครับ
-
Node:
คือ Server ตัวเดียวที่รัน Elasticsearch Instance หนึ่งตัว Node จะเป็นส่วนหนึ่งของ Cluster และมีบทบาทเฉพาะ เช่น Data Node (เก็บข้อมูล), Master Node (จัดการ Cluster), Ingest Node (ประมวลผลข้อมูลก่อน Index) เป็นต้นครับ
-
Index:
ใน Elasticsearch, Index เปรียบเสมือนฐานข้อมูล (Database) ใน RDBMS หรือ Schema ที่เก็บ Document ที่มีคุณสมบัติคล้ายกันไว้ด้วยกันครับ Index แต่ละตัวจะมี Mapping กำหนดโครงสร้างและวิธีการวิเคราะห์ข้อมูลของแต่ละฟิลด์
-
Shard:
เป็นกลไกที่ทำให้ Elasticsearch สามารถขยายขนาด (Scale) และกระจายข้อมูลได้ Shard คือหน่วยย่อยของ Index ครับ Index หนึ่งตัวจะถูกแบ่งออกเป็น Shard จำนวนหนึ่ง โดยแต่ละ Shard เป็นเหมือน Lucene Index ที่สมบูรณ์แบบตัวหนึ่ง ข้อมูลใน Index จะถูกกระจายไปเก็บใน Shard ต่างๆ Shard ช่วยให้คุณสามารถกระจายข้อมูลและการประมวลผลไปยัง Node ต่างๆ ใน Cluster ได้ ทำให้สามารถจัดการข้อมูลขนาดใหญ่และประมวลผลการค้นหาแบบขนานได้ครับ
-
Replica:
คือสำเนาของ Shard ครับ Replica Shard มีไว้เพื่อวัตถุประสงค์หลักสองประการคือ:
- High Availability (ความพร้อมใช้งานสูง): หาก Node ที่มี Primary Shard เกิดล้มเหลว Replica Shard จะถูกโปรโมตขึ้นมาเป็น Primary Shard แทน ทำให้ระบบยังคงทำงานได้ต่อเนื่อง
- Performance (ประสิทธิภาพ): Query สามารถถูกรันบน Replica Shard ได้ ทำให้ช่วยกระจายโหลดการค้นหาและเพิ่ม Throughput ของ Cluster ครับ
โดยปกติแล้ว Shard จะประกอบด้วย Primary Shard และ Replica Shard ครับ เช่น Index หนึ่งมี 5 Primary Shard และแต่ละ Primary Shard มี 1 Replica Shard ก็เท่ากับว่า Index นั้นมี Shard ทั้งหมด 5 Primary + (5 Primary * 1 Replica) = 10 Shard ครับ
Index, Document, Type (ในเวอร์ชันเก่า)
-
Index:
ตามที่กล่าวไปข้างต้น เปรียบเสมือนฐานข้อมูลหรือตารางใน RDBMS แต่มีความยืดหยุ่นมากกว่าครับ Index เป็นที่เก็บ Document ที่มีความคล้ายคลึงกันทางตรรกะ ตัวอย่างเช่น คุณอาจจะมี Index สำหรับบทความ, Index สำหรับสินค้า, และ Index สำหรับผู้ใช้งานครับ
-
Document:
คือหน่วยข้อมูลพื้นฐานที่สุดใน Elasticsearch เปรียบเสมือนแถว (Row) ในตารางของ RDBMS หรือ Object ใน NoSQL Database ครับ Document เป็น JSON Object ที่มีโครงสร้างแบบ Semi-structured และสามารถมีฟิลด์ (Field) ต่างๆ ที่เก็บข้อมูลได้ทุกประเภท เช่น ข้อความ, ตัวเลข, วันที่, Boolean, หรือแม้กระทั่ง Object ซ้อน Object ครับ Document แต่ละตัวจะมี Unique ID อยู่ใน Index นั้นๆ ครับ
-
Type (ในเวอร์ชันเก่า):
ใน Elasticsearch เวอร์ชันเก่า (ก่อน 7.0) เคยมีแนวคิดของ “Type” ซึ่งเปรียบเสมือนตาราง (Table) ภายใน Index ครับ แต่ในเวอร์ชันใหม่ Type ได้ถูกยกเลิกไปแล้ว เพื่อให้สอดคล้องกับแนวคิดของ Lucene Index ที่มีเพียง “Index” และ “Document” ครับ หากคุณมาจาก RDBMS ให้มองว่า Index ของ Elasticsearch คือ “ตาราง” และ Document คือ “แถว” ก็ได้ครับ แต่จำไว้ว่ามันมีความยืดหยุ่นมากกว่านั้นมาก
Inverted Index: หัวใจของการค้นหาที่รวดเร็ว
Inverted Index คือหัวใจสำคัญที่ทำให้ Full-text Search ของ Elasticsearch ทำงานได้อย่างรวดเร็วครับ มันคือโครงสร้างข้อมูลที่ทำหน้าที่ตรงข้ามกับ Index ทั่วไป
โดยปกติแล้ว Index ทั่วไปจะเก็บข้อมูลแบบ “เอกสารชี้ไปหาคำ” (Document -> Words) เช่น เอกสาร A มีคำว่า “Apple”, “Banana” ในขณะที่ Inverted Index จะเก็บข้อมูลแบบ “คำชี้ไปหาเอกสาร” (Word -> Documents) ครับ
ตัวอย่าง:
สมมติว่าเรามีเอกสาร 3 ชิ้น:
- Doc 1: “Elasticsearch is a powerful search engine.”
- Doc 2: “Learn about Elasticsearch and Kibana.”
- Doc 3: “Search engines are very useful.”
เมื่อ Elasticsearch ทำการ Index ข้อมูลเหล่านี้ มันจะสร้าง Inverted Index ดังนี้ (หลังจากผ่านกระบวนการ Text Analysis เช่น การแปลงเป็นตัวพิมพ์เล็ก และการแยกคำแล้ว):
{
"a": [Doc 1, Doc 2, Doc 3],
"about": [Doc 2],
"and": [Doc 2],
"are": [Doc 3],
"engine": [Doc 1],
"engines": [Doc 3],
"elasticsearch": [Doc 1, Doc 2],
"is": [Doc 1],
"kibana": [Doc 2],
"learn": [Doc 2],
"powerful": [Doc 1],
"search": [Doc 1, Doc 3],
"useful": [Doc 3],
"very": [Doc 3]
}
จากตารางนี้ หากคุณค้นหาคำว่า “Elasticsearch” ระบบจะสามารถชี้ไปหา Doc 1 และ Doc 2 ได้ทันที ทำให้การค้นหาเกิดขึ้นได้อย่างรวดเร็วมากครับ Inverted Index ไม่เพียงแค่เก็บว่าคำนั้นอยู่ในเอกสารใดบ้าง แต่ยังสามารถเก็บข้อมูลเพิ่มเติม เช่น ตำแหน่งของคำในเอกสาร (Position), ความถี่ของคำในเอกสาร (Term Frequency) เพื่อใช้ในการคำนวณคะแนนความเกี่ยวข้อง (Relevance Score) อีกด้วยครับ
Analyzer: ตัวแปรสำคัญสำหรับภาษาไทย
Analyzer คือส่วนประกอบสำคัญใน Elasticsearch ที่ทำหน้าที่ประมวลผลข้อความ (Text Analysis) ครับ กระบวนการนี้จะเกิดขึ้นเมื่อคุณทำการ Index ข้อมูล (เพื่อเตรียมข้อมูลสำหรับการค้นหา) และเมื่อคุณส่งคำค้นหาเข้ามา (เพื่อปรับคำค้นหาให้ตรงกับรูปแบบที่ถูก Index ไว้) ซึ่งมีความสำคัญอย่างยิ่งสำหรับภาษาไทยครับ
Analyzer ประกอบด้วย 3 ส่วนหลักๆ คือ:
-
Character Filters:
ทำหน้าที่ปรับเปลี่ยนข้อความก่อนที่จะส่งให้ Tokenizer เช่น การลบ HTML Tags, การแทนที่อักขระพิเศษ หรือการแปลงจากอักขระเต็มความกว้าง (Full-width) เป็นอักขระครึ่งความกว้าง (Half-width) ครับ
-
Tokenizer:
เป็นส่วนที่สำคัญที่สุดสำหรับภาษาไทย Tokenizer ทำหน้าที่ “แบ่ง” หรือ “แยก” ข้อความออกเป็นหน่วยย่อยๆ ที่เรียกว่า “Token” ซึ่งโดยทั่วไปคือคำแต่ละคำครับ สำหรับภาษาอังกฤษจะใช้ Whitespace Tokenizer ที่แยกคำตามช่องว่าง แต่ภาษาไทยไม่มีช่องว่างระหว่างคำ ทำให้ต้องใช้ Thai Tokenizer หรือ ICU Tokenizer ที่ใช้ Dictionary หรือกฎเกณฑ์ในการแยกคำครับ
-
Token Filters:
ทำหน้าที่ปรับแต่ง Token ที่ได้จาก Tokenizer ครับ เช่น
- Lowercase Token Filter: แปลง Token ทั้งหมดให้เป็นตัวพิมพ์เล็ก
- Stop Word Token Filter: ลบคำที่ไม่สำคัญออกไป เช่น “the”, “a”, “an” หรือ “คือ”, “เป็น”, “ได้” สำหรับภาษาไทย
- Synonym Token Filter: เพิ่มคำพ้องความหมายเข้าไปใน Index เพื่อให้ค้นหาได้กว้างขึ้น
- Stemming Token Filter: ลดรูปคำให้เหลือแต่รากศัพท์ เช่น “running” เหลือ “run” (สำหรับภาษาอังกฤษ)
การเลือกและปรับแต่ง Analyzer ที่เหมาะสม โดยเฉพาะสำหรับภาษาไทย จะส่งผลอย่างมากต่อความแม่นยำและประสิทธิภาพของระบบค้นหาครับ เราจะลงรายละเอียดเรื่องนี้เพิ่มเติมในส่วนของการปรับแต่ง Analyzer สำหรับภาษาไทยครับ
การติดตั้งและเริ่มต้นใช้งาน Elasticsearch
ในส่วนนี้ เราจะมาดูวิธีการติดตั้ง Elasticsearch และ Kibana เบื้องต้น รวมถึงการสร้าง Index และเพิ่มข้อมูลเข้าไปครับ
ติดตั้ง Elasticsearch
มีหลายวิธีในการติดตั้ง Elasticsearch ไม่ว่าจะเป็นการดาวน์โหลดไฟล์ Binary, ใช้ Docker, หรือติดตั้งผ่าน Package Manager (APT/YUM) สำหรับ Linux ครับ ในที่นี้จะแนะนำการติดตั้งแบบง่ายๆ สำหรับการทดลองใช้งานบนเครื่อง Local ครับ
ตัวอย่างการติดตั้งผ่าน Docker (แนะนำสำหรับ Local Development):
ก่อนอื่นต้องมี Docker และ Docker Compose ติดตั้งอยู่ในระบบของคุณก่อนนะครับ
# สร้างไฟล์ docker-compose.yml
mkdir elastic-search-demo
cd elastic-search-demo
touch docker-compose.yml
เพิ่มเนื้อหาต่อไปนี้ลงในไฟล์ docker-compose.yml:
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.1 # เลือกเวอร์ชันที่ต้องการ
container_name: elasticsearch
environment:
- xpack.security.enabled=false # ปิดความปลอดภัยสำหรับ Dev Env
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m # กำหนด Memory สำหรับ Elasticsearch
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- esdata:/usr/share/elasticsearch/data
ports:
- "9200:9200"
- "9300:9300"
networks:
- elastic_net
kibana:
image: docker.elastic.co/kibana/kibana:8.11.1 # เลือกเวอร์ชันเดียวกับ Elasticsearch
container_name: kibana
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
networks:
- elastic_net
depends_on:
- elasticsearch
volumes:
esdata:
driver: local
networks:
elastic_net:
driver: bridge
จากนั้นรันคำสั่งใน Terminal ในโฟลเดอร์เดียวกับ docker-compose.yml:
docker-compose up -d
รอสักครู่ Elasticsearch และ Kibana ก็จะเริ่มทำงานครับ คุณสามารถตรวจสอบสถานะได้โดยเข้าถึง http://localhost:9200 ในเบราว์เซอร์ ควรจะเห็น JSON Response ที่แสดงข้อมูลของ Cluster ครับ
ติดตั้ง Kibana (เครื่องมือจัดการและวิเคราะห์ข้อมูล)
Kibana ถูกติดตั้งและรันพร้อมกับ Elasticsearch ใน Docker Compose ด้านบนแล้วครับ คุณสามารถเข้าถึง Kibana ได้ที่ http://localhost:5601 ครับ Kibana จะมี Dev Tools (Console) ซึ่งเป็นเครื่องมือที่ดีเยี่ยมสำหรับการส่ง API Request ไปยัง Elasticsearch ครับ
การสร้าง Index และกำหนด Mapping
การสร้าง Index คือการเตรียมพื้นที่สำหรับเก็บข้อมูลครับ การกำหนด Mapping เป็นสิ่งสำคัญมาก เพราะมันจะบอก Elasticsearch ว่าแต่ละฟิลด์ควรจะถูกจัดเก็บและวิเคราะห์อย่างไรครับ
ใน Kibana Dev Tools (Console) ลองรันคำสั่งต่อไปนี้เพื่อสร้าง Index ชื่อ products พร้อมกำหนด Mapping เบื้องต้นครับ
PUT /products
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"thai_analyzer": {
"tokenizer": "thai",
"filter": [
"lowercase"
]
},
"keyword_analyzer": {
"tokenizer": "keyword",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"properties": {
"product_id": {
"type": "keyword"
},
"product_name": {
"type": "text",
"analyzer": "thai_analyzer",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"description": {
"type": "text",
"analyzer": "thai_analyzer"
},
"price": {
"type": "float"
},
"category": {
"type": "keyword"
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
คำอธิบาย:
number_of_shards: กำหนดจำนวน Primary Shard (1 สำหรับ Cluster เดี่ยวหรือทดสอบ)number_of_replicas: กำหนดจำนวน Replica Shard (0 สำหรับ Cluster เดี่ยวหรือทดสอบ)analysis.analyzer: ส่วนนี้เรากำหนด Custom Analyzer ชื่อthai_analyzerโดยใช้thaitokenizer (ซึ่งเป็น built-in สำหรับภาษาไทย) และlowercasefilter ครับ และkeyword_analyzerสำหรับฟิลด์ที่ต้องการเก็บเป็นคำเดียวmappings.properties: กำหนดโครงสร้างของ Document ครับproduct_id:keywordtype เหมาะสำหรับ ID หรือค่าที่ไม่ควรถูกแบ่งคำ เช่น SKUproduct_name:texttype สำหรับข้อความที่ต้องการค้นหาแบบ Full-text และใช้thai_analyzerที่เราสร้างขึ้นมา นอกจากนี้ยังมี Sub-fieldproduct_name.keywordที่เก็บชื่อสินค้าแบบเต็ม ไม่ถูกวิเคราะห์ เพื่อใช้สำหรับการทำ Sorting หรือ Aggregationdescription:texttype สำหรับรายละเอียดสินค้า ใช้thai_analyzerเช่นกันprice:floattype สำหรับตัวเลขทศนิยมcategory:keywordtype สำหรับหมวดหมู่สินค้าcreated_at:datetype พร้อมกำหนดรูปแบบ
การสร้าง Mapping ที่ดีมีความสำคัญมากครับ เพราะหากคุณต้องการเปลี่ยน Mapping หลังจากที่มีข้อมูลถูก Index ไปแล้ว คุณจะต้อง Reindex ข้อมูลใหม่ทั้งหมดครับ
การเพิ่มข้อมูล (Document) เข้าสู่ Index
เมื่อมี Index แล้ว เราก็สามารถเพิ่มข้อมูล Document เข้าไปได้ครับ
POST /products/_doc
{
"product_id": "P001",
"product_name": "เสื้อยืดผ้าฝ้ายนุ่มสบาย",
"description": "เสื้อยืดคุณภาพดี ตัดเย็บจากผ้าฝ้ายแท้ 100% สวมใส่สบาย ระบายอากาศได้ดี เหมาะสำหรับทุกโอกาส",
"price": 299.00,
"category": "เครื่องแต่งกาย",
"created_at": "2023-10-26 10:00:00"
}
POST /products/_doc
{
"product_id": "P002",
"product_name": "กางเกงยีนส์เดนิมทรงสกินนี่",
"description": "กางเกงยีนส์แฟชั่น ทรงสกินนี่เข้ารูป เนื้อผ้าเดนิมคุณภาพสูง ดีไซน์ทันสมัย",
"price": 890.50,
"category": "เครื่องแต่งกาย",
"created_at": "2023-10-26 10:05:00"
}
POST /products/_doc
{
"product_id": "P003",
"product_name": "โน๊ตบุ๊คประสิทธิภาพสูงสำหรับทำงาน",
"description": "โน๊ตบุ๊ครุ่นใหม่ล่าสุด หน่วยประมวลผลเร็ว แรง เหมาะสำหรับงานกราฟิกและการเขียนโปรแกรม",
"price": 25990.00,
"category": "อิเล็กทรอนิกส์",
"created_at": "2023-10-26 10:10:00"
}
POST /products/_doc
{
"product_id": "P004",
"product_name": "หม้อหุงข้าวไฟฟ้าอัจฉริยะ",
"description": "หม้อหุงข้าวระบบดิจิทัล หุงข้าวได้หลากหลายประเภท พร้อมฟังก์ชันอุ่นอัตโนมัติ ใช้งานง่าย",
"price": 1500.00,
"category": "เครื่องใช้ไฟฟ้า",
"created_at": "2023-10-26 10:15:00"
}
POST /products/_doc
{
"product_id": "P005",
"product_name": "เสื้อเชิ้ตลายสก็อตคลาสสิก",
"description": "เสื้อเชิ้ตแขนยาว ลายสก็อต ดีไซน์คลาสสิก เนื้อผ้าใส่สบาย เหมาะสำหรับใส่ทำงานหรือลำลอง",
"price": 450.00,
"category": "เครื่องแต่งกาย",
"created_at": "2023-10-26 10:20:00"
}
คุณสามารถใช้ _doc endpoint เพื่อให้ Elasticsearch สร้าง ID ให้เอง หรือระบุ ID เองก็ได้ เช่น PUT /products/_doc/P001 ครับ
การค้นหาข้อมูลเบื้องต้น: Match และ Term Query
หลังจากที่เรามีข้อมูลแล้ว ก็ถึงเวลาลองค้นหาข้อมูลกันครับ Elasticsearch มี Query DSL (Domain Specific Language) ที่เป็น JSON สำหรับการสร้าง Query ที่ซับซ้อนครับ
Match Query:
match query เป็น Query พื้นฐานสำหรับการค้นหา Full-text Search ครับ มันจะทำการวิเคราะห์คำค้นหา (ผ่าน Analyzer) และนำไปค้นหาในฟิลด์ที่กำหนด
GET /products/_search
{
"query": {
"match": {
"description": "เสื้อผ้าฝ้าย"
}
}
}
ผลลัพธ์ที่ได้ควรจะแสดง Document ที่มีคำว่า “เสื้อ” และ “ผ้าฝ้าย” อยู่ในฟิลด์ description ครับ (หรือคำที่ถูก Tokenize แล้ว)
GET /products/_search
{
"query": {
"match": {
"product_name": "กางเกงยีนส์"
}
}
}
จะค้นหา Document ที่มีคำว่า “กางเกง” หรือ “ยีนส์” ในฟิลด์ product_name ครับ
Term Query:
term query ใช้สำหรับการค้นหาคำที่ “ตรงกันเป๊ะ” (Exact Match) โดยไม่มีการวิเคราะห์ข้อความ (No Analysis) ครับ มักใช้กับฟิลด์ที่มี keyword type หรือฟิลด์ที่ถูกวิเคราะห์แล้วแต่ต้องการค้นหา Token นั้นๆ โดยตรง
GET /products/_search
{
"query": {
"term": {
"category": "เครื่องแต่งกาย"
}
}
}
Query นี้จะค้นหาเฉพาะ Document ที่มีค่าในฟิลด์ category เป็น “เครื่องแต่งกาย” ตรงๆ ครับ หาก category เป็น text type และถูกวิเคราะห์เป็น “เครื่อง” และ “แต่งกาย” การใช้ term query อาจจะไม่พบผลลัพธ์ที่คาดหวังครับ ดังนั้น term query จึงเหมาะกับฟิลด์ keyword มากกว่า
ยกระดับการค้นหาด้วยฟีเจอร์ขั้นสูง
Elasticsearch มีฟีเจอร์การค้นหาที่ทรงพลังมากมายที่จะช่วยให้ระบบของคุณฉลาดขึ้นครับ
Match_phrase Query: ค้นหาวลีที่ตรงกัน
บางครั้งเราต้องการค้นหาวลีที่เรียงติดกันเป๊ะๆ เช่น “เครื่องใช้ไฟฟ้า” ไม่ใช่แค่มีคำว่า “เครื่อง” และ “ไฟฟ้า” อยู่ในเอกสารแต่แยกกันอยู่ match_phrase query จะเข้ามาช่วยตรงนี้ครับ
GET /products/_search
{
"query": {
"match_phrase": {
"description": "ผ้าฝ้ายแท้ 100%"
}
}
}
Query นี้จะค้นหาเฉพาะ Document ที่มีวลี “ผ้าฝ้ายแท้ 100%” อยู่เรียงติดกันในฟิลด์ description ครับ
Multi_match Query: ค้นหาในหลายฟิลด์พร้อมกัน
ผู้ใช้งานมักจะพิมพ์คำค้นหาเพียงครั้งเดียว แต่ต้องการให้ระบบค้นหาในหลายๆ ฟิลด์ที่เกี่ยวข้อง multi_match query ช่วยให้คุณทำสิ่งนี้ได้ง่ายขึ้นครับ
GET /products/_search
{
"query": {
"multi_match": {
"query": "โน๊ตบุ๊คแรง",
"fields": ["product_name", "description"]
}
}
}
Query นี้จะค้นหาคำว่า “โน๊ตบุ๊ค” หรือ “แรง” ในทั้งฟิลด์ product_name และ description ครับ และจะนำคะแนนความเกี่ยวข้องจากทั้งสองฟิลด์มารวมกัน
Boolean Query and Filter: การค้นหาแบบผสมผสาน
bool query เป็น Query ที่ทรงพลังที่สุดตัวหนึ่งของ Elasticsearch เพราะช่วยให้คุณสามารถรวม Query หลายๆ ตัวเข้าด้วยกันโดยใช้ตรรกะแบบ Boolean (AND, OR, NOT) ครับ
must: Document ต้องมี Query นี้ทั้งหมด (AND)should: Document อาจมี Query นี้ (OR) แต่ละ Query ที่ตรงกันจะเพิ่มคะแนนความเกี่ยวข้องmust_not: Document ต้องไม่มี Query นี้ (NOT)filter: คล้ายกับmustแต่ Query ในส่วนfilterจะไม่นำมาคำนวณคะแนนความเกี่ยวข้อง ทำให้มีประสิทธิภาพสูงกว่าเมื่อต้องการกรองข้อมูลโดยไม่สนใจลำดับครับ
ตัวอย่าง: ค้นหาสินค้าประเภท “เครื่องแต่งกาย” ที่มีคำว่า “เสื้อ” และ “สวมใส่สบาย” แต่ไม่รวมสินค้าที่มีราคาเกิน 500 บาท
GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "product_name": "เสื้อ" } },
{ "match": { "description": "สวมใส่สบาย" } }
],
"filter": [
{ "term": { "category": "เครื่องแต่งกาย" } },
{ "range": { "price": { "lte": 500 } } }
],
"must_not": [
{ "match": { "product_name": "กางเกง" } }
]
}
}
}
ใน Query นี้ must และ should จะถูกนำไปใช้ในการคำนวณคะแนนความเกี่ยวข้อง (Relevance Scoring) ส่วน filter และ must_not จะใช้ในการกรองผลลัพธ์เท่านั้น โดยไม่ส่งผลต่อคะแนนความเกี่ยวข้อง ทำให้มีประสิทธิภาพสูงขึ้นเมื่อใช้ในการกรองข้อมูลที่ซับซ้อนครับ
Fuzzy Search: ค้นหาแม้สะกดผิด
ผู้ใช้งานมักจะสะกดคำผิดพลาด fuzzy query ช่วยให้คุณค้นหาคำที่ใกล้เคียงกับคำค้นหาได้ แม้จะสะกดผิดไปบ้างครับ โดยใช้ Levenshtein Distance ในการคำนวณความใกล้เคียง
GET /products/_search
{
"query": {
"match": {
"product_name": {
"query": "โน๊ตบุค",
"fuzziness": "AUTO"
}
}
}
}
เมื่อค้นหา “โน๊ตบุค” (สะกดผิด) ด้วย fuzziness: "AUTO" อาจจะเจอ “โน๊ตบุ๊ค” ครับ ค่า AUTO จะคำนวณความผิดพลาดที่ยอมรับได้ตามความยาวของคำครับ
Highlighting: เน้นคำที่ค้นหาเจอ
การเน้นคำที่ค้นหาเจอในผลลัพธ์จะช่วยให้ผู้ใช้เห็นได้อย่างรวดเร็วว่าข้อมูลส่วนใดที่เกี่ยวข้องกับคำค้นหาของพวกเขาครับ
GET /products/_search
{
"query": {
"match": {
"description": "ผ้าฝ้าย"
}
},
"highlight": {
"fields": {
"description": {}
},
"pre_tags": [""],
"post_tags": [""]
}
}
ผลลัพธ์ที่ได้จะมีฟิลด์ highlight เพิ่มเข้ามา เช่น "description": ["เสื้อยืดคุณภาพดี ตัดเย็บจากผ้าฝ้ายแท้ 100% สวมใส่สบาย..."] ครับ
Suggesters: ระบบแนะนำคำค้นหาอัจฉริยะ
Suggesters คือฟีเจอร์ที่ช่วยเพิ่มประสบการณ์การค้นหาให้ดียิ่งขึ้น ด้วยการแนะนำคำค้นหาที่เกี่ยวข้อง หรือแก้ไขคำผิดพลาดของผู้ใช้งาน มีหลายประเภทดังนี้ครับ
Completion Suggester: แนะนำคำแบบ Autocomplete
ใช้สำหรับสร้างระบบ Autocomplete ที่รวดเร็วมากๆ ครับ เหมาะกับการแนะนำคำขณะที่ผู้ใช้กำลังพิมพ์ เช่น Google Search หรือช่องค้นหาของเว็บไซต์ E-commerce
ในการใช้งาน Completion Suggester คุณต้องมีฟิลด์ที่มี completion type ใน Mapping ของคุณก่อนครับ
อัปเดต Mapping (ต้องสร้าง Index ใหม่ หรือ Reindex หากมีการเปลี่ยนแปลง):
PUT /products_v2
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"thai_analyzer": {
"tokenizer": "thai",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"properties": {
"product_id": { "type": "keyword" },
"product_name": {
"type": "text",
"analyzer": "thai_analyzer",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
},
"description": { "type": "text", "analyzer": "thai_analyzer" },
"price": { "type": "float" },
"category": { "type": "keyword" },
"created_at": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" },
"suggest_product_name": {
"type": "completion",
"analyzer": "thai_analyzer",
"search_analyzer": "thai_analyzer"
}
}
}
}
จากนั้น Index ข้อมูลโดยเพิ่มฟิลด์ suggest_product_name (สามารถใช้ product_name เป็นค่าได้เลย)
POST /products_v2/_doc
{
"product_id": "P001",
"product_name": "เสื้อยืดผ้าฝ้ายนุ่มสบาย",
"description": "เสื้อยืดคุณภาพดี ตัดเย็บจากผ้าฝ้ายแท้ 100% สวมใส่สบาย ระบายอากาศได้ดี เหมาะสำหรับทุกโอกาส",
"price": 299.00,
"category": "เครื่องแต่งกาย",
"created_at": "2023-10-26 10:00:00",
"suggest_product_name": {
"input": ["เสื้อยืดผ้าฝ้ายนุ่มสบาย", "เสื้อยืด", "ผ้าฝ้าย"]
}
}
Query สำหรับ Completion Suggester:
GET /products_v2/_search
{
"suggest": {
"product_name_suggestions": {
"prefix": "เสื้อ",
"completion": {
"field": "suggest_product_name",
"size": 5
}
}
}
}
ผลลัพธ์จะแสดงคำแนะนำที่ขึ้นต้นด้วย “เสื้อ” ครับ
Term Suggester: แก้ไขคำผิดพลาด
ใช้สำหรับแนะนำคำที่ถูกต้องเมื่อผู้ใช้พิมพ์คำผิดพลาดครับ
GET /products/_search
{
"suggest": {
"my_term_suggester": {
"text": "เสื้อยืดผ้าฝาย",
"term": {
"field": "product_name",
"suggest_mode": "popular"
}
}
}
}
หากมี “เสื้อยืดผ้าฝ้าย” อยู่ใน Index และ “เสื้อยืดผ้าฝาย” ถูกพิมพ์ผิด Term Suggester จะแนะนำคำที่ถูกต้องให้ครับ
Phrase Suggester: แนะนำวลีที่ถูกต้อง
คล้ายกับ Term Suggester แต่จะทำงานกับวลีทั้งประโยคครับ โดยจะพยายามแก้ไขไวยากรณ์หรือคำผิดที่เกิดขึ้นในวลีนั้นๆ เพื่อแนะนำวลีที่น่าจะเป็นไปได้มากที่สุด
GET /products/_search
{
"suggest": {
"my_phrase_suggester": {
"text": "โน๊ตบุ๊คสำรับทำงาน",
"phrase": {
"field": "description",
"collate": {
"query": {
"source": {
"match": {
"{{field_name}}": "{{suggestion}}"
}
}
},
"params": {
"field_name": "description"
}
},
"highlight": {
"pre_tag": "",
"post_tag": ""
}
}
}
}
}
หาก “โน๊ตบุ๊คสำรับทำงาน” ถูกพิมพ์ผิด Phrase Suggester อาจจะแนะนำ “โน๊ตบุ๊คสำหรับทำงาน” ให้ครับ
การปรับแต่ง Analyzer สำหรับภาษาไทยโดยเฉพาะ
ภาษาไทยมีความท้าทายในการทำ Full-text Search เพราะไม่มีการเว้นวรรคระหว่างคำ ทำให้การแยกคำ (Tokenization) เป็นสิ่งสำคัญอย่างยิ่งครับ Elasticsearch มี thai tokenizer มาให้ในตัว ซึ่งทำงานได้ดีในระดับหนึ่ง แต่บางครั้งเราอาจต้องสร้าง Custom Analyzer เพื่อให้ได้ผลลัพธ์ที่ดีที่สุด
Tokenizer: การแบ่งคำ
-
Standard Tokenizer:
สำหรับภาษาอังกฤษ จะแบ่งคำตามช่องว่างและเครื่องหมายวรรคตอน แต่สำหรับภาษาไทย มันจะมองประโยคทั้งประโยคเป็นคำเดียว ซึ่งไม่ถูกต้องครับ
-
Thai Tokenizer:
เป็น Tokenizer ที่ออกแบบมาสำหรับภาษาไทยโดยเฉพาะ ใช้กฎเกณฑ์และ Dictionary ในการแยกคำภาษาไทย มักจะให้ผลลัพธ์ที่ดีที่สุดสำหรับภาษาไทยครับ
-
ICU Tokenizer (ผ่าน ICU Analysis Plugin):
เป็น Tokenizer ที่รองรับภาษาได้หลากหลาย รวมถึงภาษาไทย ทำงานได้ดีและบางครั้งอาจให้ผลลัพธ์ที่แตกต่างหรือดีกว่า Thai Tokenizer ขึ้นอยู่กับบริบทและข้อมูลของคุณครับ
Token Filters: การปรับแต่งโทเค็น
หลังจาก Tokenizer แยกคำออกมาแล้ว Token Filters จะทำการปรับแต่ง Token เหล่านั้น:
-
Lowercase Token Filter:
แปลงตัวอักษรทั้งหมดให้เป็นตัวพิมพ์เล็ก มีประโยชน์สำหรับภาษาอังกฤษ แต่สำหรับภาษาไทยก็ช่วยให้การค้นหาไม่ sensitive ต่อการใช้ตัวพิมพ์ใหญ่-เล็ก (ซึ่งภาษาไทยไม่นิยมใช้) ครับ
-
Stop Word Token Filter:
ลบคำที่ไม่สำคัญออกไป เช่น “เป็น”, “คือ”, “อยู่”, “ได้”, “ให้” เพื่อลดขนาดของ Inverted Index และเพิ่มความเกี่ยวข้องของผลลัพธ์ครับ (ต้องสร้าง List ของ Stop Words สำหรับภาษาไทยเอง)
-
Synonym Token Filter:
ช่วยให้คุณสามารถกำหนดคำพ้องความหมายได้ เช่น “รถยนต์” กับ “รถเก๋ง” หรือ “มือถือ” กับ “โทรศัพท์” เพื่อให้เมื่อค้นหาคำใดคำหนึ่ง ระบบก็จะเจอเอกสารที่มีคำพ้องความหมายด้วยครับ
-
Thai Stemmer (ผ่าน Phonetic Analysis Plugin หรือ Custom):
สำหรับภาษาไทย การทำ Stemming (ลดรูปคำเหลือรากศัพท์) นั้นซับซ้อนกว่าภาษาอังกฤษมาก และยังไม่มี Stemmer ที่สมบูรณ์แบบสำหรับภาษาไทยใน Elasticsearch โดยตรง อาจต้องใช้ Plugin หรือพัฒนา Custom Token Filter เองครับ
Character Filters: การปรับแต่งตัวอักษร
ใช้ในการปรับแต่งข้อความก่อนที่จะถูกส่งไปยัง Tokenizer เช่น
-
HTML Strip Char Filter:
ลบ HTML tags ออกจากข้อความ
-
Mapping Char Filter:
ใช้สำหรับแทนที่ตัวอักษรหรือวลีหนึ่งด้วยอีกตัวหนึ่ง เช่น การแปลง “ฯพณฯ” เป็น “ท่าน” หรือการแทนที่คำผิดที่พบบ่อยครับ
การใช้งาน Thai Analyzer ใน Elasticsearch
Elasticsearch มี Thai Analyzer มาให้ในตัวครับ ซึ่งประกอบด้วย thai tokenizer และ lowercase filter โดยอัตโนมัติ
GET /_analyze
{
"analyzer": "thai",
"text": "ฉันกำลังเขียนบทความเกี่ยวกับ ElasticSearch ภาษาไทย"
}
ผลลัพธ์: ["ฉัน", "กำลัง", "เขียน", "บทความ", "เกี่ยวกับ", "elasticsearch", "ภาษาไทย"]
จะเห็นว่ามันสามารถแยกคำภาษาไทยได้ค่อนข้างดีครับ และแปลงเป็นตัวพิมพ์เล็ก (สำหรับ Elasticsearch) ครับ
ตัวอย่างการสร้าง Custom Analyzer สำหรับภาษาไทย
เราสามารถสร้าง Custom Analyzer ที่ซับซ้อนขึ้น เพื่อให้เหมาะสมกับความต้องการของเราได้ครับ เช่น การเพิ่ม Stop Words หรือ Synonyms
ตัวอย่าง: Custom Thai Analyzer พร้อม Stop Words และ Synonyms
PUT /products_custom_analyzer
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"filter": {
"thai_stopwords": {
"type": "stop",
"stopwords": ["เป็น", "คือ", "ได้", "ให้", "ของ", "ใน", "กับ"]
},
"thai_synonym": {
"type": "synonym",
"synonyms": [
"โทรศัพท์, มือถือ, สมาร์ทโฟน",
"รถยนต์, รถเก๋ง"
]
}
},
"analyzer": {
"my_thai_analyzer": {
"tokenizer": "thai",
"filter": [
"lowercase",
"thai_stopwords",
"thai_synonym"
]
}
}
}
},
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "my_thai_analyzer"
},
"description": {
"type": "text",
"analyzer": "my_thai_analyzer"
}
}
}
}
ทดสอบ Custom Analyzer:
GET /products_custom_analyzer/_analyze
{
"analyzer": "my_thai_analyzer",
"text": "โทรศัพท์มือถือเครื่องใหม่นี้เป็นของฉัน"
}
ผลลัพธ์ควรจะแสดง: ["โทรศัพท์", "มือถือ", "เครื่อง", "ใหม่", "ฉัน"] โดยคำว่า “เป็น”, “ของ” ถูกลบออกไป และ “โทรศัพท์มือถือ” ถูกรวมเป็นคำเดียวหรือแสดงคำพ้องความหมาย (ขึ้นอยู่กับการทำงานของ Synonym Filter) ครับ
การปรับแต่ง Analyzer อย่างละเอียดจะช่วยให้ระบบค้นหาของคุณเข้าใจภาษาไทยได้ดียิ่งขึ้น และมอบผลลัพธ์ที่แม่นยำและเกี่ยวข้องกับผู้ใช้งานมากที่สุดครับ
หากคุณต้องการศึกษาเพิ่มเติมเกี่ยวกับ Analyser สำหรับภาษาไทย ลองดูข้อมูลจากผู้เชี่ยวชาญในชุมชน หรือ อ่านเพิ่มเติม เกี่ยวกับการสร้าง Custom Analyzer ที่ซับซ้อนสำหรับกรณีใช้งานเฉพาะทางครับ
Relevance Scoring และการเพิ่มประสิทธิภาพการจัดอันดับ
หัวใจสำคัญของระบบค้นหาอัจฉริยะคือความสามารถในการจัดอันดับผลลัพธ์ (Relevance Scoring) ครับ Elasticsearch ใช้โมเดลคะแนนที่ซับซ้อนเพื่อตัดสินว่าเอกสารใดมีความเกี่ยวข้องกับคำค้นหามากที่สุด
TF-IDF และ BM25: หลักการจัดอันดับความเกี่ยวข้อง
-
TF-IDF (Term Frequency-Inverse Document Frequency):
เป็นอัลกอริทึมพื้นฐานที่ใช้ในการคำนวณความเกี่ยวข้องครับ
- Term Frequency (TF): ความถี่ของคำค้นหาในเอกสาร ยิ่งคำนั้นปรากฏในเอกสารบ่อยเท่าไหร่ เอกสารนั้นก็ยิ่งมีคะแนนสูงขึ้น
- Inverse Document Frequency (IDF): ความหายากของคำในชุดเอกสารทั้งหมด ยิ่งคำนั้นหายากเท่าไหร่ (ปรากฏในเอกสารน้อยชิ้น) คำนั้นก็ยิ่งมีความสำคัญสูงขึ้นเมื่อปรากฏในเอกสารที่กำลังพิจารณา
TF-IDF จะให้คะแนนสูงกับเอกสารที่มีคำค้นหาปรากฏบ่อยครั้ง และคำนั้นๆ ไม่ได้เป็นคำทั่วไปที่พบได้ในทุกเอกสารครับ
-
BM25 (Best Match 25):
คืออัลกอริทึมที่ Elasticsearch (และ Lucene) ใช้เป็นค่าเริ่มต้นในการคำนวณคะแนนความเกี่ยวข้องครับ BM25 เป็นการพัฒนาต่อยอดจาก TF-IDF โดยมีการปรับปรุงเพื่อจัดการกับความยาวของเอกสาร (Document Length) และความถี่ของคำ (Term Frequency) ได้ดีขึ้น ทำให้ผลลัพธ์มีความแม่นยำและเป็นธรรมชาติมากขึ้นครับ
การปรับแต่ง Boost และ Weight ของฟิลด์
Elasticsearch อนุญาตให้คุณปรับแต่งวิธีการคำนวณคะแนนความเกี่ยวข้องได้ด้วยการใช้ boost ซึ่งเป็นการเพิ่ม “น้ำหนัก” ให้กับฟิลด์หรือ Query บางตัวครับ
ตัวอย่าง: เพิ่มน้ำหนักให้ product_name มากกว่า description
GET /products/_search
{
"query": {
"multi_match": {
"query": "เสื้อยืด",
"fields": [
"product_name^3",
"description"
]
}
}
}
ในตัวอย่างนี้ product_name^3 หมายความว่าคำค้นหาที่เจอในฟิลด์ product_name จะมีคะแนนความเกี่ยวข้องเป็น 3 เท่าของคำที่เจอในฟิลด์ description ครับ ทำให้ผลลัพธ์ที่ชื่อสินค้าตรงกับคำค้นหาจะถูกจัดอันดับให้สูงกว่าครับ
คุณยังสามารถใช้ function_score query เพื่อควบคุมการคำนวณคะแนนได้อย่างละเอียดมากขึ้น โดยสามารถใช้ฟังก์ชันทางคณิตศาสตร์, ฟังก์ชันตามค่าของฟิลด์, หรือแม้กระทั่งสคริปต์ Pains ครับ ซึ่งเป็นหัวข้อที่ซับซ้อนขึ้นไปอีกขั้น แต่ให้ความยืดหยุ่นสูงสุดในการปรับแต่ง Relevance Scoring ครับ
Aggregations และการสร้าง Faceted Search
Aggregations คือฟีเจอร์ที่ทรงพลังของ Elasticsearch ที่ช่วยให้คุณสามารถประมวลผลข้อมูลเพื่อสร้างรายงาน, แดชบอร์ด, หรือแม้กระทั่งระบบ Faceted Search ได้ครับ Faceted Search คือการที่ผู้ใช้สามารถกรองผลลัพธ์การค้นหาตามหมวดหมู่, ช่วงราคา, แบรนด์, หรือคุณสมบัติอื่นๆ ได้อย่างง่ายดาย (เช่น ฟิลเตอร์ด้านข้างในเว็บไซต์ E-commerce)
ตัวอย่าง: นับจำนวนสินค้าในแต่ละหมวดหมู่ (Terms Aggregation)
GET /products/_search
{
"size": 0,
"aggs": {
"products_by_category": {
"terms": {
"field": "category.keyword",
"size": 10
}
}
}
}
ผลลัพธ์จะแสดงจำนวนสินค้าในแต่ละหมวดหมู่ เช่น:
"aggregations": {
"products_by_category": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "เครื่องแต่งกาย",
"doc_count": 3
},
{
"key": "อิเล็กทรอนิกส์",
"doc_count": 1
},
{
"key": "เครื่องใช้ไฟฟ้า",
"doc_count": 1
}
]
}
}
ตัวอย่าง: หาช่วงราคาของสินค้า (Range Aggregation)
GET /products/_search
{
"size": 0,
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 500, "key": "ต่ำกว่า 500" },
{ "from": 500, "to": 1000, "key": "500 - 1000" },
{ "from": 1000, "key": "มากกว่า 1000" }
]
}
}
}
}
Aggregations มีประเภทอีกมากมาย เช่น date_histogram สำหรับข้อมูลเวลา, stats สำหรับค่าสถิติ, top_hits สำหรับดึง Document ที่ดีที่สุดในแต่ละกลุ่ม และสามารถซ้อน Aggregation เข้าไปใน Aggregation อื่นๆ ได้ (Nested Aggregations) ซึ่งเป็นพื้นฐานสำคัญในการสร้างระบบ Faceted Search ที่ซับซ้อนครับ
Best Practices สำหรับการสร้างระบบค้นหาด้วย Elasticsearch
เพื่อให้ระบบค้นหาของคุณมีประสิทธิภาพและยั่งยืน ควรพิจารณา Best Practices เหล่านี้ครับ
ออกแบบ Mapping ให้เหมาะสม
-
ใช้
textสำหรับ Full-text Search: ฟิลด์ที่ต้องการค้นหาแบบ Full-text เช่น ชื่อสินค้า, รายละเอียด, เนื้อหาบทความ ควรเป็นtexttype และกำหนด Analyzer ที่เหมาะสม -
ใช้
keywordสำหรับค่าที่ไม่ควรถูกวิเคราะห์: เช่น ID สินค้า, SKU, หมวดหมู่, แบรนด์, แท็ก หรือฟิลด์ที่ต้องการใช้สำหรับการ Sorting หรือ Aggregation ครับ -
ใช้ Multi-fields: สำหรับฟิลด์เดียวที่ต้องการค้นหาหลายรูปแบบ เช่น
product_nameเป็นtextและมี Sub-field