ゼミ生がmongodbを利用して自作しているサービスでコレクションを連結した検索がしたいとか。
mongodb知ってはいてもなかなか使う機会はないもので。この分野は勉強しても具体的に使う機会がないと勉強した情報があっという間に陳腐化するので。必要になるまで勉強しないことが多くなりました。。。いけないことなんですけどね。
こちらのスライドとmongodbの薄い本を流し読み。
コレクションの連結や外部キーが使えないとのこと。そうか。。。そういうものなのか。
じゃあどうすればいいのか?方法は2つあるようで、ObjectIdを埋め込むかデータをまとめて入れてしまうか。
以下、ゼミ生にヒントとして送った内容です。このまま利用できるような情報にはなっていませんが。。。オレオレメモなのでご勘弁を。
彼がやりたいことは、foodsという食品名とその成分名(fi)のコレクションと、allergieという成分名とそのアレルゲン名(ma)のコレクションの2つをWebで登録してもらい、ある食品が自分が避けたいアレルゲン名を含んでいるか検査したい、ということでした。カラム名は意味不明ですが。。。ゼミ生の設計のままとしています。
- foodsとallergieは独立したドキュメントに登録する。以下では、食品1とその成分a,bを登録します。このときにアレルゲンを登録するための要素alも入れておきます。
> db.foods.insert({"name" : "1", "fi" : ["a","b"], "al" : [] });
> db.foods.find()
{ "_id" : ObjectId("54b1c966758df1528e9d3832"), "name" : "1", "fi" : [
"a", "b" ], "al" : [ ] }
- 次にアレルゲンのコレクションも。成分aにアレルゲンx,yが、成分bにはアレルゲンy,zが含まれているとした例です。
> db.allergie.insert({"name" : "a", "ma" : [ "x", "y" ] });
> db.allergie.insert({"name" : "b", "ma" : [ "y", "z" ] });
> db.allergie.find()
{ "_id" : ObjectId("54b1c9af758df1528e9d3834"), "name" : "a", "ma" : [
"x", "y" ] }
{ "_id" : ObjectId("54b1c9bf758df1528e9d3836"), "name" : "b", "ma" : [
"y", "z" ] }
※foodsを更新する場合。新規追加から更新かをドキュメント内をサーチして調べる必要があると思われます。以下は食品1の成分を上書きするコマンド。
> db.foods.update({"name" : "1")},{$set:{"fi":["b"]}});
- foodsを追加するときにallergieをサーチしてfoodsに追加する。あるいはallergieを追加更新した場合も同様。
以下、"name"が"1"である食品を成分を取得してアレルゲンを更新するまでの流れ。SQLのようにコマンド一発ではいかないので適時javascriptでプログラムを補う必要がある。。。と思います。ほんとかいな?コマンドが連結できればいいのですが。。。できるんだろうなぁ。
- 食品1の成分を取得します。
> db.foods.find({"name":"1"}, {fi:1,_id:0})
{ "fi" : [ "a", "b" ] }
- 取得した成分に対応したアレルゲンを取得します。
> db.allergie.find({"name": {$in:["a","b"]}},{ma:1,_id:0});
{ "ma" : [ "x", "y" ] }
{ "ma" : [ "y", "z" ] }
- 取得したアレルゲンをJavscriptでconcatする ["x","y"],["y","z"] => ["x","y","y","z"]
※重複があるがあとでdistinctで除去する。でも、concat時に重複をプログラムで除去してもよさげ。
- その結果をfoodsに上書きする
> db.foods.update({"name":"1"},{$set:{"al":["x","y","y","z"]}});
- で、最後にfoodsを指定したときにアレルゲンを表示する。
> db.foods.find({name:"1","al":"x"})
{ "_id" : ObjectId("54b1d98e40339dbcd356511a"), "name" : "1", "fi" : [
"a", "b" ], "al" : [ "x", "y", "y", "z" ] }
- 複数のアレルゲンの一致をorやandで検索することもできる。
> db.foods.find({$or:[{al:"x"},{al:"y"}]})
あってんのかなぁ。。。?時間があれば本を読まないと。。。