Sử dụng CouchDB

Một phần của tài liệu PHÁT TRIỂN ỨNG DỤNG WEB (Trang 33 - 50)

3.4.1. Khởi động

Mặc định câu lệnh #couchdb là khởi động couchdb.Nhƣng nó chỉ khởi động tiến trình trực tiếp, không khởi động nhƣ một ứng dụng nền vì vậy.Ta dùng cách sau

Khởi động couchdb nhƣ 1 tiến trình nền #couchdb -b

Tắt couchdb nhƣ một tiền trình nền #couchdb -d

Localhost đến Futon:http://localhost:5984/_utils/

3.4.2. Xử lý truy vấn trong CouchDB

CouchDB dùng chuẩn REST với HTTP API nên chỉ có 4 câu lệnh đặc trƣng GET, PUT, DELETE, POST.Dƣới đây là bảng so sánh công dụng tƣơng ứng:

Bảng 2:Bảng tƣơng ứng các câu lệnh truy vấn

Câu lệnh Tƣơng ứng SQL

GET SELECT

PUT CREATE hoặc UPDATE

DELETE DELETE

24

3.4.3. Views

Định nghĩa

Việc sử lý truy vấn đƣợc thực hiện qua một thành phần trong CouchDB gọi là view. Dƣới đây là một thể hiện ví dụ của một views

