Mapping และ Datatype ของ Elasticsearch

Nut Janekitiworapong
Nut .
Published in
3 min readJan 4, 2017

ไม่เสียเวลาพูดพร่ำทำเพลง ถ้าใครยังไม่รู้จัก Elasticsearch หละก็ มันเป็น Near realtime search engine ถ้าเอาแบบละเอียดๆ ก็ลองหาอ่านได้ที่นี่ หรือ ที่นี่ หรือไม่ก็ไปที่ https://www.elastic.co

ก่อนอื่นมารู้จักศัพท์บางคำของ Elasticsearch กันก่อน

รูปภาพ - elastic.co

Index ให้มองเป็น database นึง เช่น สมมุติเราจะทำฐานข้อมูลของร้านขายของ เราก็จะมี Index ที่ชื่อว่าร้านขายของ โดยในฐานข้อมูลนี้ ก็จะมีอีกหลาย Type

Type คือให้มองเหมือนtable ของ Relational Database เช่น Type ที่ชื่อ รายการสินค้า, Type ที่ชื่อรายชื่อลูกค้า เป็นต้น โดยในแต่ละ Type ประกอบด้วยชื่อกับ Mapping (= schema)

Mapping คือการกำหนดว่าแต่ละ documents (และ fields) จะถูกจัดเก็บและทำ Index ยังไง มองเผินๆก็จะไป คล้ายกับ Schemas ของพวก Relational Database นั่นแหละ มันใช้กำหนดได้ว่าแต่ละ field จะเป็นลักษะข้อมูลแบบไหน จะทำ index มั้ย อะไรแบบนี้

โดยในแต่ละ Mapping จะเรียกว่า Type จะประกอบไปด้วย Field ต่างๆ แล้วแต่ข้อมูลของเรา อันนี้ถ้ามองเทียบกับพวก Relational Database ก็จะเหมือนกันเลย ก็คือแต่ละ Table ก็จะมี Field ต่างๆ นอกจากนี้ใน Mapping ยังมี Meta Field อีก ซึ่งจะไว้เก็บค่าคุณสมบัติเฉพาะ เช่น _id, _type, _uid, _index อันนี้เดี่ยวค่อยมาอธิบายกันอีกที

Document คือเทียบได้กับ Row หรือ Tuple ใน Relational Database โดยจะเป็นค่าของคู่ key/value เช่น ชื่อสินค้า/ไก่ทอดบอนชอน โดยจะคล้ายๆกับ object ใน OOP

อะ สรุป เทียบ Relational Database กับ Elasticsearch

Mapping

เราสามารถทำ Mapping ได้สองแบบหลักๆ นั่นก็คือ

Dynamic Mapping

คือ Elasticsearch จะสร้าง Mapping Typeใหม่ กับ Fieldใหม่ ให้เองเลย เราไม่ต้องกำหนดก่อนล่วงหน้า สมมุติเราใส่ข้อมูลลงไป มันจะคิดให้เองเลยว่าอันนี้มีอะไรบ้าง เป็นข้อมูลประเภทไหน (จะเป็น string, numeric ก็ว่าไป) และที่สำคัญ Elasticsearch จะคิดให้เองด้วยว่าข้อมูลนี่ควรทำ Index ที่ไหนอะไรยังไง

Explicit Mapping

อันนี้เรากำหนดเองเลย ว่าจะให้แต่ละ Mapping Type เป็นแบบไหน มีประโยชน์มากเวลาที่เรารู้แล้วข้อมูลของเราจะเป็นรูปแบบไหน (Specific data format) แล้วอีกอย่างคือบางทีข้อมูลซับซ้อนจน Elasticsearch อาจ Mapping ให้เราผิด ดังนั้นเราจึงกำหนดไปเองเลย ชัวร์กว่าแน่

Data Type

เจ้า Elasticsearch รองรับกับข้อมูลหลากหลายชนิดมาก เช่น

String

ก็เอาไว้เก็บสตริง โดยจะมี 2 ประเภทอีกทีนะ ก็คือ Full Text กับ Keywords

  • Full Text มักจะเอาไว้เก็บข้อมูลที่จะเอามาเสิร์ช (เช่น ถ้าเราต้องการค้นหาสินค้าด้วยชื่อ เราก็จะกำหนด Field นี้ให้เป็น Full Text) และข้อมูลที่เป็นประเภทนี้จะถูกเอาไปวิเคราะห์ผ่านตัว Analyzer มันก็จะเอาไปตัดคำ ทำโน้นนี่ของมัน โดยส่วนมากแล้วข้อมูลที่เป็น Full Text จะไม่เอามาทำ sorting หรือเอามาทำ Aggregation
  • Keywords จะเป็นค่าที่เฉพาะแน่นอน (exact value) เช่นพวก tags, status, email, gender ค่าแบบนี้จะไม่ถูกนำไปผ่าน Analyzer (จะถูกมองเป็น 1 term) มักจะเอามา filter เวลา search
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"full_name": {
"type": "string" /* อันนี้เป็น full text */
},
"status": {
"type": "string",
"index": "not_analyzed" /* อันนี้เป็น keyword */
}
}
}
}
}

