2010/02/13に札幌で開催された[LOCAL DEVELOPER DAY '10 /Winter:title=http://www.local.or.jp/?LDD/LDD10Winter](LDD'10Winter)で、MongoDBの話をしてきました。
今回はMongoDBについてを一通り話すつもりだったのですが、一通りすぎて全然時間が足りませんでした。ごめんなさい。
あと、既存の概念ではないものを説明するのは難しいです。喋りながら全然伝わった感じがありませんでした。MapReduceとか捨ててそっちの説明に重点を置いたほうがいい気がしました。
MongoDBに関しては、機会があればまた話したいと思います。
LOCAL DEVELOPER DAY '10 /Winterが開催されます
2/13に、札幌でLOCAL DEVELOPER DAY '10 /Winter(LDD)が開催されます。
日時 | 2010/2/13(土) 12:45〜18:35 |
会場 | 札幌市産業振興センター セミナールームA |
参加費 | 無料 |
懇親会 | http://atnd.org/events/2843(2/5締切) |
公式タグ | ldd10winter |
公式ハッシュタグ | #ldd10w |
詳細 | http://www.local.or.jp/?LDD%2FLDD10Winter |
LT発表者も募集中です!道内の技術者の人は是非参加してください!
ちなみに去年はこんな感じでした。
PHPでMongoDBを使ってみる その2
http://www.nyphp.org/PHP-Presentations/145_Recovering-Mongos-MongoDB-PHP
これが参考になる
=以外の条件
<?php $mongo = new Mongo(); $col = $mongo->selectDB( "foo" )->selectCollection( "bar" ); $data = array("name" => "foo", "num" => 1); $col->insert( $data ); $data = array("name" => "bar", "num" => 2); $col->insert( $data ); $data = array("name" => "baz", "num" => 3); $col->insert( $data ); $cursor = $col->find(array('num' => array('$gt' => 1))); foreach ($cursor as $id =>$obj) { var_dump( $obj ); }
$gtとか$ltとかで条件指定するらしい、""で囲むとPHPの変数扱いされるので''で。
ちなみにこの$はphp.iniのmongo.cmdで変更できる。
$gt | > |
$lt | |
$gte | >= |
$lte | <= |
$ne | != |
$in | SQLのINと同じ |
$nin | NOT IN |
$mod | array(x, y)と指定した場合、対象をxで割った余りが1かどうか |
$all | 集合を全て含むかどうか |
$size | 要素の数 |
$exists | フィールドが存在するかどうか(true/false) |
文字列の部分一致
正規表現が使える
<?php //このへんまで上のと同じ $cursor = $col->find(array('name' => new MongoRegex('/ba/i'))); foreach ($cursor as $id =>$obj) { var_dump( $obj ); }
MongoRegexオブジェクトを作らないといけないのがちょっと面倒(言語によってはそのままいけるらしい)
SQLっぽい条件指定
SQLインジェクションの恐怖再び
$cursor = $col->find(array('$where' => 'this.num > 1'));
this.何とかで、自分の値を取れるので、this.x + this.y > 1とかもできる。
JavaScript条件
MongoDBはJavaScriptエンジンを内蔵しているので、
$cursor = $col->find(array('$where' => 'this.num > Math.abs(-1)'));
こういうことができる。
単にJavaScriptを実行してほしいだけなら
<?php $mongo = new Mongo(); $db = $mongo->selectDB( "foo" ); $code = new MongoCode("function() { return 'hello'; }"); $ret = $db->execute($code); var_dump($ret);
これで、array('retval' => 'hello', 'ok' => 1)が返ってくる。
JSに対してPHPから引数を与えたい場合は、
<?php $mongo = new Mongo(); $db = $mongo->selectDB( "foo" ); $code = new MongoCode("function() { return x + 1; }", array("x" => 1)); $ret = $db->execute($code); var_dump($ret);
深い階層の条件指定
<?php $mongo = new Mongo(); $col = $mongo->selectDB( "foo" )->selectCollection( "bar" ); $data = array("name" => "foo", "data" => array('a' => 1, 'b' => 10)); $col->insert( $data ); $data = array("name" => "bar", "data" => array('a' => 2, 'b' => 20)); $col->insert( $data ); $data = array("name" => "baz", "data" => array('a' => 3, 'b' => 30)); $col->insert( $data ); $cursor = $col->find(array('data.a' => 1)); foreach ($cursor as $id =>$obj) { var_dump( $obj ); }
ドットで繋ぐ。
ページング的な処理
LIMITとOFFSET的な(上の資料からコピペ)
$page_num= 3; $results_per_page= 10; $cursor = $results->find() ->sort(array("ts" => -1)) ->skip($page_num * $results_per_page) ->limit($results_per_page);
skipがoffset、limitはlimitで。
おまけ
MongoDBをO/Rマッパ的に使うライブラリMorph
http://code.google.com/p/mongodb-morph/
なんかメリット殺してるような気がしなくも…。でも楽かもしれない
PHPでMongoDBを使ってみる
5ヶ月ぶりの更新とかどういうことなの…
MongoDBって何だ
MongoDBのインストール
バイナリが配布されているのでそれを使う
http://www.mongodb.org/
自分のプラットフォーム用バイナリを落として、/opt/mongoに入れると仮定。変なプラットフォームの人はgithubから拾ってくる。
あとデータフォルダがデフォルトで/data/dbとかという変なパスなので作る
sudo mkdir -p /data/db
起動
普通に実行してもいいけど
http://gist.github.com/232227
このへんにinitスクリプトがあるので拾ってきてパスを書き換える
使い方
ここから本編
というかチュートリアルがあるからそれを参考にする
http://www.mongodb.org/display/DOCS/PHP+Tutorial
DBに接続
$mongo = new Mongo();
外部に接続する場合は
$mongo = new Mongo("example.com:65432");
データベースを取得
RDBのデータベースと同じ単位だと思われるデータベースへの接続ハンドラ(?)を取得
$db = $mongo->selectDB( "dbname" );
あ、CREATE DATABASE的なものはいらないです。
コレクションを取得
RDBのテーブルと同じ単位だと思われるコレクションへの接続ハンドラ(?)を取得
ハンドラというか何て呼ぶんだこれは
$col = $db->selectCollection( "collectionname" );
あ、CREATE TABLE的なものはいらないです。
コレクションにデータを入れる
いわゆるINSERT
テーブル定義なんてものはないよ!自由に入れれるよ!
$doc = array( "name" => "MongoDB", "type" => "database", "count" => 1, "info" => (object)array( "x" => 203, "y" => 102), "versions" => array("0.9.7", "0.9.8", "0.9.9") ); $col->insert( $doc );
こんな感じで、自由に定義した配列を入れれる。勝手に拡張もできる。
ちなみにinsertすると、内部ID保持用に_idというキーが自動的に$docに入る。あとでUPDATEとかに使う。
1項目取得
findOneというそのままな
$obj = $col->findOne();
var_dump( $obj );
条件指定する場合は
$obj = $col->findOne(array("name"=>"MongoDB")); var_dump( $obj );
ない場合はnullを返す
件数取得
$col->count();
複数項目取得
findでできる
$cursor = $col->find(); foreach ($cursor as $id =>$obj) { var_dump( $obj ); }
findが返すのはカーソルなので、foreachで回して処理する。もちろんfindに条件を指定することもできる。
ちなみに
$cursor->count();
で、問い合わせ結果の件数を取得できる。
ORDER BY的なことをしたい場合は、
$cursor->sort(array('name'=>1));
とかする。-1にすると逆順になる。この「1で昇順、-1で降順」は他でも出てくる。
インデックスの作成
$col->ensureIndex( array( "name" => 1 ) );
というわけで出てきました。
複合インデックスの場合は
$col->ensureIndex( array( "name" => 1 , "count" => -1 ) );
みたいに。
第二引数をTRUEにするとunique制約になる。
データの更新
$col->update(array("name" => "MongoDB"), $newobj);
第三引数にTRUEを指定すると、対象が見つからない場合にINSERTしてくれる。
取得してきたものをそのまま更新したい場合は
$obj = $col->findOne(); $obj["name"] = "nazo"; $col->save($obj);
削除
DBの削除
$db->drop();
Collectionの削除
$col->drop();
条件指定でデータを削除
$col->remove(array("name" => "MongoDB"));
条件指定でデータを削除(1つだけ)
$col->remove(array("name" => "MongoDB"), true);
findしたものを削除
$obj = $col->findOne(); $col->remove($obj);
詳しいAPIドキュメント
なんと公式にあるよ!まあPECLだから当たり前?だけど!
http://jp.php.net/mongo
twitterの発言の勢いを表示するUserScript
アドオンとかで、その人がどれだけ喋ってるかとか、いわゆる「勢い」がどれぐらいか視覚的に分かるものをフォロワ一覧もしくはそれぞれのHomeとかでざっと見たりとかできたりしたら便利そうだなあ。アイコンをよくしゃべってる人から順に赤-橙-黄-緑-青-紫-黒ってborderつけたり
https://twitter.com/waka_/status/2322401195
それグリモンでできるんじゃね?ということで作ってみた!
http://nazone.info/gm/timelinepower.user.js
インストールして、各ユーザーのHomeを開くと、名前の上のほうにカラーバーが表示されます。
赤いほうが最近活発に発言しています。
follow前に「この人最近twitter使ってるのかな…」と確認する時にでもお使い下さい。
既知の不具合
- たまにBASIC認証が出ます
rhaco2を使う
rev4574だよ!1ヶ月後くらいには別物になってるかもしれないから気をつけてね!
参考:http://www.sharkpp.net/php/rhaco/how-to-use-rhaco2.html
初期設定
- __settings__.phpを作る
<?php require_once("/path/to/rhaco2/rhaco2"); // rhaco2のRhaco.phpを読み込む application_settings(__FILE__, 'http://localhost');
application_settingsはRhaco::initのエイリアス
initは
static public function init($path,$url=null,$lib=null,$work=null){
ということで、
- 自分のパス
- このアプリケーションのURL
- 他
を指定する。
とりあえず何か出す
- index.php
<?php include_once(dirname(__FILE__)."/__settings__.php"); import('core.Flow'); $flow = new Flow(); $flow->output('index.html');
- templates/index.html
<html> <body> hoge </body> </html>
url handlerを使う
- index.php
<?php include_once(dirname(__FILE__)."/__settings__.php"); import('core.Flow'); $flow = new Flow(); $flow->handler(array( ""=>"class=BlogView,method=index,template=index.html,name=blog", )); $flow->output();
$flow->handlerで、URLと処理対象のマッピングを書く
Cakeとかみたいに書いてある通りに動くと思うな!このゆとりが!全部マッピング書け!
いや多分Cakeみたいに指定箇所に置けば動くようにもできると思うけど
keyのURLマッピング(ここでは空なので全部)にマッチしたら、classのmethodをController的に呼び出して(MTVと考えればViewか)、index.htmlを表示する。nameは識別用としてしか使われてない?後で使う?
- lib/BlogView.php
<?php class BlogView extends Flow { public function index() { $this->vars('hoge', 'fuga'); return $this; } }
普通のControllerっぽいクラス。Flowを継承する。
$this->varsでテンプレート変数を割り当てる。
- templates/index.html
<html> <body> {$hoge} </body> </html>
{$hoge}でアサインした変数の出力
DB使いたい!
- テーブル作成
create table blog ( id int not null auto_increment primary key, name varchar(255), title varchar(255), body text, created datetime, updated timestamp ); insert into blog(name, title, body, created) values('nazo', 'title1', 'body1', now()); insert into blog(name, title, body, created) values('nazo', 'title2', 'body2', now()); insert into blog(name, title, body, created) values('nazo', 'title3', 'body3', now());
- lib/model/Blog.php
<?php import('db.Dao'); class Blog extends Dao { static protected $__id__ = "type=serial"; static protected $__name__ = "type=string"; static protected $__title__ = "type=string"; static protected $__body__ = "type=string"; static protected $__created__ = "type=timestamp"; static protected $__updated__ = "type=timestamp"; protected $id; protected $name; protected $title; protected $body; protected $created; protected $updated; protected function __init__(){ $this->created = time(); $this->updated = time(); } }
※型訂正
- __settings__.php
<?php require_once("/path/to/rhaco2/rhaco2"); application_settings(__FILE__,"http://localhost"); def("db.Db@app","type=mysql,dbname=rhaco2test,host=localhost,user=root,password=");
- lib/BlogView.php
<?php import('model.Blog'); class BlogView extends Flow { public function index() { $this->vars('entries', C('Blog')->find_all()); return $this; } }
- templates/index.html
<html> <body> <ul rt:param="entries" rt:var="entry" rt:reverse="true"> <li><span>{$t.html($entry.name())}</span><span> [{$entry.fmCreated()}]</span></li> <li><span>{$t.html($entry.title())}</span></li> <li><span>{$t.html($entry.body())}</span></li> </ul> </body> </html>