MongoBKK กลุ่มผู้ใช้ MongoDB ในสยามประเทศ

22Nov/120

materials MongoDB from OSSFasttrack [SIPA-KU]

เอกสารประกอบการอบรม หลักสูตร MongoDB  for Developer and Administrators

โดย SIPA ร่วมกับ ม.เกษตร สอนโดยคุณณัฐ ครับ

วันที่ 1 - 3

VDO ประกอบการอบรมทั้ง 3 วัน

12Jan/122

ทำ Sharding MongoDB ไม่ยากส์

พอดีต้องใช้เลยจดไว้กันลืม เดี๋ยวมาแก้ใหม่ให้สวยกว่านี้ครับ
การทำ Sharding ใน MongoDB
สำหรับ MongoDB แล้วจะใช้คอนเซ็บ proxy สำหรับการทำ sharding ดังนั้นเราจะกำหนด mongos daemon ขึ้นมาหนึ่งตัวเพื่อทำ หน้าทีเป็น controller สำหรับส่งงานไปให้ mongod-based shard servers ดังนั้นแอพพลิเคชั่นที่เราเขียนขึ้นมาจะติดต่อกับ daemon เป็นหลักและจะรู้สึกว่ากำลังทำงานอยู่กับ server เพียงตัวเดียวเท่านั้นนั่นหมายความว่าทุกๆคำสั่ง(เช่น updates, queirs, delete)จะถูกส่งเข้าไปที่ daemon ตัวเดียว

คือรับผิดชอบเรื่องการแจกงานไปให้ server หลังบ้านและในกรณีที่ต้องใช้ข้อมูลจากหลายๆเครื่องมารวมกันตัว daemon เองจะทำการรวมผลที่ได้จากหลายเครื่องเข้าด้วยกันและส่งกลับมายังแอพพลิเคชั่นเพื่อนำไปใช้งานต่อไป
สำหรับ mongodb เองแล้วจะสร้าง shard ในระดับ collection ไม่ใช่ระดับ database ดังนั้นเราจะพบว่าในระบบของเราเองจะมีข้อมูลในไม่กีี collection เท่านั้นทีมีจำนวนข้อมูลจากจนจำเป็นจะต้องถูก shard เนื่องจากการจัดการเรื่อง shard เป็นเรืองยุ่งยากมากพอสมควรจึงไม่มีความจำเป็นใดๆที่จะต้องทำ sharding ทุกๆ collection เพื่อให้เห็นภาพมากขึ้นเราจะลองมองไปที่ระบบ Social Network เราจะพบว่า user collection จะเป็นที่เก็บรายละเอียดของ user ทั้งหมดดังนั้นจำนวน user จะเพิ่มขึ้นจนถึงจุดที่ต้องทำ shard ส่วน collection อื่นไม่ว่าจะเป็น event, countries หรืออื่นๆยังไม่มีแนวโน้มที่จะขยายไปจนถึงสุดนั้น
กระบวนการทำ sharding จะอาศัย sharding key เพื่อจับ data ลงไปเก็บใน chunk (เป็น block การเก็บข้อมูลที่เป็น document) ซึ่งแต่ละ chunk เองจะเก็บเอกสารเป็นช่วงที่ต่อเนื่องกัน(ใช้ sharding key เป็นตัวแบ่ง) นั่นหมายความว่า controller จะรู้ทันทีว่าข้อมูลชุดไหนเก็บอยู่ที่ chunk ไหนโดยจะดูจากชุดของ sharding key นอกจากนี้ระบบการทำ sharding ของ mongodb เองยังเก็บ config ว่า sharding server ไหนเก็บข้อมูล chunk ใดๆไว้ในตัวมันบ้างซึ่งเรื่องนี่เป็นเรื่งสำคัญมากเพราะมันทำให้เราสามารถถอดหรือใส่ shard server ได้ตลอดเวลาโดยไม่ต้องมีการ back up, restore data
เมื่อใดก็ตามที่มีการเพิ่ม shard เข้าไปใน cluster ระบบจะทำการ redistribute chunks ใหม่ทั้งหมดไปที่กลุ่มของ server เพื่อทำการเพิ่มหระสิทธิภาพการทำงาน และในทำนองเดียวกันถ้ามีการดึง shard ออกจาก cluster ระบบก็จะทำการ redistribute chunk ใหม่เช่นกัน
ารตั้งค่าการทำ sharding ใน MongoDB จำเป็นจะต้องมีที่สำหรับเก็บ configuration ของ shards และที่สำหรับเก็บข้องมูลของการทำ shard ใน cluster เพื่อทำสิ่งนี้ให้ได้ mongodb มีส่ิ่งที่เรัยกว่า config server ซึ่งจริงๆแล้วตัวมันเองก็เป็น mongodb server ธรรมดาเครื่องหนึ่งแต่มีหน้าที่รับผิดชอบเฉพาะคือมันจะทำหน้าที่เหมือน directory ที่เก็บตำแหน่งว่า chunk แต่ละตัวอยู่ที่ไหนเท่านั้น
อย่างไรก็ตามหลายท่านอาจคิดว่าการทำ sharding จะต้องใช้ server จำนวนมากซึ่งจริงๆแล้วไม่ใช่อย่างนั้น เราสามารถสร้าง instance ของ MongoDB อยู่ใน Server เดียวได้หลายๆตัวเพื่อแก้ปัญหานี้ได้ ดังนั้นสิ่งที่เราต้องทำมีเพียงแค่การ set จำนวน instance ให้เท่ากับจำนวนน้อยที่สุดที่ sharding ต้องการซึ่งจากรูปด้านล่างเราสามารถทำ shrding ได้ด้วยการใช้ server เพียงสามตัวเท่านั้น