{

"_id": "_design/people",

"_rev": "6-2b78f1ce2c09d3d703e68ddb5fa7f9bc", "vendor": {

"timing": "var timing = {};\ntiming.YearToSeconds = function(years){\n\tvar curDate = new Date();\n\tvar startDate = new Date();\n\tstartDate.setFullYear(curDate.getFullYear() - years);\n\treturn (curDate.getTime() - startDate.getTime())/1000;\n}", "couchapp": { "metadata": { "name": "couchapp", "fetch_uri": "git://github.com/couchapp/couchapp.git", "description": "official couchapp vendor"

} } }, "language": "javascript", "views": { "ageByYear": {

"map": "function(doc) {\n\t//Next line is CouchApp directive\n\tvar timing = {};\ntiming.YearToSeconds = function(years){\n\tvar curDate = new

25

Date();\n\tstartDate.setFullYear(curDate.getFullYear() - years);\n\treturn (curDate.getTime() - startDate.getTime())/1000;\n}\n\n\t\n \temit(doc.name, {\n\t\"age\":\n\t{\n\t\t\"seconds\": timing.YearToSeconds(doc.age), \n\t\t\"age\": doc.age\n\t}\n\t\n\t});\t\n}"

},

"age": {

"map": "function(doc) {\n\temit(doc.name, doc.age); \n}",

"reduce": "function(keys, values, rereduce) {\n\treturn sum(values); \n}"

},

"recent-items": {

"map": "function(doc) {\n if (doc.created_at) {\n var p = doc.profile || {};\n emit(doc.created_at, {\n message:doc.message, \n gravatar_url: p.gravatar_url, \n nickname: p.nickname, \n name: doc.name\n });\n }\n};" } }, "lists": { }, "updates": { }, "_attachments": { "index.html": { "content_type": "text/html", "revpos": 3, "digest": "md5-lScL+LCwuZFHXvbLkTUoHA==", "length": 2307, (adsbygoogle = window.adsbygoogle || []).push({});

26 "stub": true }, "vendor/couchapp/md5.js": { "content_type": "text/x-js", "revpos": 3, "digest": "md5-p777iQdVR/HkNU2OXNRcDA==", "length": 8656, "stub": true }, } }

Muốn thực hiện truy vấn, ta chỉ cần gọi tên view với đƣờng dẫn chính xác, có thể thêm các tham số cần thiết là ta có đƣợc truy vấn SQL tƣơng ứng.Ví dụ:

#CURL http://localhost:5984/test/_design/people/_view/age

Phân loại view cố định và views tạm thời

 View cố định: Đƣợc lƣu trữ trong document đặc biệt (design view), có khả năng truy cập qua HTTP qua yêu cầu GET tới URI cố định theo định dạng sau /DBname/docID/viewName

 View tạm thời: Không đƣợc lƣu trữ trên database, nhƣng đƣợc thực thi theo tùy loại yêu cầu (temporary view).Để thực thi view này ta cần sử dụng phƣơng thức yêu cầu HTTP - POST tới URI theo định dạng /DBname/temp_view_name, nơi mà phần phân của yêu cầu chứa code của hàm view mà content-type đƣợc thiết đặt đến application/ JSON

Ví dụ về view tạm thời

#curl -X PUT -d '{"spatial":{"points":"function(doc) {\n if (doc.loc) {\n emit({\n type: \"Point\", \n coordinates: [doc.loc[0], doc.loc[1]]\n }, [doc._id, doc.loc]);\n }};"}}' http://127.0.0.1:5984/geocouch/_design/main

27

Câu lệnh trên tạo view tạm thời cho cơ sở dữ liệu là geocouch với design là main Chú ý: việc sử dụng view tạm thời chỉ hợp lý trong quá trình phát triển, vì các view này đƣợc thực thi lúc nào đƣợc gọi đến, không nhƣ view cố định, dữ liệu đã đƣợc sinh sẵn.Và thời gian để chạy Javascript làm chậm đáng kể việc truy xuất dữ liệu từ database

Các thành phần trong views

View gồm hai thành phần:Map và reduce.Có chức năng đúng nhƣ tên gọi của nó.Một hàm với chức năng gắn các văn bản với các yêu cầu, mong muốn của ngƣời phát triển với nhau.Một hàm có chắc năng nén dữ liệu

a, Map function

Ví dụ cơ bản

function(doc){ emit(null, doc); }

Hàm trên trả về toàn bộ các document trong database mà không trả lại khóa.doc ở đây ám chỉ là toàn bộ document của CouchDB.Hàm emit có chức năng sinh ra dữ liệu với 2 tham số đầu vào là key và values.Giá trị value có thể là một phần tử hoặc là một đối tƣợng.

Dƣới đây là một ví dụ chi tiết hơn:

function(doc) {

emit(doc.name, doc.age); }

Hàm này trả về tất cả các giá trị trƣờng age của bản ghi nào có trong cơ sở dữ liệu doc, với khóa là trƣờng name

b, Reduce function

Đây là một hàm hữu ích của CouchDB.Nó làm tăng hiệu năng rất lớn cho CouchDB.Nó có chức năng thu gọn các kết quả đƣợc trả về từ hàm map hoặc từ chính các hàm reduce khác.

28

Nếu view có sử dụng hàm reduce.Nó có thể sử dụng việc kết hợp các kết quả cho view đó.Hàm reduce chấp nhận các tập nhƣ đầu vào, các kết quả đã đƣợc trả về qua hàm emit

3.4.6. Futon

CouchDB cũng cung cấp một ứng dụng web application tên là Futon, là một công cụ quản trị nhƣ phpmyadmin của MySQL, phpMoadmin của mongoDB.Nó cho phép bạn bảo trì cơ sở dữ liệu, kiểm soát.

Mặc định CouchDB chiếm cổng 5984 và chạy tại địa chỉ http://127.0.0.1:5984/_utils/

29

Hình 5: Tạo temporary view

3.5. Tìm hiểu phần mở rộng GEO của COUCHDB (adsbygoogle = window.adsbygoogle || []).push({});

3.5.1. GeoCouch là gì

GeoCouch là một nhánh của Couchdb và đƣợc viết bởi Volker Mische.Dự án

GeoCouch đƣợc lƣu ở github tại địa chỉ

https://github.com/couchbase/geocouch.GeoCouch tồn tại dƣới dạng một phần mở rộng của Couchbase(là một cơ sở dữ liệu kết hợp giữa CouchDB và Membase), nhƣng vẫn tƣơng thích tuyệt đối cho các phiên bản couchdb của Apache.

Ngoài việc hỗ trợ trợ tất cả tính năng của CouchDB, thì GeoCouch còn hỗ trợ thêm một cách đánh chỉ mục mới, R-tree.Cấu trúc R-tree đƣợc sử dụng bởi rất nhiều cho việc lƣu trữ dữ liệu không gian địa lý bởi vì nó đƣợc tối ƣu tốc độ cho việc tìm kiếm.R-tree đơn giản là tập các ô hoặc mảnh khung hoặc có thể gọi là khoảng không gian.Nó bao gồm các điểm ánh xạ tới các điểm thực sự trong khoảng đó hoặc giữa các khoảng nhỏ hơn với nhau đƣợc bao gồm trong các khoảng lớn hơn.Nó giảm đƣợc số mảnh phải truy vấn, bởi

30

vì các tập dữ liệu thƣờng đƣợc giới hạn thành các tập nhỏ hơn trong các các điểm ánh xạ đƣợc lƣu trữ.GeoCouch cung cấp một cách dễ dàng nhất để xây dựng các ứng dụng liên quan đến không gian một cách đơn giản bao gồm hỗ trợ định dạng vật lý GeoJSON trong CouchDB view.

3.5.2. Cài đặt GeoCouch

Mặc định GeoCouch chỉ là một phần mở rộng, tức là nếu muốn dùng phải phải cài đặt riêng.

Chúng ta sẽ tải bản geocouch từ github tại địa chỉ sau https://github.com/couchbase/geocouch/ .Tại dây tùy vào phiên bản CouchDB hiện tại ta dùng là phiên bản nào mà ta sẽ chọn nhánh tƣơng ứng đến phiên bản đó.Ở đây trong ví dụ hiện tại ta đang dùng phiên bản CouchDB 1.1.1.Do đó ta sẽ dùng phiên bản sau.

https://github.com/couchbase/geocouch/tree/couchdb1.1.x Tải GeoCouch về máy.

#git clone https://github.com/couchbase/geocouch.git #cd geocouch

Chọn nhánh đúng với phiên bản của CouchDB mình đang dùng

#git checkout couchdb1.1.x

Tiếp theo ta sẽ tạo biến môi trƣờng CouchDB để biên dịch GeoCouch

#export COUCH_SRC=/path/to/source/src/couchdb

Ở đây sẽ lấy ví dụ ở máy hiện tại thì câu lệnh đúng là:

#export COUCH_SRC=/usr/lib64/couchdb/erlang/lib/couch-1.1.1/include/

Sau đó ta chạy lệnh make

#make

Sau khi biên dịch xong thì ta sẽ tiến hành copy file cấu hình từ trong thƣ mục geocouch sang thƣ mục cấu hình của couchdb

#cp etc/couchdb/local.d/geocouch.ini /etc/couchdb/local.d/

Để kiểm tra xem phần cài đặt đã thành công chƣa.Ta sẽ copy các file test vào futon:

31 Tạo file test và add nội dung

#vi /usr/local/share/couchdb/www/script/couch_tests.js #loadTest("spatial.js"); loadTest("list_spatial.js"); loadTest("etags_spatial.js"); loadTest("multiple_spatial_rows.js"); loadTest("spatial_compaction.js"); loadTest("spatial_design_docs.js"); loadTest("spatial_bugfixes.js");

Việc cuối cùng để có thể tiến hành chạy GeoCouch là xuất biến chạy cho nó(lƣu ý.nên xuất biến này cùng với khởi động hệ điều hành, không có biến này, CouchDB và GeoCouch sẽ gặp lỗi)

#export ERL_FLAGS="-pa <geocouch>/build"

#export ERL_FLAGS="-pa /home/tuandung/KLTN/Data-KLTN/geocouch/build/"

3.5.3. Kết nối NodeJs và GeoCouch

Nhƣ đã nói ở chƣơng trƣớc.Để có thể kết nối đƣợc NodeJS với CouchDB thì ta phải dùng một module có tên là Cradle.Tuy nhiên, mặc định cradle không hỗ trợ kiểu views spatial.Ta phải dùng một nhánh khác của Cradle do thompson viết, có đầy đủ có chức năng của module Cradle và ngoài ra có thêm việc hỗ trợ GeoCouch. (adsbygoogle = window.adsbygoogle || []).push({});

Địa chỉ download tại đây: https://github.com/dthompson/cradle và chọn nhánh thứ 2.

#git clone https://github.com/dthompson/cradle.git

#cd cradle

#git checkout add_spatial_support

Mặc định trong thƣ mục down về chỉ có code mà chƣa có các module đi kèm.Để tải dùng lệnh sau

#npm install -d

3.5.4. Sử dụng GeoCouch

32

#curl -X PUT http://127.0.0.1:5984/geocouch

Tiếp theo là nhập dữ liệu.ví dụ về dữ liệu tại địa chỉ: https://github.com/dthompson/example_geocouch_data Tải thƣ mục về và chạy file import.js dữ liệu.

Vậy là ta đã có cơ sở dữ liệu và dữ liệu để test.Bây giờ là tạo một design document.Có hai cách để tạo.Một là tạo trực tiếp, cách hai là tạo bằng futon.

Cách một

Sau khi chạy xong thì ta sẽ tạo ra 1 request để kiểm tra.

#curl -X PUT -d '{"spatial":{"points":"function(doc) {\n if (doc.loc) {\n emit({\n type: \"Point\", \n coordinates: [doc.loc[0], doc.loc[1]]\n }, [doc._id, doc.loc]);\n }};"}}'

http://127.0.0.1:5984/geocouch/_design/main

Khá dài dòng và không thân thiện, nhƣng chỉ với một câu lệnh thì ta đã thành công trong một bƣớc tạo design.Trong câu lệnh này ta đã add cả hàm map vào trực tiếp vào câu lệnh.Mặc định spatial view không có hỗ trợ hàm reduce, chỉ có hàm map

Cách hai

Dùng couchapp.Ta tạo thêm thƣ mục spatial.Trong thƣ mục đó tạo 1 file tên là point.js.Ở đây tên file chính là design document.Và nội dung trong file đó chính là hàm map tƣơng ứng với câu lệnh ở cách 1.Cuối cùng là ta chỉ cần push lên là xong.

#couchapp push geocouch

Ta có thể thấy, dùng cách hai đơn giản hơn nhiều cách 1. Cuối cùng là ta sẽ tạo ra một request để truy vấn.

#curl -X GET 'http://localhost:5984/geocouch/_design/main/_spatial/points?bbox=0, 0, 180, 90'

33

Chƣơng 4 ỨNG DỤNG MAPCHAT

Trong chƣơng này tôi sẽ làm một ứng dụng cho phép ngƣời dùng tƣơng tác với nhau bằng các đoạn chat thời gian thực trong khi vẫn gắn vị trí hiện thời của họ trên bản đồ.Ứng dụng đó có tên là mapchat.Cách vận hành và bản chất của chƣơng trình này giống với trang www.mapchat.im .Ứng dụng sẽ sử dụng Google Maps để hiển thị bản đồ và đánh dấu vị trí ngƣời dùng.NodeJS chạy các ứng dụng thời gian thực hoặc theo sự kiện rất tốt,trong khi CouchDB thì có nhiệm vụ lƣu lịch sử các đoạn chat.

4.1. ExpressJS

ExpressJs là một framework trong NodeJS với việc hỗ trợ khá nhiều tiện ích cho nodeJS

Lệnh cài đặt :

#npm install express

Để khởi tạo một http server với ExpressJS ta chỉ cần thêm vài dòng code sau :

var express = require('express') ; var app = express.createServer(); app.get('/', routes.index); (adsbygoogle = window.adsbygoogle || []).push({});

app.listen(3000);

Đoạn mã trên nói nói rằng khi vào trang chủ (tức /) thì sẽ chuyển đến file index.js và đƣợc lắng nghe trên cổng 3000.

Việc tiếp theo là ta sẽ cấu hình server để chuẩn bị cho chƣơng trình.Ta sẽ đặt cho views,tức các file để dành cho việc hiển thị đƣợc đặt trong thƣ mục /views với kiểu file là ejs.Các file trình bày CSS đƣợc dùng theo định dạng stylus, tức là có thể dùng đƣợc theo 2 dạng abc.stylus hoặc abc.css.Và cuối cùng là các file static,các file chứa js tĩnh đƣa về phía trình duyệt, các file ảnh hay file css sẽ đƣợc lƣu trong thƣ mục /public.Dƣới đây là đoạn mã thực hiện :

app.configure(function(){

app.set('views', __dirname + '/views'); app.set('view engine', 'ejs');

34 app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(require('stylus').middleware({ src: __dirname + '/public' })); app.use(app.router); app.use(express.static(__dirname + '/public')); }); 4.2. SocketIO

Socket.io là một module trong NodeJS.Nó cung cấp một tập các thƣ viện (API) cho các ứng dụng để điểu kiển các thông điệp giữa client và server.Nó tự động sử dụng kiểu kết nối tốt nhất có thể đƣợc để liên lạc giữa client và server.Các kết nối đó bao gồm websockets, flash proxy,XHR-long và một vài cách kết nối khác.Nó cũng bao gồm các thƣ việc con khác với chức năng phụ trợ cho nó ví dụ nhƣ now.js để đồng bộ thời gian giữa client và server.Socket.io tự nó tạo ra các chuẩn dữ liệu để chuyển qua lại dữ liệu giữa nhiều ngôn ngữ khác nhau

Để cài đặt Socket.io ta có thể dùng trình quản lý gói của Nodejs - NPM :

#npm install socket.io

Để khai báo module và khởi tạo server thì đầu tiên server phải khởi động một sự kiện lắng nghe đƣợc quản lý bởi socket.io thay vì http nhƣ thƣờng lệ

var io = require('socket.io); var socket = io.listen(app);

Module socket.io hoạt động theo đúng mô hình socket client-server tức là việc giữa 2 bên tạo ra các sự kiện và tiến hành bắt sự kiện khi có hành động tƣơng ứng.Dƣới đây là code ví dụ về phía server

socket.on('connection',function(client){ socket.send({message : "abc"}); });

35

4.3. Cài đặt project 4.3.1. Phần Chatting

Phía server có 2 sự kiện nhận:

register : Ngƣời dùng sẽ đăng kí tên đến server và server sẽ lƣu tên ngƣời dùng.

message: Khi có thông điệp (message) từ client đến,server sẽ nhận thông điệp,gán tên ngƣời dùng đã gửi và sau đó gửi lan truyền cho tất cả số ngƣời đang chat đều nhận đƣợc thông điệp.

Đây là đoạn mã :

client.on('message',function(data){ //phan chatting

var username = 'noname';

console.log("User " + client.id + " send " + data); client.get('username', function (err, name) {

console.log('Chat message by ', name); console.log('error ', err); username = name; }); client.broadcast.emit('updateStatus',{ message : data, user: username }); });

//server nhan duoc ten nguoi dung vao chat client.on('register',function(name){

36

client.set('username', name); })

