https://www.cuspy.org/diary/2012-04-17/the-little-mongodb-book-ja.pdf
with 6.0.3
演習用データ
data
  db.unicorns.insert({name: 'Horny', dob: new Date(1992,2,13,7,47), loves: ['carrot','papaya'], weight: 600,gender: 'm', vampires: 63});
  db.unicorns.insert({name: 'Aurora', dob: new Date(1991, 0, 24, 13, 0), loves: ['carrot', 'grape'], weight: 450, gender: 'f', vampires: 43});
  db.unicorns.insert({name: 'Unicrom', dob: new Date(1973, 1, 9, 22, 10), loves: ['energon', 'redbull'], weight: 984, gender: 'm', vampires: 182});
  db.unicorns.insert({name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], weight: 575, gender: 'm', vampires: 99});
  db.unicorns.insert({name: 'Solnara', dob: new Date(1985, 6, 4, 2, 1), loves:['apple', 'carrot', 'chocolate'], weight:550, gender:'f', vampires:80});
  db.unicorns.insert({name:'Ayna', dob: new Date(1998, 2, 7, 8, 30), loves: ['strawberry', 'lemon'], weight: 733, gender: 'f', vampires: 40});
  db.unicorns.insert({name:'Kenny', dob: new Date(1997, 6, 1, 10, 42), loves: ['grape', 'lemon'], weight: 690, gender: 'm', vampires: 39});
  db.unicorns.insert({name: 'Raleigh', dob: new Date(2005, 4, 3, 0, 57), loves: ['apple', 'sugar'], weight: 421, gender: 'm', vampires: 2});
  db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), loves: ['apple', 'watermelon'], weight: 601, gender: 'f', vampires: 33});
  db.unicorns.insert({name: 'Pilot', dob: new Date(1997, 2, 1, 5, 3), loves: ['apple', 'watermelon'], weight: 650, gender: 'm', vampires: 54});
  db.unicorns.insert({name: 'Nimue', dob: new Date(1999, 11, 20, 16, 15), loves: ['grape', 'carrot'], weight: 540, gender: 'f'});
  db.unicorns.insert({name: 'Dunx', dob: new Date(1976, 6, 18, 18, 18), loves: ['grape', 'watermelon'], weight: 704, gender: 'm', vampires: 165});
database
collection
document
field
find
db.unicorns.find({gender: 'm', weight: {$gt: 700}})
db.unicorns.find({loves: 'watermelon'})
https://www.mongodb.com/docs/manual/reference/operator/query/#query-selectors
表示フィールドのフィルタ(projection)
db.unicorns.find({loves: 'watermelon'}, {name: 1, _id: 0})
sort
db.unicorns.find().sort({name: 1, vampires: -1})
1が昇順で、-1が降順
paging
db.unicorns.find().sort({weight: -1}).limit(2).skip(1)
2番目と3番目に重たいユニコーン
上位2件を出しているが、skipで1番目を省いてるので2,3番目🐰
count
db.unicorns.find({vampires: {$gt: 50}}).count()
update
updateコマンド使えない
https://stackoverflow.com/questions/38883285/error-the-update-operation-document-must-contain-atomic-operators-when-running
replaceOne
db.unicorns.replaceOne({name: 'Roooooodles'}, {weight: 590})
でもこれは危ない。ドキュメント全体を第二引数で置き換えることになる
db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})
更新演算子 $set
フィールドに対して作用するので、ドキュメントが消えるおそれはない
更新演算子
$set
db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})
$push
db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})
$inc
db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})
https://www.mongodb.com/docs/manual/reference/operator/update/#update-operators
upsert
なければinsert、あればupdate
❌db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}});
🔴db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert:true});
updateのmulti(複数同時更新)
db.unicorns.update({}, {$set: {vaccinated: true }});
これだと最初にマッチした1documentしか更新されない
db.unicorns.update({}, {$set: {vaccinated: true }}, {multi:true});
multi trueでall document
集約
sqlのgroup_byみたいなやつ
シェル内ではパイプライン演算子を配列で渡す aggregate ヘルパー関数があります
多分気がついていると思いますが_id フィールドには gender でなく$gender を渡しています。フィールド名の前の$はドキュメントのフィールド値に置き換えられます。
これは SQL の GROUP BY と似たようなもので、_id で指定したフィールドでグループ化 (ここでは gender) とその他フィールドの集約結果 (ここでは特定の性別にマッチするドキュメントの合計数) で新しいドキュメントを生成します。
cmd
  db.unicorns.aggregate([{$group:{_id:'$gender',
                                  total: {$sum:1}}}])

  db.unicorns.aggregate([{$match: {weight:{$lt:600}}},
                         {$group: {_id:'$gender', total:{$sum:1},
                                   avgVamp:{$avg:'$vampires'}}},
                         {$sort:{avgVamp:-1}} ])

  db.unicorns.aggregate([{$unwind:'$loves'},
                         {$group: {_id:'$loves', total:{$sum:1},
                                   unicorns:{$addToSet:'$name'}}},
                         {$sort:{total:-1}},
                         {$limit:1} ])