การตั้งค่าเบื้องต้นเพื่อสำหรับการทำ sharding
ส่วนที่แล้วเราเข้าใจกระบวนการทำงานเรื่อง sharding ของ mongodb แล้วส่วนต่อไปคือการลองลงมือทำเพื่อให้เห็นภาพมากขึ้น โดยเราจะลองสร้าง sharding environment ที่มีการทำ shard เพียงแค่ลอง shard server ดังนั้นเราจะสามารถสรุปการกำหนดค่าได้ตามตารางด้านล่าง

สิ่งแรกที่เราจะทำคือการ set configuration server ขึ้นมาก่อนโดนเราจะใช้คำสั่งนี้

$sudo mkdir -p /db/config/data
$sudo mongod --port 27022 --dbpath /db/config/data --configsvr

เมื่อสั่งเสร็จแล้วอย่าเพิ่งปิด terminal นะไม่งั้นมันจะหายไปเราต้องปล่อยให้มันวิ่งอยู่แบบนั้นก่อน ดังนั้นส่วนต่อไปเราต้องทำคือการทำ shard controller สิ่งที่เราต้องทำคือต้องเปิด terminal ใหม่ขึ้นมาและใช้ command ด้านล่าง

$sudo mongos --configdb localhost:27022 --port 27021 --chunkSize 1

คำสั่งนี้จะ start shard controller ขึ้นมาและรอฟังคำสังต่างๆที่พอร์ท 27021 และให้ไปคุยกับ configuration server ที่พอร์ท 27022
ตัวอย่างนี้เราจะพยายามกำหนดให้ chunk มีขนาดเล็กที่สุดเท่าที่จะทำได้ นั่นคือ 1 MB (ใช้ไม่ได้ในชีวิตจริงเพราะขนาด document ใหญ่ได้มากถึง 4MB) แต่นี่เป็นแค่ตัวอย่างเราเลยทำให้มันเล็กไว้ก่อน(แต่ถ้าเราไม่กำหนดขนาด ค่า default ของมันคือ 128 MB)
เหลืออีกสองอย่างที่เราจะต้องทำคือสร้าง sharding server อีกสองตัวโดยเราจะใช้คำสั่งตามนี้บน terminal ใหม่ๆสองอัน

$sudo mkdir -p /db/shard0/data
$sudo mongod --port 27023 --dbpath /db/shard0/data --shardsvr

อันที่สอง

$sudo mkdir -p /db/shard1/data
$sudo mongod --port 27024 --dbpath /db/shard1/data --shardsvr

เราได้ sharding environment มาแล้วส่วนต่อไปคือการต่อเข้ากับ controller เพื่อทดลองยิงคำสั่งและต้องไม่ลืมว่าเราต้องไม่อ้อมหลังไปต่อเข้ากับ sharding server โดยเด็ดขาดเพราะถึงมันจะเป็น mongodb instance เหมือนกันแต่มันไม่สมบูรณ์ในตัวมันเอง เรามาลองยิง command กัน

$ mongo localhost:27021
> use admin
switched to db admin
> db.runCommand( { addshard : "localhost:27023", allowLocal : true } ) { "added" : "localhost:27023", "ok" : 1 }

> db.runCommand( { addshard : "localhost:27024", allowLocal : true } )

{ "added" : "localhost:27024", "ok" : 1 }

เราจะเห็นว่า shrding ของเราถูก activate แล้วต่อไปเราจะลองตรวจสอบความถูกต้องด้วยการใช้ listshards
> db.runCommand({listshards:1})
{
"shards" : [ {

"_id" : "shard0",
"host" : "localhost:27023"
},

{
"_id" : "shard1",
"host" : "localhost:27024"
}
],
"ok" : 1 }

ต่อไปเราจะลองสร้าง database ชื่อ testdb ขึ้นมาและสร้าง testcollection ข้างใน testdb แน่นอนว่าเราจะ shard collection นี้ ดังนั้นเราจะกำหนด shard key ขึ้นมาอีกหนึ่งตัวนั่นคือ testkey

> testdb = db.getSisterDB("testdb")
testdb
> db.runCommand({ enablesharding: "testdb"})
{ "ok" : 1 }
> db.runCommand({ shardcollection : "testdb.testcollection", key : {testkey : 1}})
{ "collectionsharded" : "testdb.testcollection", "ok" : 1 }

ต่อไปเราจะลองยิงข้อมูลใส่เข้าไปที่ shard controller และลองตรวจสอบดูว่าข้อมูลถูกกระจายได้อย่างถูกต้องหรือไม่

// เขียนตามถนัดภาษาอะไรก็ได้

สิ่งแรกที่เราจะทำคือเราจะดูก่อนว่าที่ shard controller มีข้อมูลเท่าไหร่ด้วยการสั่ง

$mongo localhost:27021 >use testdb >db.testcollection.count() 100000

เราจะเห็นว่ามรีข้อมูลทั้งหมด 100,000 records. ต่อไปเราจะลองเข้าไปนับข้อมูลที่ตัว shard server ทีละตัวและดูว่ามีข้อมูลครบหรือไม่ shard ที่หนึ่งมี 48875

$mongo localhost:27023 >use testdb >db.testcollection.count() 48875

shard ที่สองมี 51125

$mongo localhost:27024
>use testdb
>db.testcollection.count()
51125

หล่อมาก

Filed under: General 2 Comments
22Dec/110

JMongoBrowser เครื่องช่วยคนไม่ชอบ command

