2014年11月25日 星期二

iOS 開發筆記 - UIWebview loadData 以及 local resource 配置

很久以前把玩過 PhoneGap 後,就很久沒使用 UIWebview 來當作主要 UI。這次嘗試使用 Web UI 當作操作介面,於是乎就碰到使用 jQuery 的問題 XD 偷懶的解法就是用 remote resource,更佳的解法是用 local file。

使用方式:

// 記得要 Added folders 要採用 Create folder references
// web-resource/index.html
// web-resource/jquery.min.js
//
NSString *localWebPagePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"web-resource"];

[self.webview loadData:[NSData dataWithContentsOfFile:localWebPagePath] MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:localWebPagePath]];


其中的 baseURL 只要填寫正確,在 index.html 裡頭就可以用相對路徑存取 local file 了。

例如:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="jquery.min.js"></script>
</head>
<body>
...
</body>
</html>

[PHP] CodeIgniter 與 Facebook PHP SDK v4 整合筆記 @ Ubuntu 14.04

整個流程並主要是把 Facebook PHP sdk v4 source code 下載到 ci_proj/application/libraries 中,接著撰寫一隻 ci library: facebook.php 來橋接一下,就差不多可以收工了。在此謹驗證收到的 facebook token 是否有效,其他部分並不實作:

$ cd /path/ci_proj/application/libraries
$ git clone https://github.com/facebook/facebook-php-sdk-v4.git
$ ln -s facebook-php-sdk-v4 facebook


建立 Facebook 相關 config:

$ vim /path/ci_proj/application/config/facebook.php
<?php defined('BASEPATH') OR exit('No direct script access allowed');
$config['facebook']['api_id'] = 'FACEBOOK APP ID';
$config['facebook']['app_secret'] = 'FACEBOOK APP SECRET KEY';


編輯橋接的 library 用法:

$ vim /path/ci_proj/application/libraries/facebook.php

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require_once APPPATH . 'libraries/facebook/autoload.php';

use Facebook\FacebookRedirectLoginHelper;
use Facebook\FacebookSession;
use Facebook\FacebookRequest;
use Facebook\GraphUser;
use Facebook\FacebookRequestException;

class Facebook {
var $ci;
var $helper;
var $session;
var $permissions;

public function __construct() {
$this->ci =& get_instance();
$this->ci->load->config('facebook');
$this->permissions = $this->ci->config->item('permissions', 'facebook');
FacebookSession::setDefaultApplication( $this->ci->config->item('api_id', 'facebook'), $this->ci->config->item('app_secret', 'facebook') );
        }
public function token_validation($token) {
try {
$this->session = new FacebookSession( $token );
if ( $this->session->validate() )
return true;
} catch ( Exception $e ) {
}
$this->session = null;
return false;
}
public function get_user($token = NULL) {
if ( $this->session || (!empty($token) && $this->token_validation($token)) ) {
$request = ( new FacebookRequest( $this->session, 'GET', '/me' ) )->execute();
$user = $request->getGraphObject()->asArray();
return $user;
}
return false;
}
}


使用方式:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Test extends CI_Controller {
public function index()
{
$this->load->config('facebook');
$this->load->library('facebook');
$output = array(
'reqeusts' => $_REQUEST,
'app_id' => $this->config->item('api_id', 'facebook'),
'user_id' => $this->facebook->get_user($_REQUEST['token'])
);
$this->output->set_content_type('application/json')->set_output(json_encode($output));
        }
}

[PHP] 使用 CodeIgniter 和 codeigniter-restserver 建立 RESTful API 以及測試方式 @ Ubuntu 14.04

環境資訊:

  • Ubuntu 14.04 64 Bit (lsb_release -a)
  • PHP 5.5.9 (php -v)
  • CodeIgniter 2.2.0 (echo CI_VERSION)

接著,透過 github.com/chriskacerguis/codeigniter-restserver 擴充 CodeIgniter:

$ wget https://raw.githubusercontent.com/chriskacerguis/codeigniter-restserver/master/application/config/rest.php -O /path/ci_proj/application/config/rest.php

$ wget https://raw.githubusercontent.com/chriskacerguis/codeigniter-restserver/master/application/libraries/Format.php -O /path/ci_proj/application/libraries/Format.php

$ wget https://raw.githubusercontent.com/chriskacerguis/codeigniter-restserver/master/application/libraries/REST_Controller.php -O /path/ci_proj/application/libraries/REST_Controller.php


接著,請確認 application/config/rest.php 設定:

$config[rest_default_format] = 'json'; // 預設為 xml

之後,就可以撰寫簡單的 RESTful API 啦:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
require(APPPATH.'/libraries/REST_Controller.php');

class Item extends REST_Controller {
        public function index_get() {
                $this->list_get();
        }
        public function index_post() {
                $this->add_post();
        }
        public function index_put() {
                $this->update_put();
        }
        public function index_delete() {
                $this->delete_delte();
        }

        public function list_get() {
                echo "list";
        }
        public function add_post() {
                echo "create";
        }
        public function update_put() {
                echo "put";
        }
        public function remove_delete() {
                echo "remove";
        }
}


測試方式:

取得清單(GET)

$ curl -X GET http://localhost/ci_proj/item/list
$ curl -X GET http://localhost/ci_proj/item/

新增資料(POST)

$ curl -X POST http://localhost/ci_proj/item/add
$ curl -X POST http://localhost/ci_proj/item/


更新資料(PUT)

$ curl -X PUT http://localhost/ci_proj/item/update
$ curl -X PUT http://localhost/ci_proj/item/


刪除資料(DELETE)

$ curl -X DELETE http://localhost/ci_proj/item/delete
$ curl -X DELETE http://localhost/ci_proj/item/


也可以用 wget --method GET/POST/PUT/DELETE 等進行測試。

至於 RESTful 常見的 HTTP STATUS CODE,可以參考 Using HTTP Methods for RESTful Services

例如:

取得清單成功回應 200 OK,失敗則為 404 NOT FOUND;新增資料成功 200 OK 或是 201 LOCATION 到新增項目的 URI,失敗為 404 NOT FOUND;更新資料成功為 200 OK,失敗為 404 NOT FOUND;刪除資料成功為 200 OK,失敗為 404 NOT FOUND。

接著,是設計每個 GET, POST, PUT, DELETE API 細節,可以分別使用 $this->get('param')、$this->post('param')、$this->put('param') 和 $this->delete('param') 取得對應資料,而回應時,需要的 HTTP Status Code 可以使用:

$this->response( array('status' => true), 200);
$this->response( array('status' => false, 'error' => 'error message'), 400);
$this->response( array('status' => false, 'error' => 'error message'), 404);


若要 debug 的話,大概可以用:

$this->response( array('status' => true, 'get' => $this->get(), 'post' => $this->post(), 'put' => $this->put(), 'delete' => $this->delete() ), 200);

搭配 curl -i -X GET|POST|PUT|DELETE -d 'key=value' 方式進行,除了可以看到 HTTP Response code 外,也能看到 get, post, put, delete 各種參數資訊:

$ curl -i -X DELETE -d "haha=hehe" CGI
HTTP/1.1 200 OK
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.5
Content-Length: 68
Content-Type: application/json; charset=utf-8

{"status":true,"get":[],"post":[],"put":[],"delete":{"haha":"hehe"}}

2014年11月20日 星期四

[Linux] Docker 使用筆記 - 透過 Supervisor 開機執行多項服務(SSH, Apache Web Server, MySQL) @ Ubuntu 14.04

前陣子把玩 Docker 時,粗略知道運作是著重落在一隻 process 上,在 Dockerfile 中雖然可以執行多個 CMD,但擺上多隻會共同運行指令仍是無效的。解法的原理就是執行一隻程式,由那支程式管理其他程式。在 Docker 官方文件上就是使用 Supervisor 啦:Using Supervisor with Docker

Dockefile:

FROM ubuntu:14.04
RUN apt-get update
RUN sudo -E "DEBIAN_FRONTEND=noninteractive" apt-get install -y telnet curl mysql-server-5.6 apache2 php5 php5-mysql openssh-server supervisor
RUN mkdir -p /var/lock/apache2 /var/run/apache2 /var/run/mysqld /var/run/sshd /var/log/supervisor

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

RUN mkdir -p /root/.ssh/ && chmod 700 /root/.ssh/
COPY id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 600 /root/.ssh/authorized_keys

EXPOSE 22 80 3306
CMD ["/usr/bin/supervisord"]


supervisord.conf:

[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"

[program:mysql]
command=/usr/bin/pidproxy /var/run/mysqld/mysqld.pid /usr/sbin/mysqld
autorestart=true


id_rsa / id_rsa.pub:

$ ssh-keygen -t rsa -f id_rsa -P''

上述檔案都在同一層目錄中,分別有 Dockefile, supervisord.conf, id_rsa 和 id_rsa.pub ,共四個檔案。

接著建立 Image:

$ cd /path/dir
$ sudo docker build -t dev .


運行,將本機端的 10022, 10080, 13306 分別對應到 Docker Container 的 22, 80, 3306:

$ sudo docker run -t -p 10022:22 -p 10080:80 -p 13306:3306 dev
/usr/lib/python2.7/dist-packages/supervisor/options.py:295: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
  'Supervisord is running as root and it is searching '
CRIT Supervisor running as root (no user in config file)
WARN Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
INFO RPC interface 'supervisor' initialized
CRIT Server 'unix_http_server' running without any HTTP authentication checking
INFO supervisord started with pid 1
INFO spawned: 'sshd' with pid 9
INFO spawned: 'mysql' with pid 10
INFO spawned: 'apache2' with pid 11
INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
INFO success: mysql entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
...


也可以按 ctrl+c 離開,但 VM 還會繼續跑

遠端登入:

$ ssh -i /path/dir/id_rsa root@localhost:10022

若有需求想要 console 端登入:

$ sudo docker ps -a
$ sudo docker inspect --format '{{.State.Pid}}' CONTAINER_ID
$ sudo nsenter --target PID_NUMBER --mount --uts --ipc --net --pid


收工!

2014年11月13日 星期四

[SQL] 從 Table 中取出 Column 1 取資料並塞進 Column 2 方法 @ MySQL 5.6

有一張 table 儲存 field1, field2 為原始資料,由於資料尚未驗證,想要透過 REGEXP 驗證資料結果,這時就想到每次找都要驗證一次有點煩,所以就乾脆建立 field3,將驗證過的資料儲存起來。

至於解法方面,就單純把撈出來的資料再 INSERT 回去 :P 假設有一張 Table 名為 mTable,其中 key 為 field1, field2,而 field3 預設可以為空,目標則是將 field1 驗證過的資料儲存在 field3,未來使用時就可以透過 field3 來當作資料驗證。

新增資料用法:

INSERT INTO (field1, field2) VALUES ... ;

驗證 field1 資料,把正確格式擺進 field3:

INSERT mTable (field1, field2, field3)

SELECT field1, field2, field1 AS field3 FROM mTable WHERE field3 IS NULL AND field1 REGEXP '^[0-9A-Za-z]+$'

ON DUPLICATE KEY UPDATE

field3 = VALUES(field3)


如此一來,就完成驗證 :P

接著應該也可以嘗試 MySQL trigger 用法?不過,驗證資料這件事應該不要叫 DB 做才對 XD 只是...環境所限啊。

2014年11月12日 星期三

[Linux] 使用 awk, sort, uniq, grep, dig 批次驗證 email address @ Ubuntu 14.04

工作時中,偶爾會對 UNIX 系統感動! 雖然有時很簡單的任務會龜毛到硬要連續指令來處理,明明寫一小段 code 就好了啊 Orz

取處已知清單中的 email domain:

$ mysql -e "select email from db.user;" > email.log;
$ sort email.log | uniq | awk -F'@' '{print $2}' | sort | uniq | grep "." > email.domain.log


最後的 grep "." 則是要避開 hostname 不合法的項目(如 localhost 等)。

驗證 mx:

$ dig -t mx test.com +short | wc -l
$ test `dig -t mx test.com +short | wc -l` -ne 0 && echo "ok"


批次驗證:

$ awk '{ system("test `dig -t mx "$1" +short | wc -l` -ne 0 && echo "$1)}' email.domain.log > email.validated.log

2014年11月11日 星期二

[Linux] c++: internal compiler error: Killed (program cc1plus) @ Ubuntu 14.04

最近 編譯 C++ 的 source code 時,開始會蹦出奇怪的狀態:

c++: internal compiler error: Killed (program cc1plus)
Please submit a full bug report,
with preprocessed source if appropriate.

See <file:///usr/share/doc/gcc-4.8/README.Bugs> for instructions.


一開始碰過一兩次後,重開機真的又沒事,但些會開始又常碰到,接著猜想是不是 memory 不夠了,除了用 free -m 觀察外,也把一些 daemon 關掉,真的猜中了 :P

大概是因為我租的機器只有 512MB RAM 啦 Orz

2014年11月8日 星期六

[Linux] 在 UTF8 Terminal 環境下,找尋 Big5 檔案內的關鍵字 @ Ubuntu 14.04

最近想要研究一些老檔案,都是 Big5 編碼,接著就想著到底該怎樣叫 grep 幫你查資料,因為 pattern 要用 Big5 編碼,但 Terminal 是 UTF-8 環境。

我想到的是先把 pattern 轉成 big5 後,透過 xxd 轉乘 hex code,然後一樣將 input 透過 xxd 輸出後用 grep 去找,結果堪用,但不是很方便。

問一下高手,高手獻技:

$ grep -r `echo -ne "大學" | piconv -f utf8 -t big5` target_files

還差一點,因為 Terminal 環境編碼關係,解法:

$ LC_ALL=C grep -r `echo -ne "大學" | piconv -f utf8 -t big5` target_files

此時 Terminal 輸出會亂掉,所以再把 output 從 Big5 轉成 UTF-8:

$ LC_ALL=C grep -r `echo -ne "大學" | piconv -f utf8 -t big5` target_files | piconv -f big5 -t utf8

若想要有 grep highlight ,那就再叫 grep 做一次吧 XD

$ LC_ALL=C grep -r `echo -ne "大學" | piconv -f utf8 -t big5` target_files | piconv -f big5 -t utf8 | grep "大學"

收工!

2014年11月5日 星期三

[Linux] MySQL Server 5.6 更換 DB Location @ Ubuntu 14.04

在 AWS EC2 叫機器出來時,大多都會用 EBS 來擴充空間,對於 mysql server 預設裝在 /var/lib/mysql 時,就容易碰到空間不足的時候。解法:手動更換 mysql db location 吧 :P

順便把 EBS 處理過程也都列一列,假設已經新增並掛載 EBS 空間:

$ sudo lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
...
xvdb    202:16   0     4G  0 disk /mnt
xvdc    202:32   0   500G  0 disk

$ sudo file -s /dev/xvdc
$ sudo mkfs -t ext3 /dev/xvdc
$ sudo mkdir /data
$ sudo mount -t ext3 /dev/xvdc /data
$ sudo chmod 777 /da
$ sudo vim /etc/fstab
/dev/xvdc /data ext3 defaults,nofail 0 2


處理 MySQL Server:

$ sudo apt-get install mysql-server-5.6
$ sudo service mysql stop
$ sudo mv /var/lib/mysql /data/mysql
$ sudo ln -s /data/mysql /var/lib/mysql
$ sudo vim /etc/apparmor.d/usr.sbin.mysqld
  /var/lib/mysql/ r,
  /var/lib/mysql/** rwk,
  /var/log/mysql/ r,
  /var/log/mysql/* rw,

  /data/mysql r,
  /data/mysql** rwk,
  /data/mysql r,
  /data/mysql* rwk,
$ sudo service apparmor restart
$ sudo service mysql start

2014年11月3日 星期一

[Linux] 架設 MongoDB Sharded Cluster 與驗證資料分散性 @ Ubuntu 14.04

今年年初時,把玩過 MongoDB Replica Set ,也試過一些 Map-Reduce 計算等,最近終於又開始抽空實驗了 :P 當時看到不少 Sharded Cluster 搭建在 MongoDB Replica Set 身上,深感 process (server) 的數量不低,就遲遲沒跳下去玩,最近才發現就算只是單一個 MongoDB Server 也可以當作 shared Cluster 一員,就方便啦!

此次 MongoDB Sharded Cluster 架構:
  • 跑兩個 MongoDB Server,分別在 port 10001, 10002
  • 跑一個 MongoDB Config Server - port 20001
  • 跑一個 MongoDB Routing Server - port 30001
以下操作全部都在一台 ubuntu 14.04 server:

$ rm -rf /tmp/mongo && mkdir -p /tmp/mongo/db/1 /tmp/mongo/db/2 /tmp/mongo/db/c /tmp/mongo/log

$ mongod --port 10001 --dbpath /tmp/mongo/db/1 --logpath /tmp/mongo/log/1 &
$ mongod --port 10002 --dbpath /tmp/mongo/db/2 --logpath /tmp/mongo/log/2 &
$ mongod --port 20001 --configsvr --dbpath /tmp/mongo/db/c --logpath /tmp/mongo/log/c &

$ mongos --port 30001 --configdb 127.0.0.1:20001 &


設定 partition 用法:

$ mongo --port 30001
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("###############")
}
  shards:
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
mongos> sh.addShard('127.0.0.1:10001')
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard('127.0.0.1:10002')
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("#####################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
mongos> sh.enableSharding('ydb')
{ "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("######################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
        {  "_id" : "ydb",  "partitioned" : true,  "primary" : "shard0000" }
mongos> use ydb
switched to db ydb
mongos> db.ycollection.ensureIndex( { _id : "hashed" } )
{
        "raw" : {
                "127.0.0.1:10001" : {
                        "createdCollectionAutomatically" : true,
                        "numIndexesBefore" : 1,
                        "numIndexesAfter" : 2,
                        "ok" : 1
                }
        },
        "ok" : 1
}
mongos> sh.shardCollection("ydb.ycollection", { "_id": "hashed" } )
{ "collectionsharded" : "ydb.ycollection", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
  sharding version: {
        "_id" : 1,
        "version" : 4,
        "minCompatibleVersion" : 4,
        "currentVersion" : 5,
        "clusterId" : ObjectId("#####################")
}
  shards:
        {  "_id" : "shard0000",  "host" : "127.0.0.1:10001" }
        {  "_id" : "shard0001",  "host" : "127.0.0.1:10002" }
  databases:
        {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
        {  "_id" : "test",  "partitioned" : false,  "primary" : "shard0000" }
        {  "_id" : "ydb",  "partitioned" : true,  "primary" : "shard0000" }
                ydb.ycollection
                        shard key: { "_id" : "hashed" }
                        chunks:
                                shard0000       2
                                shard0001       2
                        { "_id" : { "$minKey" : 1 } } -->> { "_id" : NumberLong("-4611686018427387902") } on : shard0000 Timestamp(2, 2)
                        { "_id" : NumberLong("-4611686018427387902") } -->> { "_id" : NumberLong(0) } on : shard0000 Timestamp(2, 3)
                        { "_id" : NumberLong(0) } -->> { "_id" : NumberLong("4611686018427387902") } on : shard0001 Timestamp(2, 4)
                        { "_id" : NumberLong("4611686018427387902") } -->> { "_id" : { "$maxKey" : 1 } } on : shard0001 Timestamp(2, 5)


簡易驗證資料分散性:

mongos>

db.ycollection.insert({user:"a", item: "1", rate: 0.3})
db.ycollection.insert({user:"a", item: "3", rate: 0.5})
db.ycollection.insert({user:"a", item: "4", rate: 0.9})
db.ycollection.insert({user:"b", item: "2", rate: 0.1})
db.ycollection.insert({user:"b", item: "3", rate: 0.6})
db.ycollection.insert({user:"b", item: "5", rate: 0.2})
db.ycollection.insert({user:"c", item: "3", rate: 0.2})
db.ycollection.insert({user:"c", item: "4", rate: 0.7})

mongos> db.ycollection.find()
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "3", "rate" : 0.5 }
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "1", "rate" : 0.3 }
{ "_id" : ObjectId("#################"), "user" : "a", "item" : "4", "rate" : 0.9 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "2", "rate" : 0.1 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "5", "rate" : 0.2 }
{ "_id" : ObjectId("#################"), "user" : "b", "item" : "3", "rate" : 0.6 }
{ "_id" : ObjectId("#################"), "user" : "c", "item" : "3", "rate" : 0.2 }
{ "_id" : ObjectId("#################"), "user" : "c", "item" : "4", "rate" : 0.7 }


接著,想要驗證資料的確是儲存在不同台 mongodb server ,作法就是把資料透過 mongodump 出來(bson),再用 bsondump 印出來驗證:

$ cd /tmp
$ mongodump --port 10001 -d ydb -c ycollection
$ bsondump dump/ydb/ycollection.bson
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "1", "rate" : 0.3 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "2", "rate" : 0.1 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "3", "rate" : 0.6 }
{ "_id" : ObjectId( "#############" ), "user" : "c", "item" : "4", "rate" : 0.7 }
4 objects found

$ mongodump --port 10002 -d ydb -c ycollection
$ bsondump dump/ydb/ycollection.bson
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "3", "rate" : 0.5 }
{ "_id" : ObjectId( "#############" ), "user" : "a", "item" : "4", "rate" : 0.9 }
{ "_id" : ObjectId( "#############" ), "user" : "b", "item" : "5", "rate" : 0.2 }
{ "_id" : ObjectId( "#############" ), "user" : "c", "item" : "3", "rate" : 0.2 }
4 objects found


因此可完成驗證,資料的確分在不同區!有興趣還可以用 MongoDB Aggregate (Map-Reduce) 實作共現矩陣(Co-Occurrence Matrix)及簡易的推薦系統 來驗證 partition 演算法的正確性。