Phía client thì có một sự kiện:

Nếu server gửi dữ liệu broadcast về client thì nó sẽ updateStatus vào log chat Chi tiết đoạn mã

function updateStatus(user,message){ var status = $('#status'); var result = ' ' + user + ' : '+ message + '

'; status.append(result); } (adsbygoogle = window.adsbygoogle || []).push({});

socket.on('updateStatus',function(data){updateStatus(data.user,data.message);})

4.3.2 Phần map

Ứng dụng của chúng ta dùng HTML5 để định vị vị trí ngƣời dùng.Mặc định khi vào trang web thì client sẽ load toàn bộ phần bản đồ và thiết đặt vị trí ngƣời dùng.

Để tự động tải khi load trang ta thêm vào thẻ body nhƣ sau :

<body onload="loadMap();">

Ứng dụng của chúng ta sẽ định vị bằng GPS.Nếu trình duyệt hoăc thiết bị có hỗ trợ dùng HTML5 để xác định vị trí ngƣời dùng thì sẽ trả về vị trí ngƣời dùng và sau đó tạo marker vị trí ngƣời dùng đó.Chi tiết hàm loadMap:

function loadMap(){

//Neu trinh duyet ho tro dinh vi vi tri nguoi dung

if(navigator.geolocation) { navigator.geolocation.getCurrentPosition(getPosition); }

//neu khong,tra ve vi tri mac dinh else{

var myLatlng = new google.maps.LatLng(40.334, -103.644); var yourOption = {

37

zoom : 8,

center : myLatlng,

mapTypeId : google.maps.MapTypeId.ROADMAP };

map = new google.maps.Map(document.getElementById("map"),yourOption); }

}