JMongoBrowser เป็น GUI ที่ใช้สำหรับ browse และจัดการ MongoDB clusterเราสามารถโหลดมาใช้งานได้ทั้งบน Linux, Windows และ Mac OSX.
ความสามารถสำหรับเวอร์ชั่นนี้:
* connect to a single server, a replica set, or a MongoS instance
* DB ops: create, drop, authenticate, command, eval, …
* Collection ops: create, rename, drop, find, insert, save, …
* Document ops: update, duplicate, remove, …
* Index ops: create, drop, …
* Shard ops: enable sharding, add shard, shard collection, …
* GUI Document builder
* Import / Export data from database to local files in JSON, BSON, CSV format.
* Support for query options and write concerns (getLastError)
* Display of numerous stats (server status, db stats, replication info, etc)
* Mongo tree refreshes to have a real time view of cluster (servers up/down, durability, etc)
* All operations are executed in background to keep UI responsive
* Background threads can repeat commands automatically
* GUI is identical on all OS

14Oct/111

Little MongoDB Book: บทที่ 4

แปลมาจาก The Little MongoDB Book นะครับ

Chapter 4 - Data Modeling
บทนี้เราจะมาเสวนาเกี่ยวกับ data modeling ด้วยแนวคิดใหม่ซึ่งไม่ใช่เรื่องง่ายแน่นอนเพราะหลายๆคนยังสงสันว่าอะไรที่ควรทำและไม่ควรทำสำหรับการ model data ด้วยแนวคิดใหม่ อย่างไรก็ตามการเสวนาในบทนี้เป็นเพียงแค่แนวคิดเท่านั้นส่วนที่ต้องทำเพิ่มคือการลงมือทำและการเขียนโค้ดจริงๆเท่านั้น
สำหรับเรื่องการ model data นั้น document base ดูเหมือนจะแตกต่างจาก RDBMS น้อยที่สุดเมื่อเทียบกับ NoSQL ตัวอื่นๆอย่างไรก็ตามถึงแม้ว่า document base จะมีส่วนแตกต่างน้อยแต่ก็ไม่ได้หมายความว่าความแตกต่างเพียงเล็กน้อยนี้จะไม่สำคัญ

No Joins
เรื่องแรกและเป็นเรื่องที่สำคัญมากคือ MongoDB ไม่มี Join เพราะอย่างที่เรารู้กันว่า join เป็นตัวการทำให้ระบบเรา scale ยากดังนั้นเมื่อไหร่ก็ตามที่เราเริ่มแบ่งข้อมูลในแนวระนาบเรากำลังก้าวข้ามเข้าสู่สภาพแวดล้อมที่ scale ยาก :) แต่ถ้าเราใช้ RDBMS มันก็เลี่ยงไม่ได้ที่จะต้องใช้งาน Join เนื่องจากเราเก็บข้อมูลเชิง relational และเนื่องจาก MongoDB ไม่ใช่ relational ดังนั้นมันจึงไม่มี Join :)
ดังนั้นเมื่อ MongoDB ไม่มี join แน่นอนว่าการจะหาข้อมูลที่มีความเกี่ยวข้องกันต้องเกิดจากการเขียนโค้ดที่ระดับ application เองและสำหรับการนำข้อมูลเข้านั้นสามารถทำได้ด้วยการใส่คีย์ของ document ต้นทางให้เหมือนกับที่เรากำหนด foreign key ที่เราคุ้นเคย ดังนั้นเพื่อให้เห็นภาพมากขึ้นเราจะลองทำอะไรเล่นกันโดยเราจะสร้าง manager ขึ้นมาก่อนดังนี้

db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d730"), name: 'Leto'})

หลังจากนั้นเราจะสร้าง employee ที่อยู่ภายใต้ manager ตัวนี้เราสามารถทำได้ง่ายๆดังนี้

db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d731"), name: 'Duncan'
, manager: ObjectId("4d85c7039ab0fd70a117d730")});
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d732"), name: 'Moneo',
manager: ObjectId("4d85c7039ab0fd70a117d730")});

แน่นอนว่าถ้าเราต้องการ employee ที่อยู่ภายใต้ Leto ก็สามารถทำได้ดังนี้
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})

แน่นอนว่าไม่มีสิ่งมหัศจรรย์ใดๆสำหรับ MongoDB ในเมื่อไม่มี Join เราก็ต้องเขียน Query อีกอันเพื่อดึง Data :)

Arrays and Embedded Documents
บางท่านอาจเซ็งเรื่อง MongoDB ไม่มี join มันอาจให้เราต้องทำงานเพิ่มขึ้นหนึ่งขั้นสำหรับการดึงข้อมูลที่มีส่วนเกี่ยวเนื่องกัน อย่างไรก็ตามเรายังไม่อับจนหนทางเรายังมีทางออกเพราะ MongoDB นั้นให้ความสำคัญกับ Arrays มากหรืออาจเรียกว่าเป็นชั้นหนึ่งเลยทีเดียว นั่นหมายความว่าเราสามารถนำมันมาใช้แก้ปัญหาเรื่อง many-to-one, many-to-many ได้เป็นอย่างดีทีเดียวเชียวดังนั้นอย่ารอช้าเอามาลองใช้กับตัวอย่างของเราเลยโดยการเก็บ employee ดังนี้

db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona',manager:
[ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })

อย่างไรก็ตามบางครั้งบางเอกสารเอง manager สามารถเป็น scalar value ได้โดยที่ตัวอื่นๆกลายเป็นแค่ array ดังนั้น query ดิมที่เราเขียนไว้สามารถนำกลับมาใช้งานกับทั้งสองกรณี

db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})

ดังนั้นเราจะเห็นว่า array จะมีประโยชน์มากเมื่อเราต้องทำงานกับเอกสารที่ต้องการเก็บแบบเชื่อมไปยังเอกสารอื่นเป็นแบบ many-to-many
นอกจาก array แล้ว MongoDB ยังรองรับการใช้งานสิ่งที่เรียกว่า embedded document ด้วยมันเป็นยังไงอย่าเสียเวลาอธิบายลองเลยครับ

db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima
', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4
d85c7039ab0fd70a117d730")}})

หลังจากจัดเก็บลงไปแล้วเราจะเอามันขึ้นมาก็สามารถอ้างถึงได้ด้วย dot-notation ดังนี้

