Barg the Blog

You're looking at an article archive of Barg the Blog, all contents here are no longer updated.

Please update your bookmark to Grid.in.th.


Introduction to CouchDB with Pylons

สารภาพว่าก่อนหน้านี้ผมไม่เคยคิดที่จะมอง CouchDB อยู่ในสายตาเลยแม้แต่น้อย อาจจะเรียกว่าเป็นคนสาย RDBMS เต็มขั้นจนไม่เห็นความจำเป็นที่จะต้องเปลี่ยนไปใช้ฐานข้อมูลที่สร้างขึ้นด้วยแนวคิดอื่น แต่หลังจากอ่านที่มีคนอธิบายความสามารถของ CouchDB ใน Reddit ทำให้เกิดความสนใจขึ้นมาเล็กน้อยทำให้เริ่มลองศึกษาแนวคิดการทำงานของมัน

คอนเซปของ CouchDB ก็คือไม่มีการใช้ schema เป็นเบื้องหลังฐานข้อมูล และไม่มีคอนเซปที่เรียกว่าตาราง แต่ละแถวในฐานข้อมูลถูกเรียกว่า “document” และแยกออกจากกันชัดเจน โดยไม่จำเป็นต้องมีรูปแบบตายตัว สิ่งที่ได้มาจากการทำแบบนี้คือความยืดหยุ่นของข้อมูลในฐานข้อมูลหนึ่งๆ

ตัวอย่างง่ายๆ ที่สามารถนำคอนเซป document ของ CouchDB ไปใช้ได้คือ node ของ Drupal ที่สามารถเป็นได้หลายอย่าง ในกรณีของการใช้ RDBMS คุณต้องมานั่งปวดหัวกับ schema ของตารางที่จะยุ่งวุ่นวายขึ้นตามความหลากหลายของข้อมูลใน node แต่สำหรับการใช้งาน CouchDB แล้ว มันก็เป็นเพียงแค่ document หนึ่งๆ เท่านั้น

ชอยส์เฟรมเวิร์คที่ผมเลือกใช้ในการทำงานกับ CouchDB ก็คือ Pylons ที่ไม่เคยสนใจว่าเราจะใช้อะไรเป็น model และด้วยคอนเซป “กลับหลัง” ของ Pylons ทำให้การใช้งาน CouchDB ร่วมกันสามารถทำได้ด้วยการลง CouchDB-Python ไลบราลี่สำหรับติดต่อ CouchDB และแก้ไขไฟล์ model/__init__.py

from couchdb import client
server = client.Server('http://localhost:5984/')
database = server['my_database']
# There's no fourth line.

สิ่งที่ถูก import ออกมาจาก CouchDB-Python มีหลักๆ ด้วยกันสองอย่างคือ client ที่ขาดไม่ได้ ใช้สำหรับการเชื่อมต่อฐานข้อมูลและทำงานกับ document ต่างๆ และ schema ที่สามารถเลือกใช้หรือไม่ใช้ก็ได้ สำหรับแปลง document ในฐานข้อมูลให้กลายเป็น Python object (ในตัวอย่างด้านบนไม่ได้ถูก import เข้าไปด้วย)

สำหรับการใช้งาน client ก็ตรงไปตรงมา

from couchdb import client
server = client.Server('http://localhost:5984/')
# สร้างฐานข้อมูลใหม่
database = server.create('my_database')
# สร้าง document ใหม่ในฐานข้อมูล
database.create({'user': 'sirn', 'favorites': ['Python', 'Spaghetti']}) # -> 67f12c55c56efca50c779bd3db25cb61
# เรียกหา document ในฐานข้อมูล
database['67f12c55c56efca50c779bd3db25cb61']
# ลบฐานข้อมูล
del server['my_database']

ในส่วนของการใช้งาน schema นั้น ก่อนอื่นเราต้องทำการระบุ schema ของข้อมูลเสียก่อน… แต่เอ๊ะ ไหนบอกว่า CouchDB ไม่จำเป็นต้องมี schema? เจ้า schema ที่ว่านี้จะมีตัวตนอยู่เพียงเพื่อสำหรับ Python object เท่านั้น และจะไม่ถูกเก็บลงไปในฐานข้อมูล การใช้งานเบื้องต้นสามารถเขียนลงไปใน model/__init__.py ได้เลยเช่นกัน