Hàm getPosition thực hiện việc sử lý sau khi đã nhận tham số truyền vào là tọa độ điểm vị trí của ngƣời dùng :

function getPosition(position){ var lat = position.coords.latitude, lng = position.coords.longitude;

var yourPoint = new google.maps.LatLng(lat, lng ), yourOption = { zoom: 10,

center: yourPoint,

mapTypeId: google.maps.MapTypeId.ROADMAP }; //tao ban do mapPos = document.getElementById("map"); map = new google.maps.Map(mapPos,yourOption); //danh dau vi tri cua ban

marker = new google.maps.Marker({ position: yourPoint, map: map, title: "You are here" });

}

38

#node app.js

Sau khi đăng nhập trình duyệt theo địa chỉ http://localhost:3000 thì ta có kết quả nhƣ sau

Hình 4: Ứng dụng mapchat

39

KẾT LUẬN (adsbygoogle = window.adsbygoogle || []).push({});

Kết quả đạt đƣợc của khóa luận

Khóa luận đã đƣa ra một cái nhìn tổng quát về xu hƣớng webserver, ngôn ngữ và cơ sở dữ liệu trong tƣơng lai gần.Các bài benmark kết quả đã cho thấy đƣợc hiệu quả rõ ràng so với các giải pháp cũ.Với webserver thì ta có thêm một cách tiếp cận mới so với giải pháp hƣớng luồng cũ.Với cơ sở dữ liệu thì ta có một cách lƣu trữ và tổ chức dữ liệu mới đạt hiệu quả cao mà không thông qua các câu truy vấn cũ.

Định hƣớng nghiên cứu tiếp theo

Hiện tại tôi mới tìm hiểu sơ qua về các khái niệm, đặc điểm của các giải pháp.Chƣa đi sâu về nghiên cứu việc triển khai nó với các bài toán lớn, cụ thể.

Trong giai đoạn tiếp theo tôi sẽ nghiên cứu thêm các ứng dụng nữa của NodeJS đáp ứng đƣợc tốt hơn các giải pháp cũ ngoài các ứng dụng thời gian thực nhƣ mapchat.Còn về NoSQL sẽ nghiên cứu thêm các cơ sở dữ liệu khác nhƣ MongoDB hay Cassandra.Các hệ NoSQL cách lƣu trữ khác theo cách lƣu trữ theo văn bản (document storage) nhƣ Key value, column oriented nhƣ C-store.

Một phần của tài liệu PHÁT TRIỂN ỨNG DỤNG WEB (Trang 33 - 50)