db.employees.find({'family.mother': 'Chani'})

DBRef
(แปลไม่ออก กลัวมั่วขอลองใช้ก่อนครับ)MongoDB supports something known as DBRef which is a convention many drivers support. When a driver encounters a DBRef it can automatically pull the referenced document. A DBRef includes the collection and id of the referenced document. It generally serves a pretty specific purpose: when documents from the same collection might reference documents from a differ- ent collection from each other. That is, the DBRef for document1 might point to a document in managers whereas the DBRef for document2 might point to a document in employees.

Denormalization
ทางเลือกสำหรับการไม่มี joins อีกทางคือ denormalize data ซึ่งการทำ denormailize นี้แต่ก่อนมีไว้สำหรับงานบางประเภทที่ต้องการเรื่องประสิทธิภาพหรือในางกรณีที่เราต้องทำ snapshot เช่น audit log อย่างไรก็ตามหลังจากที่ NoSQL ได้รับความนิยมมากขึ้นและหลายตัวก็ไม่สนับสนุน joins ทำให้ denormailization กลายเป็นเรื่องปกติสำหรับการทำ data modeling ของ NoSQL อย่างไรก็ตามการทำ denormalization ก็มีข้อควรระวังว่าอาจเกิดการซ้ำซ้อนของข้อมูลได้ ดังนั้นการทำ data model ควรเลือกเก็บข้อมูลที่จำเป็นและเก็บไว้ในเอกสารที่มันควรจะอยู่เท่านั้น ยกตัวอย่างเช่นถ้าเราต้องทำ forum application สมัยก่อนแต่ละหัวข้อเราจะเก็บ userid ไว้สำหรับการอ้างถึงในยามต้องการดังนั้นทุกครั้งที่เราต้องการแสดง post ใดๆก็ตามเราต้องข้ามไปดึงข้อมูลจากฝั่ง user มาด้วยไม่เช่นนั้นเราจะไม่รู้ว่าใครกันแน่เป็นคนสร้าง post นั้นเพราะเราเก็บไว้แต่ userid ดังนั้นสำหรับ MongoDB แล้วเราสามารถเก็บชื่อของคนเขียน post ลงไปเลยโทงๆ หรือสามารถทำได้แม้กระทั่งฝังเป็น embedded document ได้ดังนี้เช่น

{id: ObjectId('Something'), name: 'Leto'}.

อย่างไรก็ตามอย่าลืมว่า ไม่มีสิ่งมหัศจรรย์ เพราะเมื่อไหร่ก็ตามที่ user เปลี่ยนชื่อเมื่อนั้นเราต้องมาตามเช็ดกันทีละ post เลยทีเดียวเชียว
อย่างไรก็ตามแนวคิดนี้อาจดูโคตรโง่เลยสำหรับงานบางอย่าง แต่อย่าไปกลัวครับเพราะสำหรับบางงานแนวคิดนี้ก็โคตรเทพเช่นกันดังนั้นอย่ากลัวที่จะลองคิดด้วยวิธีนี้เมื่อต้องแก้ปัญหาบางประเภทที่เราหาทางออกไม่ได้ด้วยวิธีเดิมๆ

Which Should You Choose?
ยังไงดี เอาอย่างนี้ก่อน array of ids เหมาะสำหรับการทำ one-to-many หรือ many-to-many เพราะมันค่อนข้างปลอดภัยดังนั้นเราสามารถพูดได้ว่าการใช้ DBRef นั้นอาจเกิดขึ้นไม่บ่อย อย่างไรก็ตามมันก็ยังไม่มรกฎตายตัวให้เราใช้สำหรับการเลือกใช้ embedded document หรือการอ้างด้วยมือเราเอง
แต่อย่างไรก็ตามมีข้อมูลบางอย่างที่เราควรจะรู้และอาจเป็นประโยชน์ในการพิจรณานั่นคือ document แต่ละตัวจะต้องมีขนาดไม่เกิน 4MB ดังนั้นเมื่อเรารู้ถึงข้อจำกัดนี้จะทำให้เราเริ่มมีหลักคิดในกรณีที่เราต้องเลือก solution ดังนั้นจะเห้นได้ว่าคนส่วนมากจะเลือกการใช้ manual ref แทนเพราะเราจะได้ไม่ต้องพบข้อจำกัดนี้อย่างไรก็ตามในการทำงานจริงๆเราก็ไม่สามารถทนใช้ manual ref ไปได้ตลอดเพราะมันไม่เป็นธรรมชาติยกตัวอย่างเช่นการเก็บข้อมูล user เราควรจะเก็บแบบนี้มากกว่า

db.users.insert({name: 'leto', email: 'leto@dune.gov', account: {
allowed_gholas: 5, spice_ration: 10}})

ดังนั้นเราจะพบว่าการเก็บเอกสารแบบ embedded นั้นจะทรงพลังมากสำหรับงานบางประเภทเพราะเราไม่ต้องอาศัยกระบวนการ join เพื่อให้ได้มาซึ่งข้อมูลทีุ่ถูกต้องและอย่าลืมว่า MongoDB ยอมให้เรา query และ index เอกสารที่เป็น embedded ด้วยเช่นกัน

