BSON
ライブラリ
BSON
概要
BSON (Binary JSON)は、JSONのバイナリエンコード形式で、MongoDBの主要なデータ保存・転送フォーマットとして開発されました。JSONの利便性を保ちながら、バイナリ形式による高速なパース・トラバーサル、拡張データ型のサポート(日付、バイナリデータ、ObjectId等)を提供します。JavaScript/TypeScript環境では、MongoDBドライバーと統合されており、データベース操作において透過的に使用されます。
詳細
BSONは、JSONの表現力を拡張しつつ、効率的なデータ保存と処理を実現するために設計されました。各要素に長さ情報を含むことで、データ全体をパースすることなく特定の要素にアクセスできます。MongoDBでは内部的にすべてのドキュメントをBSON形式で保存し、インデックス作成やクエリ処理を最適化しています。標準的なJSONでは表現できない、日付、正規表現、バイナリデータなどの型をネイティブサポートすることで、実用的なアプリケーション開発を支援します。
主な特徴
- 拡張データ型: ObjectId、Date、Binary、Decimal128など12種類以上の型をサポート
- 効率的なトラバーサル: 長さプレフィックスによる高速なデータアクセス
- MongoDB統合: MongoDBの標準データフォーマットとして完全対応
- 言語中立性: 多言語対応の公式ドライバーを提供
- スキーマレス: JSONと同様の柔軟なデータ構造
- バイナリ安全: バイナリデータの直接保存が可能
メリット・デメリット
メリット
- 日付やバイナリデータなど拡張型のネイティブサポート
- JSONより高速なパース・シリアライゼーション処理
- MongoDBとのシームレスな統合
- 一意のObjectId生成機能による分散システム対応
- ネストされた構造の効率的なクエリ処理
- 型情報を保持したまま保存・復元が可能
デメリット
- JSONよりファイルサイズが大きくなる傾向
- 人間が直接読み書きできないバイナリ形式
- MongoDB以外での採用例が限定的
- テキストエディタでの編集・デバッグが困難
- 学習コストがJSONより高い
- 一部のプログラミング言語でのサポートが限定的
参考ページ
書き方の例
基本的な使用方法
// BSONライブラリのインポート
import { BSON, ObjectId } from 'bson';
// BSONドキュメントの作成
const document = {
_id: new ObjectId(),
name: '田中太郎',
age: 30,
createdAt: new Date(),
isActive: true,
scores: [85, 90, 78],
profile: {
bio: 'ソフトウェアエンジニア',
avatar: Buffer.from('バイナリデータ')
}
};
// BSONへのシリアライズ
const bsonData = BSON.serialize(document);
console.log('BSON size:', bsonData.length, 'bytes');
// BSONからのデシリアライズ
const parsedDoc = BSON.deserialize(bsonData);
console.log('Parsed:', parsedDoc);
Extended JSON (EJSON) の使用
import { EJSON } from 'bson';
// 拡張型を含むドキュメント
const doc = {
_id: new ObjectId(),
price: BSON.Decimal128.fromString('99.99'),
pattern: /^test$/i,
binary: new BSON.Binary(Buffer.from('hello')),
timestamp: new Date()
};
// EJSONへの変換(文字列形式で拡張型を表現)
const ejsonString = EJSON.stringify(doc, { relaxed: false });
console.log('EJSON:', ejsonString);
// EJSONからの復元
const restored = EJSON.parse(ejsonString);
console.log('Restored types:', {
isObjectId: restored._id instanceof ObjectId,
isDecimal: restored.price instanceof BSON.Decimal128,
isRegExp: restored.pattern instanceof RegExp
});
MongoDBとの統合使用
import { MongoClient, ObjectId } from 'mongodb';
async function mongoExample() {
const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('myapp');
const users = db.collection('users');
// BSONの型を活用したドキュメント挿入
const result = await users.insertOne({
_id: new ObjectId(),
name: '山田花子',
birthDate: new Date('1990-05-15'),
tags: ['developer', 'tokyo'],
metadata: new BSON.Binary(Buffer.from('メタデータ')),
lastLogin: new Date()
});
// ObjectIdを使用したクエリ
const user = await users.findOne({
_id: new ObjectId(result.insertedId)
});
// 日付型を使用した範囲クエリ
const recentUsers = await users.find({
lastLogin: { $gte: new Date(Date.now() - 24*60*60*1000) }
}).toArray();
await client.close();
}
カスタム型の処理
import { BSON } from 'bson';
// カスタムクラス
class Location {
constructor(lat, lng) {
this.latitude = lat;
this.longitude = lng;
}
toBSON() {
return {
type: 'Point',
coordinates: [this.longitude, this.latitude]
};
}
static fromBSON(doc) {
if (doc.type === 'Point') {
return new Location(doc.coordinates[1], doc.coordinates[0]);
}
return doc;
}
}
// 使用例
const location = new Location(35.6762, 139.6503); // 東京
const bson = BSON.serialize({ location });
const parsed = BSON.deserialize(bson);
console.log('Location:', parsed.location);
TypeScriptでの型安全な使用
import { ObjectId, Decimal128 } from 'bson';
// 型定義
interface Product {
_id: ObjectId;
name: string;
price: Decimal128;
categories: string[];
inStock: boolean;
createdAt: Date;
image?: Buffer;
}
// 型安全なBSON操作
function createProduct(data: Omit<Product, '_id'>): Product {
return {
_id: new ObjectId(),
...data
};
}
const product = createProduct({
name: 'TypeScript入門',
price: Decimal128.fromString('2980'),
categories: ['書籍', 'プログラミング'],
inStock: true,
createdAt: new Date()
});
// シリアライズ/デシリアライズ
const bsonData = BSON.serialize(product);
const restored = BSON.deserialize(bsonData) as Product;
// 型チェックが有効
console.log(restored.price.toString()); // "2980"
バイナリデータの処理
import { BSON } from 'bson';
import fs from 'fs';
// 画像ファイルの保存
async function saveImageToBSON(imagePath, outputPath) {
const imageBuffer = await fs.promises.readFile(imagePath);
const document = {
filename: imagePath.split('/').pop(),
contentType: 'image/jpeg',
data: new BSON.Binary(imageBuffer),
uploadedAt: new Date(),
size: imageBuffer.length
};
const bsonData = BSON.serialize(document);
await fs.promises.writeFile(outputPath, bsonData);
return bsonData.length;
}
// 画像の復元
async function loadImageFromBSON(bsonPath) {
const bsonData = await fs.promises.readFile(bsonPath);
const document = BSON.deserialize(bsonData);
// バイナリデータをBufferとして取得
const imageBuffer = document.data.buffer;
return {
filename: document.filename,
contentType: document.contentType,
data: imageBuffer,
size: document.size
};
}