データベース

MongoDB

概要

MongoDBは、世界で最も人気のあるNoSQLドキュメントデータベースです。JSONライクなBSONドキュメント形式でデータを格納し、スケーラビリティ、パフォーマンス、柔軟性を重視して設計されています。動的スキーマとリッチクエリ言語により、現代のアプリケーション開発に最適化されています。

詳細

MongoDBは2007年に10gen(現MongoDB Inc.)によって開発されました。従来のリレーショナルデータベースの制約を解決するために、ドキュメント指向のアプローチを採用しています。JSONに似たBSON(Binary JSON)形式でデータを格納し、スキーマレスな設計により開発者の生産性向上を実現しています。

MongoDBの主な特徴:

  • ドキュメント型データベース(BSON形式)
  • 動的スキーマ
  • 水平スケーリング(シャーディング)
  • レプリケーション
  • 豊富なクエリ言語
  • インデックス機能
  • 集約フレームワーク
  • GridFS(大容量ファイル格納)
  • 地理空間データサポート
  • ACID特性(4.0以降でマルチドキュメントトランザクション)

メリット・デメリット

メリット

  • 柔軟性: スキーマレス設計で迅速な開発が可能
  • スケーラビリティ: 水平スケーリングに優れている
  • パフォーマンス: 読み取り・書き込み処理が高速
  • 開発者フレンドリー: JSONライクな直感的なデータ形式
  • 豊富な機能: 全文検索、地理空間クエリ、時系列データ処理
  • クラウド統合: MongoDB Atlasによるマネージドサービス
  • コミュニティ: 活発なコミュニティとエコシステム

デメリット

  • メモリ使用量: 大量のメモリを消費する場合がある
  • JOINの制限: リレーショナルなJOIN操作が制限される
  • データ一貫性: 結果的整合性を考慮する必要がある
  • ストレージ使用量: BSONはJSONより多くのストレージを使用
  • 学習コスト: NoSQLの概念とMongoDB特有の機能の習得が必要

主要リンク

書き方の例

インストール・セットアップ

# Ubuntu/Debian
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update
sudo apt install mongodb-org

# macOS (Homebrew)
brew tap mongodb/brew
brew install mongodb-community

# Docker
docker run --name mongodb -p 27017:27017 -d mongo

# MongoDB起動
sudo systemctl start mongod
sudo systemctl enable mongod

# MongoShell接続
mongosh

基本操作(CRUD)

// データベース使用
use myapp

// コレクション作成とドキュメント挿入(Create)
db.users.insertOne({
  name: "田中太郎",
  email: "[email protected]",
  age: 30,
  address: {
    city: "東京",
    prefecture: "東京都"
  },
  hobbies: ["読書", "映画鑑賞"],
  createdAt: new Date()
})

// 複数ドキュメント挿入
db.users.insertMany([
  {
    name: "佐藤花子",
    email: "[email protected]",
    age: 25,
    address: { city: "大阪", prefecture: "大阪府" }
  },
  {
    name: "山田次郎",
    email: "[email protected]",
    age: 35,
    address: { city: "名古屋", prefecture: "愛知県" }
  }
])

// ドキュメント読み取り(Read)
db.users.find()
db.users.findOne({ name: "田中太郎" })
db.users.find({ age: { $gte: 30 } })
db.users.find({ "address.city": "東京" })

// ドキュメント更新(Update)
db.users.updateOne(
  { name: "田中太郎" },
  { $set: { age: 31, email: "[email protected]" } }
)

db.users.updateMany(
  { age: { $lt: 30 } },
  { $set: { category: "young" } }
)

// ドキュメント削除(Delete)
db.users.deleteOne({ name: "田中太郎" })
db.users.deleteMany({ age: { $gt: 40 } })

データモデリング

// 埋め込みドキュメント
db.posts.insertOne({
  title: "MongoDB入門",
  content: "MongoDBはドキュメント型データベースです",
  author: {
    name: "田中太郎",
    email: "[email protected]"
  },
  tags: ["mongodb", "nosql", "database"],
  comments: [
    {
      author: "佐藤花子",
      text: "とても参考になりました",
      date: new Date()
    }
  ],
  createdAt: new Date()
})

// 参照(リレーション)
db.users.insertOne({
  _id: ObjectId("65a1b2c3d4e5f6789abc1234"),
  name: "田中太郎",
  email: "[email protected]"
})

db.orders.insertOne({
  userId: ObjectId("65a1b2c3d4e5f6789abc1234"),
  items: [
    { product: "ノートPC", price: 80000 },
    { product: "マウス", price: 2000 }
  ],
  total: 82000,
  orderDate: new Date()
})

インデックス・最適化

// 単一フィールドインデックス
db.users.createIndex({ email: 1 })

// 複合インデックス
db.users.createIndex({ age: 1, "address.city": 1 })

// テキストインデックス(全文検索)
db.posts.createIndex({ title: "text", content: "text" })

// 地理空間インデックス
db.places.createIndex({ location: "2dsphere" })

// インデックス確認
db.users.getIndexes()

// 実行計画確認
db.users.explain().find({ email: "[email protected]" })

// 統計情報確認
db.users.stats()

実用例

// 集約フレームワーク
db.users.aggregate([
  { $match: { age: { $gte: 25 } } },
  { $group: {
    _id: "$address.prefecture",
    count: { $sum: 1 },
    avgAge: { $avg: "$age" }
  }},
  { $sort: { count: -1 } }
])

// 全文検索
db.posts.find({ $text: { $search: "MongoDB NoSQL" } })

// 地理空間クエリ
db.places.find({
  location: {
    $near: {
      $geometry: { type: "Point", coordinates: [139.6917, 35.6895] },
      $maxDistance: 1000
    }
  }
})

// 時系列データクエリ
db.logs.find({
  timestamp: {
    $gte: ISODate("2024-01-01T00:00:00Z"),
    $lt: ISODate("2024-02-01T00:00:00Z")
  }
})

ベストプラクティス

// トランザクション(レプリカセット必要)
const session = db.getMongo().startSession()
session.startTransaction()

try {
  db.accounts.updateOne(
    { _id: "account1" },
    { $inc: { balance: -100 } },
    { session }
  )
  
  db.accounts.updateOne(
    { _id: "account2" },
    { $inc: { balance: 100 } },
    { session }
  )
  
  session.commitTransaction()
} catch (error) {
  session.abortTransaction()
  throw error
} finally {
  session.endSession()
}

// バルク操作
const bulk = db.users.initializeUnorderedBulkOp()
bulk.insert({ name: "User1", email: "[email protected]" })
bulk.find({ name: "User2" }).update({ $set: { active: true } })
bulk.find({ name: "User3" }).remove()
bulk.execute()

// データベース設定
// mongod.conf
storage:
  dbPath: /var/lib/mongodb
  journal:
    enabled: true

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

net:
  port: 27017
  bindIp: 127.0.0.1

replication:
  replSetName: "rs0"

Node.js での使用例

const { MongoClient } = require('mongodb')

// 接続
const client = new MongoClient('mongodb://localhost:27017')

async function main() {
  try {
    await client.connect()
    const db = client.db('myapp')
    const collection = db.collection('users')
    
    // データ挿入
    await collection.insertOne({
      name: '田中太郎',
      email: '[email protected]',
      age: 30
    })
    
    // データ検索
    const users = await collection.find({ age: { $gte: 25 } }).toArray()
    console.log(users)
    
  } finally {
    await client.close()
  }
}

main().catch(console.error)