Few or Many Collections
อย่างที่เรารู้กันว่า collections ใน MongoDB นั้นไม่ได้บังคับเกี่ยวกับ schema ดังนั้นจึงเป้นไปได้ที่เราจะเก็บเอกสารทั้งหมดไว้ใน collection เดียวกันทั้งๆที่เอกสารเหล่านั้นจะมีรูปแบบที่ไม่เหมือนกันเลยก็ตาม ดังนั้นบ่อยครั้งเราจะเห็นการใช้ MongoDB ทำการเก็บข้อมูลโดยที่จะมีวิธี model คล้ายๆกับสิ่งที่เราทำใน relational model ดังนั้นจึงไม่แปลกที่อะไรก็ตามที่เราคิดว่าเป็น table ใน relational-model ก็สามารถแปลงเป็น collection ใน MongoDB ได้เช่นกัน อย่างไรก็ตามเมื่อเราข้ามมาเสวนากันเกี่ยวกับ embedded document เราก็มีบางอย่างที่เราต้องพิจรณาเพิ่มขึ้นเช่นถ้าเรากำลังทำระบบ blog เราควรจะมี post collection กับ comment collection แยกกันหรือเราควรจะมี post และทำ comment ของแต่ละ post ให้เป็น embedded document ซึ่งจริงๆสำหรับ MongoDB สามารถทำได้ทั้งสองแบบ แต่ถ้าเราพิจรณาเรื่องขนาดของ document ที่สามารถมีขนาดได้ไม่เกิน 4M เราอาจต้องย้อนกลับไปใช้ solution แรกเพราะถ้า post ของเราได้รับความนิยมสูงก็อาจทำให้มันมีขนาดใหญ่กว่า 4M ได้ดังนั้นมันจึงไม่มีข้อบังคับใดๆสำหรับ MongoDB เราสามารถลองเล่นได้เสมออย่าได้กลัวแล้วเราจะรู้เองว่า การทำ data model แบบไหนที่จะเหมาะกับเรา

In This Chapter
จุดประสงค์ของบทนี้คือให้คำแนะน่ำเบื้องต้นเกี่ยวกับการทำ data modeling เบื้องต้นใน MongoDB และเราจเห็นว่าการทำ data modeling ใน document based นั้นถึงแม้จะไม่เหมือนกับ relational-model แต่ก็ไม่ได้แต่กต่างกันมากนักมีางอย่างที่น่าสนใจมากเช่นกันและแน่นอนว่าวิธีการที่ดีที่สุดคือการลงมือทำ

4Oct/110

Little MongoDB Book: บทที่ 3

แปลมาจาก The Little MongoDB Book นะครับ

บทที่ 3 - Mastering Find
ในบทที่ 1 เราได้เห็นบางส่วนของคำสั่ง find ไปแล้วแต่นั่นเป็นแค่ส่วนน้อยนิดเท่านั้นในบทนี้เราจึงกลับมาอีกครั้งเพื่อแสดงพลังของ find ให้เห้นว่าจริงๆแล้วเราสามารถใช้มันทำอะไรได้บ้าง
Field Selection
ก่อนที่เราจะข้ามไปที่เรื่อง cursors เราย้อนอดีตกันนิดว่าคำสั่ง find รับพารามิเตอร์ตัวที่สองและพารามิเตอร์ตัวนี้เป็นรายการฟิวด์ที่เราต้องการดึงขึ้นมา ยกตัวอย่างเช่นเราสามารถดึงข้อมูลของ Unicorns ขึ้นมาได้ด้วยคำสั่งนี้

db.unicorns.find(null, {name: 1});


และผลที่ได้โดยปกติแล้วเราจะได้ _id ติดมาด้วยอยู่เสมอแต่ถ้าเราต้องการที่จะตัดมันออกไปก็สามารถใช้คำส่งแบบนี่

{name :1, _id: 0}.

Ordering
ก่อนหน้านี้เราได้พูดถึงผลของการค้นหาที่เราได้มานั้นจริงๆแล้วมันคือ cursor ซึ่งตัว cursor เองจะไม่ทำงานจนกว่าเราจะต้องการผลที่ได้จริงๆออกมาใช้งาน แต่เนื่องจากที่ผ่านมาเราทำงานทั้งหมดผ่าน shell ดังนั้นเราอาจจะไม่ได้เห็นสิ่งนี้ชัดเจนนักเนื่องจากมันเป็นพฤติกรรมเฉพาะของ shell เท่านั้นแต่ถ้าเราต้องการดูพฤติกรรมของ cursor ให้ชัดเจนเราต้องมาดูโค้ดชุดนี้กัน อันดับแรกเรามาดูการ sort กันก่อนซึ่ง sort นั้นจะทำงานคล้ายๆ field selection ในหัวข้อก่อนหน้านี้ คือเราต้องระบุ field ที่ต้องการจะ sort ก่อนหลังจากนั้นให้ระบุวิธีการ sort ด้วยการกำหนดว่า 1(ascending), -1(descending)

//heaviest unicorns first
db.unicorns.find().sort({weight: -1})
//by vampire name then vampire kills:
db.unicorns.find().sort({name: 1, vampires: -1})

นี่เป้นตัวอย่างเล็กๆของการทำ ordering และแน่นอนว่าสำหรับ MongoDB นั้นเราสามารถทำ indexing ได้ด้วยเช่นกันและที่สำคัญเราต้องรู้ด้วยว่า MondoDB นั้นจำกัดปริมาณข้อมูลต่อ query สำหรับข้อมูลใดๆก็ตามที่ไม่ถูกทำ index เพราะ MongoDB ถือว่า Database มีสิทธิ์ที่จะไม่ run query ที่ไม่ถูก optimize (เท่จริงอะไรจริงฟีเจอร์นี้)

Paging
เราสามารถทำ paging ได้ด้วยการใช้ limit และ skip เมธอดกับ cursor ดังตัวอย่างถ้าเราต้องการได้ unicorn ตัวที่หนักที่สุดเป็นลำดับที่ สอง และ สามเราสามารถทำได้ดังนี้้

db.unicorns.find().sort({weight: -1}).limit(2).skip(1)

เราสามารถใช้ limit ่กับ sort ได้และนี่เป็นทางออกที่ดีสำหรับการเลี่ยงปัญหาในกรณีที่เราต้องเรียงข้อมูลการ field ที่ไม่ได้ทำ index ไว้