from couchdb import client
from couchdb.schema import Document, Schema
from couchdb.schema import DateTimeField, IntegerField, TextField
from couchdb.schema import ListField, DictField

server = client.Server('http://localhost:5984/')
database = server['my_database']

# Tables (of sorts)
class User(Document):
    user = TextField()
    favorites = ListField()
    projects = ListField(DictField(Schema.build(
        title = TextField(),
        url = TextField()))) # Too many parenthesis...

นอกเหนือจากการระบุ schema แบบปกติแล้วสำหรับ CouchDB-Python ยังสามารถซ้อน schema เข้าไปใน schema ได้ด้วย ถ้าเทียบกับการใช้งาน RDBMS แล้วจะเหมือนกับการทำ One-to-Many หรือ Many-to-Many เพียงแต่ข้อมูลทั้งหมดจะถูกเก็บอยู่ใน document เดียว

ถ้าหากแตกข้อมูล schema ด้านบนออกมาเป็น JSON ที่จะถูกเก็บใน CouchDB แล้วมันจะออกมาเป็นข้อมูลหน้าตาเรียบง่าย

[{
    'user': user,
    'favorites': favorite,
    'projects': [
        {'title': title, 'url': url},
        {'title': title, 'url': url},
        ...
    ]
},
...
]

การนำ schema ไปใช้กับ Pylons ใน controller ก็จะคล้ายกับการใช้ ORM ทั่วไป เพียงแต่จะแตกต่างเล้กน้อยคือเราจำเป็นต้องเขียน map function เพื่อเรียกหาข้อมูลจากฐานข้อมูลด้วย JavaScript (ตามคอนเซปของ MapReduce) การทำแบบนี้จะเรียกว่า Temporary View (อ่าน HTTP view API เพิ่มเติม)

from appname.model import User, database

def list(self):
    map_func = "function(doc) { emit(null, doc); }"
    c.user = User.query(database, map_func, None)
    # -> [<User '67f12c55c56efca50c779bd3db25cb61'@...

def create(self):
    user = User()
    user.user = 'jane'
    user.favorites = ['Ruby', 'Pizza']
    user.projects.append({'title': 'Example', 'url':'http://example.com/'})
    user.store(database)

def show(self, id):
    c.user = User.load(database, id)

def delete(self, id):
    del database[id]

การใช้งาน Temporary View นั้นจะช้ากว่าปกติ เพราะจะมีการทำ index ใหม่ทุกครั้งเมื่อมีการสั่ง map function นั้นๆ สู่ CouchDB ถึงแม้ข้อมูลจะไม่เปลี่ยนแปลง ดังนั้นเมื่อนำไป deploy จริง จึงต้องมีการนำ View เหล่านี้ฝังเข้าไปในฐานข้อมูลในฐานะ document พิเศษภายใต้ชื่อที่ขึ้นต้นด้วย _design/ และจะเรียกว่า Permanent View

ถึงแม้ตอนนี้ CouchDB จะยังไม่สมบูรณ์ดีนัก และอาจจะมีการเปลี่ยนแปลง API ในอนาคต (จริงๆ คืออัพเดทเวอร์ชั่นเมื่อไหร่ต้องมีอะไรพัง) แต่สำหรับผมแล้ว การใช้งาน CouchDB มันทำให้การทำเว็บมันสนุกขึ้นอีกครั้งจริงๆ ขอยก quote หนึ่งของ Jacob Kaplan-Moss ที่ถูกเขียนลง Relax with CouchDB มาปิดท้าย

“Django may be built for the Web, but CouchDB is built of the Web. I’ve never seen software that so completely embraces the philosophies behind HTTP. CouchDB makes Django look old-school in the same way that Django makes ASP look outdated.”

มาใช้ CouchDB กันเถอะ!

Back