BSONとMapオブジェクト
JavaScript, MongoDBMap
こういうの
newMap.ts
const foo = new Map([
['bar', 'baz'],
['hoge', 'fuga'],
])
console.log(foo.get('hoge')) // fuga
JSON と Map
JSON は Map を許容しないので、そのままでは入らない。
newMap.ts
const foo = new Map([
['bar', 'baz'],
['hoge', 'fuga'],
])
console.log(
JSON.stringify({
mapObject: foo,
}),
)
// {"mapObject":{}}
ただ、Map は iterable なので、Array.from()
やスプレッド構文で配列にするやり方がある。
ネストされた Map は Map のままなので入らない、注意
newMap.ts
const foo = new Map([
['bar', 'baz'],
['hoge', 'fuga'],
])
console.log(
JSON.stringify({
mapObject: Array.from(foo),
}),
)
// {"mapObject":[["bar","baz"],["hoge","fuga"]]}
BSON と Map
BSON ではどうなってしまうの。
まず、MongoDB Node.js Driver の挙動を確認。
mongo.ts
import mongodb from 'mongodb'
const MongoClient = mongodb.MongoClient
const URL = 'mongodb://127.0.0.1:27017/myDB'
const DB_NAME = 'mydb'
const COLLECTION_NAME = 'foo'
const client = new MongoClient(URL, {
useNewUrlParser: true,
})
const connect = async () => {
try {
await client.connect()
const collection = client.db(DB_NAME).collection(COLLECTION_NAME)
await collection.insertOne({
mapObject: new Map([['bar', 'baz']]),
})
// { _id: 5e9bc479294b2ea5713b8ac8, mapObject: { bar: 'baz' } }
const itemList = await collection.find().toArray()
console.log(itemList)
} catch (error) {
console.log(error.stack)
}
client.close()
}
connect()
無慈悲にも object に変換される。
MongoDB Node.js Driver が使用する BSON parser が Map.prototype.entries()
を使って Iterate し、シリアライズした値を 1 つずつ入れるのでこうなってしまう。
ついでに、Map は色々な値を key にできるので、やってみた。
mongo.ts
// 略
await collection.insertOne({
mapObject: new Map([
[undefined, 'baz'],
[NaN, 'fuga'],
]),
})
// 略
TypeError: argument must be a string
が出て Insert に失敗した。
BSON parser が文字列以外の key を buf.write()
に渡してしまうので、エラー。
困ること
Map オブジェクトではなくなるので、クライアントが Map を知りすぎているとつらい。
例えば、以下の getPersonList
は Map.prototype.entries()
を使用している。
person.ts
export type PersonList = Map<string, number>
export const getPersonList = (personList: PersonList) => {
const array = []
for (const [key, value] of personList.entries()) {
array.push({ key, value })
}
return array
}
次に、personList を持った hoge コミュニティのデータを insert する。
この時点では getPersonList(personList)
の結果は問題なく表示される。
mongo.ts
const personList: PersonList = new Map()
personList.set('taro', 18)
personList.set('jiro', 26)
personList.set('goro', 56)
console.log(getPersonList(personList))
/*
[
{ name: 'taro', age: 18 },
{ name: 'jiro', age: 26 },
{ name: 'goro', age: 56 }
]
*/
await collection.insertOne({ community: 'hoge', personList })
ところが、DB には object が入るので、getPersonList(personList)
の中で result.personList.entries()
という存在しない関数を実行しようとして、エラーが発生する。
mongo.ts
const result = await collection.findOne({ community: 'hoge' })
console.log(getPersonList(result.personList)) // result.personList.entries() がない
ちょっと辛いけど Array.from
などを使って、配列で保存すれば対処はできる。
取り出す時には new Map()
する。
mongo.ts
// 略
await collection.insertOne({
community: 'hoge',
personList: Array.from(personList), // Array.from
})
const result = await collection.findOne({ community: 'hoge' })
console.log(getPersonList(new Map(result.personList)))
/*
[
{ name: 'taro', age: 18 },
{ name: 'jiro', age: 26 },
{ name: 'goro', age: 56 }
]
*/
// 略
これで一応の挿入順は保証される。
ネストされた Map は Map のままなので object になる、注意。
終わり
Map 難しい