Count

และแน่นอนว่าเราสามารถ count ได้เหมือนกับ database ทั่วไปเช่นกัน

db.unicorns.count({vampires: {$gt: 50}})

ตัวอย่างโค้ดนี้แสดงให้เห็นว่า count เป็นเมธอดของ cursor แต่เนื่องจากเราใช้ shell เราเลยสามารถเรียกแบบย่อได้แต่ถ้าเราเรียกจาก driver ของภาษาอื่นๆที่ไม่ได้เตรียม shortcut ไว้ให้เราต้องเรียกแบบนี้

db.unicorns.find({vampires: {$gt: 50}}).count()

สรุปท้ายบท
บทนี้สั้นมากเราได้เห็นเรื่อง find และ cursor จะว่าลึกไหมก็ไม่เท่าไหร่และได้เห็นกระบวนท่าการทำ sorting, ordering แบบเล็กๆที่เป็นพื้นฐานที่ดีสำหรับการทำงานที่ซับซ้อนกว่านี้

30Sep/110

Little MongoDB Book: บทที่ 2 ตอนที่ 2

แปลมาจาก The Little MongoDB Book นะครับ

Upserts

สิ่งหนึ่งที่น่าสนใจมากสำหรับคำสั่ง update ใน MongoDB คือมันรองรับการทำงานแบบ upsert ได้โดยที่คำสั่ง upsert คือความสามารถในการทำสองอย่างในหนึ่งคำสั่งแล้วแต่เงือนไข ว่าถ้าเราต้องการเปลี่ยนแปลงอะไรสักอย่างกับ document โดยที่เราเองก็ยังไม่แน่ใจว่ามันมีอยู่หรือป่าวเมื่อเราสั่ง upsert มันจะตรวจสอบก่อนว่ามี document นั้นอยู่จริงไหม ถ้ามีก็ update ตามปกติแต่ถ้าไม่มีสิ่งที่ต้องทำคือ insert มันลงไปใหม่ โดยถ้าเราต้องการใช้ upsert สิ่งที่เราต้องทำคือ เพิ่มพารามิเตอร์ตัวที่สามเข้าไปและกำหนดค่าให้เป็น true
ตัวอย่างที่เหมาะสมที่สุดคือการทำ counter สำหรับเว็บส่วนใหญ่เรามักจะตรวจสอบกก่อนเสมอว่าสิ่งที่เรากำลังจะใส่ลงไปมีอยู่หรือไม่ ถ้ามีเราก็ update ค่าแต่ถ้าไม่มีเราก็ insert มันลงไปใหม่ ถ้าเราไม่ใช้ upsert ตามตัวอย่างคำสั่งด้านล่างผลที่ได้คือไม่มีอะไรเกิดขึ้น

db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}});
db.hits.find();

อย่างไรก็ตามถ้าเรากำหนดให้ทำ upsert ผลที่ได้จะแตกต่างกันโดยสิ้นเชิง

db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true); db.hits.find();

เนื่องจากยังไม่มีเอกสารอะไรเลยที่มี field ชื่อ unicorns ดังนั้นสิ่งทีมันทำคือ insert เอกสารใหม่ลงไปหนึ่งตัวและกำหนดค่า hits เป็น 1 ดังนั้นถ้าเราสั่งคำสั่งเดิมลงไปอีกครั้งสิ่งที่เราได้คือ hits จะเปลี่ยนค่าเป็น 2

db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true); db.hits.find();

Multiple Updates

เอกสารหนึ่งตัวได้ง่ายๆขนาดไหน อย่างไรก็ตามยังมีอีกอย่างที่น่าสนใจคือการ update เอกสารครั้งละหลายๆตัว จะเกิดอะไรขึ้นถ้าเราสั่งคำสั่งนี้

db.unicorns.update({}, {$set: {vaccinated: true }}); db.unicorns.find({vaccinated: true});

อันนี้เกือบถูกแต่ยังไม่ถูกเพราะในกรณีที่เราต้องการทำ multiple update สิ่งที่เราต้องทำคือกำหนด พารามิเตอร์ตัวที่ 4 ให้เป็น true ดังนี้

db.unicorns.update({}, {$set: {vaccinated: true }}, false, true); db.unicorns.find({vaccinated: true});

สรุป
บทนี้เราได้เรียนรู้วิธีการทำงานง่ายๆอย่าง CRUD operation ไปเรียบร้อยแล้ว เราได้รู้ว่าจริงๆแล้วคำสั่ง update ใน mongodb คือการ replace document เดิมที่มีอยู่ แต่ถ้าเราต้องการเปลี่ยนค่า field บางอย่างก็ให้ใส่ $set เข้าไป ต่อมาเราได้เรียนวิธีการทำ upsert รวมไปถึงการทำ multiple update ในตอนท้ายของบทอีกด้วยและสุดท้ายการสั่ง update นั้นถ้าเราไม่กำหนดอะไรชัดเจนสิ่งที่ MongoDB จะทำคือมันจะทำกับเอกสารตัวแรกที่มันเจอเพียงตัวเดียวก่อนเท่านั้น
อย่างไรก็ตามสิ่งที่เราได้เรียนในบทนี้เป็นการเรียนโดยผ่านมุมมองของ Mongo Shell ซึ่งในทางปฎิบัติเมื่อเราเขียน program ต่อกับ MongoDB ผ่าน Driver ของภาษาต่างๆเราอาจพบว่ามีข้อแตกต่างปลีกย่อยระหว่างภาษานั้นๆบ้างเช่นใน ruby เอง driver api จะทำการรวมพารามิเตอร์สองตัวสุดท้ายไว้ด้วยกันใน hash ดังนี้ {:upsert => false, : multi => false}.

29Sep/110

Little MongoDB Book: บทที่ 2 ตอนที่ 1