ขอโน้ตไว้ตรงนี้ว่าถ้าใช้ Elasticsearch เวอร์ชัน 5.x แล้วเนี้ย จะไม่มี string แล้วนะ จะแยกไปเลยว่าจะเป็น text (full text) หรือว่า keyword เลย

Numeric

ไว้เก็บพวกตัวเลข เช่น Integer, Float, Double

Date

ไว้เก็บวันที่ โดยเป็นได้ทั้งข้อความText (String) หรือจะเป็นตัวเลขแบบ Long (ที่นับเป็น Milisecondes ตั้งแต่ epoch) หรือจะเป็นตัวเลขจำนวนเต็มที่นับเป็นวินาทีตั้งแต่ epoch โดยค่าพื้นฐานจะเก็บเป็นตัวเลขแบบ Long

Boolean

เก็บค่า true หรือ false
Tips!: สามารถใช้คำหรือตัวเลขแทนความหมายได้นะ เช่น false, “false”, 0, “off”, “no”, “” (สตริงเปล่าๆ), 0.0 พวกนี้มีความหมายว่า FALSE หมดเลย ส่วนอะไรที่ไม่ใช่ false ก็จะถือว่าเป็น true

Elasticsearch ก็ยังสามารถเก็บ complex data type ได้ด้วย ยกตัวอย่างเช่น Object ที่เป็น JSON document เช่น

{
"product": "KFC",
"price": 20
}

Array เช่น [1,2] หรือจะ [“KFC”, “Bonchon”] แต่ต้องระวังนะ ถ้าเราจะเก็บเป็น Array of objects เช่น

{ "products": [ 
{ "name": "Bonchon", "price": 20 },
{ "name": "KFC" , "price": 99 }] }

เจ้า Elasticsearch จะยุบมันจนกลายเป็น

{ "products.name": ["Bonchon", "KFC"], "products.price": [20, 99] }

ทำให้ความสัมพันธ์ที่เรารู้ว่าบอนชอนราคา 20 บาทนั้นหายไปเลย ทางแก้คือเราต้องเก็บมันเป็น nested data type โดยเจ้า nested data type นี้ จะเก็บแต่ละ object แยกไปแต่ละ document เลย แล้วใช้ nested query เวลาเราจะเรียกมันขึ้นมา

Geo-Point ไว้เก็บคู่ของ latitude กับ longitude,Geo-Shape เป็นแบบรูปทรงต่างๆ มักใช้ในกรณีที่เราระบุเป็น Geo-point ไม่พอแล้ว, IPV4 เก็บเลขไอพี

Meta Field

เป็นค่า Meta data ซึ่งก็คือคำอธิบายของแต่ละ document ซึ่งจะมีหลายประเภทด้วยกัน

Identity Meta Field

  • _index เก็บเพื่อบอกให้รู้ว่า document นี้อยู่ใน Index (database) ไหน
  • _type เพื่อบอกให้รู้ว่า document นี้เป็น Type ไหน (อยู่ใน Table อะไร) ช่วยให้เราค้นหาด้วย Type เร็วขึ้น
  • _id เพื่อระบุ ID ของ document

Document Source Meta Field

  • _source เก็บค่าข้อมูลจริงที่ส่งเข้ามาตอนแรกเลย ก็คือเป็นข้อมูลที่ยังไม่ได้ผ่านการโพรเสสใดๆ โดย Elasticsearch โดยมันจะเก็บไว้ให้เผื่อที่เราอยากจะทำโน้นนี่ ซึ่งแน่นอนว่าช่องนี้จะไม่ถูกเอาไป index และค้นหาไม่ได้ คือเก็บไว้เฉยๆ เผื่อไว้ (เปลืองที่ด้วย ปิดได้นะ)
  • _size ขนาดของช่อง _source หน่วยเป็น Bytes

Indexing Meta Field

  • _all จะเอาค่าของทุก Field ใน document มาต่อกัน แล้วคั่นด้วยช่องว่าง ให้เป็นสตริงอันเดียว ประโยชน์คือเอาไว้เพื่อ search เท่านั้น (จะถูก analyze และ index แต่ไม่ถูกจัดเก็บไว้)
  • _field_name เก็บ ‘ชื่อ’ ของ Field ใน document ที่มีค่า (ไม่ได้เป็น NULL) เช่นสมมุติ type คือ product ช่องนี้ก็จะเก็บคำว่า name, price อะไรต่างๆไว้ เอาไว้ใช้ตอนเราค้นหาว่าแบบมีproduct ไหนมั้ย ที่ไม่มีชื่อ ไม่มีราคา อะไรแบบนี้

Routing Meta Field

  • _parent เอาไว้บอกความสัมพันธ์ parent-child ของแต่ละ document ดูแล้วคล้ายๆเหมือน foreign key ในพวก Relational Database
  • _route เอาไว้กำหนด shard ของ document อันนี้ยังไม่เคยใช้เลย ยังไม่เคยรันกับโปรเจคใหญ่ๆ

Other Meta Field

  • _meta เก็บอะไรก็ได้ที่เราต้องการ เป็น domain specific ของเราเอง​ ซึ่งเจ้า Elasticsearch จะไม่มากวนใจ

มีประมานนี้ฮะ ที่จะจดไว้เรื่อง Mapping กับ Data type ไว้มีเจออะไรน่าสนใจจะมาเพิ่มเติมเนอะ

--

--