Simple N-Gram Tokenizer in Elasticsearch

ConvoLab
ConvoLab
Published in
2 min readMay 5, 2019

ในการค้นหาข้อมูลบน elasticsearch เราสามารถค้นหาข้อมูลได้โดยการ match keyword ที่ต้องการแบบ exactly ได้จาก query พื้นฐาน เช่น

ซึ่งก็จะได้ result การ match field title กับ value ที่ต้องการ ดังนี้

แต่ถ้า การ search นี้เป็นการ search ที่มาจาก User ทั่วไปซึ่งเราไม่สามารถจะควบคุม keyword ที่เข้ามาได้ละ tik tok tik tok

บางคนบอกว่าก็ใช้ regexp ตัดสิก็เป็นแนวทางที่ดี แต่ถ้า User พิมพ์ keyword เข้ามาแบบ “อยากกินปลาดกฟูจังเยย” จะตัดยังไงดีน๊าาา

แท่น แท้น แท๊นนน ……

elasticsearch มี analysis tokenizer มากมายที่จะมาช่วยเรื่องการหา sub-string ไป match กับ keyword ที่เราต้องการ วันนี้จะแนะนำ tokenizer ตัวนึง ชื่อ

N-gram tokenizer

n-gram คืออะไร? มันคือ a frequency analysis of the terms in a text รายละเอียดใน Link นี้เลยจร้า

โอเคร เรารู้จัก n-gram แล้ว 5555555 ต่อไปเรามาหาวิธีใช้มันกับ elasticsearch กัน

เริ่มจากเราต้องสร้าง analyzer ขึ้นมาสำหรับ index นั้น ๆ ก่อน ดังนี้

จาก config ข้างต้น เราสร้าง Analyzer ขึ้นมาตัวนึงชื่อ ngram_analyzer แล้วกำหนดให้ Tokenize ของมันเป็น ngram_tokenizer ซึ่งกำหนดค่าไว้ให้ min และ max เป็น 2 และ 4 ตามลำดับ… แล้ว min max คืออะไร? มันคือ จำนวน character ใน range ระหว่าง min->max ที่จะเอาไปเปรียบเทียบกับ keyword เพื่อหา frequency ของ term นั้นเพื่อเอาไปทำ score อีกที

ถ้างงไม่เป็นไรนะ ผมยังงง เยย

ต่อไป เมื่อเราสร้าง Analyzer ขึ้นมาแล้ว เราจะ set Analyzer ที่เราสร้างไปยัง field ที่เราต้องการ ดังนี้

  • ข้อควรระวัง ใน elasticsearch เราจะ set properties ให้กับ mapping ได้แค่ก่อนที่มันจะมีข้อมูลเท่านั้น หลังจากนั้นมันจะไม่ให้เรา set properties แย้วนะ ต้อง _reindex ไป dummy แล้วลบ index เก่า แล้ว _reindex อีกทีจาก dummy มา index ใหม่ที่ชื่อเหมือนอันเก่า เลยนะครับ (หรือ อาจจะมีวิธีที่ดีกว่านี้ 5555)

จากนั้น เราก็ใส่ข้อมูลเข้าไปเพื่อ ทดสอบด้วย query เหมือนเดิมเยยแต่เพิ่มให้มัน ซับซ้อน นิสนึง

แต่เราก็ยังได้ผลลัพธ์เหมือนเดิมนะ

เรียบร้อยแล้ว ง่ายใช่มั้ยละ จบกันไปที่ post แรกของฉัน บัย

ปล.

  • min max gram สำคัญมาก กำหนดให้เหมาะกับข้อมูลของเรานะ
  • ในความเป็นจริง ข้อมูลอาจจะมี whitespace แล้ว text อาจจะมีความหมายต่อเนื่องกันอย่าลืมเพิ่ม token_chars เป็น whitespace
  • อาจจะอีกหลายกรณีที่ไม่ครอบคลุมนะ 55555555

--

--