แปลมาจาก The Little MongoDB Book นะครับ

จากบทที่ 1 เราได้เรียนรู้การทำงานกับ MongoDB เบื้องต่นไปแล้วทั้งหมดทั้ง Create Read Update Delete แต่มันเป็นแค่เบื้องต้นเท่านั้นในบทนี้เราจะมาลันดูแก แลดูกันว่าเราจะ update มันอย่างเดียวมันมีอะไรให้ศึกษาบ้าง

Update:ความต่างระหว่าง Replace Versus $set
สองตัวคือ selector(where) คือจะไป update อะไรและตัวต่อมาคือฟิวด์ที่จะไป update นั้นเราจะใส่ค่าอะไรลงไปดังนั้นจากตัวอย่างด้านล่างเราจะหา “Roooooodles” เพื่อไปเพิ่มนำหนักให้มันหน่อย

db.unicorns.update({name: 'Roooooodles'}, {weight: 590})

(*****เราแนะนำให้ท่าน populate date ใหม่ทั้งหมดสำหรับ Unicorns DB เพื่อป้องกันให้เรามีข้อมูลที่ตรงกัน)
อย่างไรก็ตามในความเป็นจริงเราอาจจะอยาก update ข้อมูลโดยการอ้างถึง _id แท่นเพื่อความแน่นอนแต่เนื่องจากเราไม่รู้ว่าเจ้า MongoDB สร้างเลขอะไรให้เราดังนั้นถ้าเราอยากรู้เราต้องสั่งดูสิว่าเจอไหม

db.unicorns.find({name: 'Roooooodles'})

แล้วเราก็จะแปลกใจว่าทำไมไม่มี document นี้อยู่ ???? ไม่ต้องแปลกใจที่มันหายไปเพราะเราดันไป update มันไง :) เนื่องจากคำสั่งแรกทีเราหวังว่าสั่งเปลี่ยนน้ำหนักนั้นจริงๆแล้วพารามิเตอร์ตัวที่สองที่เราส่งไปนั้นเป็นการบอกว่าเรา replace ตัว document ต้นทางเลยดังนั้น Roooooodles จึงหายไป ดังนั้นคำสั่ง update ของ MongoDB จะต่างกับ update ใน RDBMS ดังนั้นการจะใช้คำสั่งนี้ต้องระวังเป็นอย่างยิ่ง อย่างไรก็ตามถ้าเราต้องการจะ update บางค่าใน document จะทำยังไงคำตอบคือเราต้องใช้ $set ครับ

db.unicorns.update({weight: 590}, {$set: {name: 'Roooooodles', dob: new Date (1979, 7, 18, 18, 44), loves: ['apple'], gender: 'm', vampires: 99}})

คำสั่งด้านบนเป็นการ update ค่าใน document ดังนั้นไม่ต้องกลัวว่า weight:590 จะหายไป ดังนั้นเราลองมาดูผลก่อนว่าสำเร็วไหม

db.unicorns.find({name: 'Roooooodles'})

เราได้สิ่งที่เราต้องการกลับมาแล้วครับ คราวนี้เราลองมาดูสิว่าเราจะเปลี่ยน weight ให้ถูกต้อต้องทำอย่างไร

db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 600}})

Update Modifiers
อย่างไรก็ตามในคำสั่ง update เองก็สามารถส่งพารามิเตอร์อื่นๆเข้าไปได้อีกสำหรับการเปลี่ยนค่าบางประเภทเช่น $inc เราสามารถใช้สำหรับการเพิ่มหรือลดค่าที่เป็นตัวเลขเช่นถ้าเราต้องการเปลี่ยนค่า vampires ให้กับ Pilot ลดลงไปสองเราสามารถทำได้แบบนี้

db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})

หรือถ้า Aurora เกิดชอบของหวานขึ้นมาเราก็สามารถเพิ่มค่าเข้าไปที่ field loved ได้ด้วย $push
db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})

ยังมีพารามิเตอร์อีกหลายตัวที่เรายังไม่ได้นำมาแสดงแต่ก็สามารถไปอ่านเพิ่มได้ที่เว็บ MongoDB นะครับ

18Sep/112

Little MongoDB Book: บทที่ 1 ตอนสอง

แปลมาจาก The Little MongoDB Book นะครับ

เมพส์ Selectors
หลังจากเราได้เรียนรู้แนวคิดหกข้อที่สำคัญมากของ MongoDB แล้วก่อนที่เราจะไปสู่ขั้น advance เราก็ควรจะรู้สักหน่อยก่อนว่าวิธีการเรียกข้อมูลออกมาจาก MongoDB เนี่ยมันทำยังไง สำหรับ MongoDB นั้น query selector ละม้ายคล้ายกัย where clause ใน SQL Statement เราจะใช้มันในตอนที่เราต้องการจะหา นับ แก้ไข ลบ document จาก collection โดนที่ selector นี้คือ JSON object ดังนั้นยกตัวอย่างเช่น ถ้าเราต้องการดึงข้อมูลทั้งหมดที่เราต้องการออกมาจาก collection เราก็ใส่ {} และถ้าเราต้องการหา female unicorn สิ่งที่เราต้องทำคือใส่ condition เพิ่มเข้าไปดังนี้ {gender:'f'}

เริ่มคันไม้คันมือกันแล้วดังนั้นเรามา setup data กันก่อนเดี๋ยวจะได้ select กันให้มันมือ สิ่งแรกที่เราต้องทำคือล้างข้อมูลเก่าที่เราเพิ่งใส่ลงไปใน db.unicorns ด้วยคำสั่ง db.unicorns.remove() จากนั้นเพิ่มข้อมูลใหม่ลงไปด้วยคำสั่งด้านล่าง (อย่าตกใจ copy n paste ได้)

17Sep/113

Little MongoDB Book: บทที่ 1