何もわからん🐰
まずsqlの集約を覚えてない
呪文が多すぎてワーキングメモリ足らない
とりあえず、
1: 前加工。matchで絞り込んだり、unwindで配列を平らにしたり。
2: 集約。grouping。指定フィールドで集約するのと、集約されたモンをどう加工するか($sum、$avg etc)
3: 後加工。sortで並べ替えたり、limitで頭だけ取ったり。
{$sum:1} 1?
なんか掛けてるみたいで、2にしたら合計値の2倍が返ってくる
MapReduce
mapreduceってなんだっけ?
MapReduce は 2 段階のステップに分かれています。
最初に map を行い、次に reduce を行います。
mapping の段階で入力されたドキュメントを変換し、key=>value のペアを出力します (キーと値は複合できます)。
次に key/value ペアを key 毎にグループ化します。reduce の段階で出力されたキーと値の配列から最終的な結果を生成します。
map とreduce 関数は JavaScript で記述します。
使い方
mapReduce の引数には map 関数と reduce 関数、そして出力ディレクティブを引き渡します。
mongodb のシェルでは JavaScript の関数を定義して引数に指定します。
多くのライブラリでは関数を文字列で渡してやります (ちょっとカッコ悪いけど)。
3 番目の引数にはドキュメント解析するためのフィルターやソート、リミットなどの追加のオプションを指定します。さらに、reduce 段階の後に評価される finalize メソッドを指定することも出来ます。
とはいえ、通常使うことはないだろうとのこと
---
データモデリング
mongoにはjoinが無い
代案1 フィールドにドキュメントを入れる
RDBではフィールドに1つのスカラー値しか入れられないので、テーブルがどうしても分かれる
mongoではフィールドにドキュメントも入れられるので、必要な値がドキュメント一箇所に集まる
あとはfindで見つければいい
クエリ時に結合するのではなく、データ側で最初からまとめて入れておけるって話?🐰
代案2 非正規化
RDBでは必要な情報はjoinする(外部キーの値を使って別テーブルから取ってきて結合する)
mongoでは最初からドキュメントに入れておくことができる
たとえばuseridとusernameは、普通はテーブルを分けるが、username使うから(userid, username)にしちゃうか、みたいなこともできる
ただしこの場合、usernameの修正が入ったら、フィールドの値全部も再修正が要る
使い分けのヒント
ドキュメントサイズは16MBまで
たいていは親と一緒にほしい小さなデータを埋めることになるはず
埋め込みドキュメントは頻繁に利用されますが、ほとんどの場合親ドキュメントと同時に取得したい小さなデータです。
ユーザー情報に住所{street: "229 W. 43rd St", city: "New York", state:"NY",zip:"10036"}を埋め込む例が載ってる🐰
RDBのテーブルの多くは、mongoのコレクションで置き換えることができる
mongoのコレクションは、おそらくRDBのテーブルよりは少なくて済む
ドキュメントを埋め込みつつ、別にコレクションをつくるというハイブリッドもできる
posts コレクションと comments コレクションを別々に持つべきか、postドキュメントにコメントの配列を埋め込むべきでしょうか?
MongoDB の柔軟なスキーマはこの 2 つのアプローチを組み合わせて、別々のコレクションに分けたまま、少ない数のコメントを投稿に埋め込む事が出来ます。これは 1 回のクエリーで欲しいデータをまとめて取得するという原則に従っています。
コメントが10以下ならpostsに埋め込んで、10超えたらcommentsに移そう、みたいなことができるわけか🐰
なるほど
下手にテーブル(コレクション)分けるのが正義ではない、と
どんなときにmongodbを利用するか
動的スキーマに頼りたいとき
私にとって動的スキーマの本当の利点はセットアップの省略とオブジェクト指向プログラミングとの摩擦の低減です。これは、静的型付け言語を利用している場合に特に当てはまります。
jsonでそのまま送るだけでいいという親和性もある
正確にはbsonだがほぼ同じ
ロギング
非同期で書き込み後すぐに戻ってくるので速い
耐久性を制御できる(何台書き込んだらokとみなすかをいじれるらしい)
Cappedコレクション
サイズ固定したコレクションで、超過分は切り捨てる
サイズではなくドキュメント数でも制限できるらしい
ttlも指定できる
並列処理
巨大なデータの並列処理はmongoだけでは扱えない
Hadoopなどと連携する必要はあるだろう、とのこと
トランザクション
mongoにはトランザクションが無い
1 アトミック操作($incみたいなやつ)
2 二層コミット?
よくわからん
---
Transactions — MongoDB Manual
ドキュメント単位ならアトミック
ひとつのドキュメントに対する操作はアトミック (原子的) です。複数のドキュメントやコレクションにまたがって正規化するのではなく、 埋め込みドキュメントや配列を使ってデータ間の関係をひとつのドキュメント構造にまとめることができるからです。
Transaction APIなるものもあるらしい
コールバックの仕組みもあって、リトライやエラー処理もできるみたいだね🐰
分散トランザクション
分散トランザクションとは、シャードされたクラスタやレプリカセットでの複数ドキュメントのトランザクションのことです。
よくわからんが内部的に複数ドキュメント全部書き込んだ or notを担保してくれるみたい
読み込み時はロックはかからず、たとえばABCのうちAだけ書き込みできてるなら(BCの書き込み完了を待たずに)Aから読み込んでくれるみたい
全文検索
配列と全文検索をサポートしている
よほど強力なモンが要るなら専用エンジン必要だが、通常は?デフォで事足りるらしい?
耐障害性
1台でもジャーナリングがあるので、クラッシュしても大丈夫
n台の場合、自動シャーディング
シャーディングはデータを複数のサーバーやクラスターに分割してスケーラビリティを高める手法です。
単純な実装ではデータの名前が A~M で始まるものをサーバー 1 に、残りをサーバー 2 に格納するでしょう。有り難いことに、MongoDB のシャーディング能力はその単純なアルゴリズムを上回ります。
1レプリカの限界超えるときに思い出してあげて
単一レプリカセットのデータが限界まで増えた時、あなたはシャーディングの存在を思い出して利用することを検討してください。
レプリケーション
3台以上が理想
awsのdocumentdbはそうだっけか🐰
1-primary N-secondary
セカンダリはリードレプリカで、非同期的に同期される
セカンダリからの読み込みは許可・禁止を指定できる
プライマリが死んだら、セカンダリ1台が昇格する
インデックス
ensureIndex
dropIndex
DBのインデックスって何?
explain()
かかった時間などを教えてくれる
db.stats()
データベースの統計情報
サイズとか
db.unicorns.stats()
コレクションの統計情報
プロファイラ
有効化して、
db.setProfilingLevel(2);
何か処理して、
プロファイラをチェック
db.system.profile.find()
バックアップとリストア
mongodump
ローカルホストに繋いで全データを dump/ にバックアップ
mongorestore
リストア
mongoexport
jsonとかcsvで出せる
ああそうか、デフォはbsonだっけ🐰
が、全部出せるわけではないのでdumpが良いとのこと
---
Links From <- tilscb
Links To -> MapReduce DBのインデックスって何?