แปลมาจาก The Little MongoDB Book นะครับ

## บทที่ 1 - The Basics ##
เราจะเริ่มการผจญภัยของเราด้วยการเรียนรู้การทำงานของกลจักรของมองโกดีบีกันก่อน เพราะเมื่อเราเข้าใจแก่นแท้หรือแนวคิดพื้นฐานของมันแล้วเราจะสามารถใช้มันเพื่อตอบคำถามคลาสสิก "มองโกดีบี" เหมาะกับอะไรได้อย่างชัดเจน

สำหรับการเริ่มต้นนี้ มีข้อมูลพื้นฐานหกข้อที่เราจะต้องเข้าใจมัน

1. มองโกดีบีมีแนวคิดพื้นฐานเหมือนกับ 'database' ที่เราคุ้นๆกันอยู่ (หรือ schema สำหรับสาวกโอราเคิล) ดังนั้นภายใต้ MongoDB instance เราสามารถที่จะมี database กี่ตัวก็ได้ซึ่ง database แต่ละตัวจะทำหน้าที่เป็นเหมือนที่เก็บของประเภทต่างๆที่มีใน MongoDB

2. สำหรับ Database หนึ่งตัวสามารถที่จะมี collection กี่ตัวก็ได้ ซึ่งเราสามารถเปรียบเทียบ collection เหมือนกับ table ได้เช่นกันจะได้เห็นภาพได้ง่ายขึ้น (แต่มันไม่ใช่สิ่งเดียวกันนะมันอยู่ระดับเดียวกัน)

3. สำหรับ Collection เองก็นั้นมีไว้สำหรับเก็บสิ่งที่เรียกว่า 'document' ซึ่งเรามองไปที่สิ่งที่เราคุ้นเคยในระดับเดียวกันเราจะเห็นสิ่งที่เรียกว่า row

4. สำหรับ document เองก็ประกอบไปด้วย field ต่างๆมากมายและแน่นอน ที่ระดับเดียวกันเราจะมองเห็น column

5. Index ใน MongoDB เหมือนกันกับสิ่งที่มีใน RBDMS

6. สุดท้ายคือ 'Cursor' อันนี้ต่างจากห้าข้อด้านบนแต่มันมีความสำคัญมากใน MongoDB เพราะทุกครั้งที่เราถามหาข้อมูลจาก MongoDB สิ่งที่เราได้กลับมาคือ cursor ซึ่งเราสามารถทำอะไรกับมันได้เช่นนับ จับมันข้ามไปข้างหน้าหรืออื่นๆ โดยที่เราไม่ได้ดึงข้อมูลจริงๆออกมาเลย

10Jun/114

ตารางเปรียบเทียบ SQL กับ Mongo

MongoDB queries จะถูก express ออกมาในรูปแบบของ  JSON (BSON) objects.  ตารางเปรียบเทียบนี้ถูกสร้างขึ้นมาเพื่อให้เราพอเข้าใจและเห็นภาพการทำงานของ MongoDB ได้ดีขึ้นเนื่องจากเราสามารถเปรียบเทียบกลับไปหาโลกเก่าแบบที่เราคุ้นเคยได้ ลองทัศนาครับ


SQL Statement
Mongo Query Language Statement
CREATE TABLE USERS (a Number, b Number)
db.createCollection("mycoll")
INSERT INTO USERS VALUES(1,1)
db.users.insert({a:1,b:1})
SELECT a,b FROM users
db.users.find({}, {a:1,b:1})
SELECT * FROM users
db.users.find()
SELECT * FROM users WHERE age=33
db.users.find({age:33})
SELECT a,b FROM users WHERE age=33
db.users.find({age:33}, {a:1,b:1})
SELECT * FROM users WHERE age=33 ORDER BY name
db.users.find({age:33}).sort({name:1})
SELECT * FROM users WHERE age>33
db.users.find({'age':{$gt:33}})})
SELECT * FROM users WHERE age<33
db.users.find({'age':{$lt:33}})})
SELECT * FROM users WHERE name LIKE "%Joe%"
db.users.find({name:/Joe/})
SELECT * FROM users WHERE name LIKE "Joe%"
db.users.find({name:/^Joe/})
SELECT * FROM users WHERE age>33 AND age<=40
db.users.find({'age':{$gt:33,$lte:40}})})
SELECT * FROM users ORDER BY name DESC
db.users.find().sort({name:-1})
SELECT * FROM users WHERE a=1 and b='q'
db.users.find({a:1,b:'q'})
SELECT * FROM users LIMIT 10 SKIP 20
db.users.find().limit(10).skip(20)
SELECT * FROM users WHERE a=1 or
b=2
db.users.find( { $or : [ { a : 1 } ,
  { b : 2 } ] } )
SELECT * FROM users LIMIT 1
db.users.findOne()
SELECT DISTINCT last_name FROM users
db.users.distinct('last_name')
SELECT COUNT(*y)
FROM users
db.users.count()
SELECT COUNT(*y)
FROM users where AGE > 30
db.users.find({age:
{'$gt': 30}}).count()
SELECT COUNT(AGE)
from users
db.users.find(
{age: {'$exists': true}}).count()
CREATE INDEX myindexname ON users(name)
db.users.ensureIndex({name:1})
CREATE INDEX myindexname ON users(name,ts DESC)
db.users.ensureIndex({name:1,ts:-1})
EXPLAIN SELECT * FROM users WHERE z=3
db.users.find({z:3}).explain()
UPDATE users SET a=1
WHERE b='q'
db.users.update({b:'q'},
{$set:{a:1}}, false, true)
UPDATE users SET a=a+2
WHERE b='q'
db.users.update({b:'q'},
{$inc:{a:2}}, false, true)
DELETE FROM users WHERE z="abc"
db.users.remove({z:'abc'});
Filed under: General 4 Comments