MongoServerError: $or/$and/$nor entries need to be full objects の対策(node.js)
本来ならこのエラーが出ないようにMongodbに渡すオブジェクトを作るのが正しいと思います。 しかし、そうすると複雑になり過ぎてしまう場合は、オブジェクトを作った後でオブジェクトを作り変えることで対策するのも一つの方法だと思います。
今回Mongodbに渡すオブジェクトを、このエラーを出さないものに作り替える関数を書きましたので、紹介します。
その関数というものがこちらです。
function deleteEmptyArraysAndObjects(v) {
const isObject = obj => obj && obj.constructor === Object;
if (isObject(v) || Array.isArray(v)) {
for (let key in v) {
deleteEmptyArraysAndObjects(v[key]);
if (Array.isArray(v[key])) {
v[key] = v[key].filter(e => e !== null && e !== undefined);
if (v[key].length === 0) {
delete v[key];
}
} else if (isObject(v[key]) && Object.keys(v[key]).length === 0) {
delete v[key];
}
}
}
};
使い方は次の通り。
let query={};
//queryに$andや$orなどを追加していく
deleteEmptyArraysAndObjects(filter)
const res=await db.collection("hoge").findOne(query);//findOne以外でも動作します
//...
Mongodbに渡す前にdeleteEmptyArraysAndObjects関数を通すことでこのエラーを回避できます。
注意: この関数は渡されたオブジェクトを変更します。
仕組み
このエラーは空のArrayやObject(正確なところはわかりませんが)を放置したままMongodbに渡すと発生するようです。
つまり、そうした空のArrayやObjectを消してしまえばいいわけです。空なので消しても問題ありません。
この関数では再帰を使ってすべてのArrayやObjectを探索し、ArrayやObjectを見つけた場合は空のものを削除します。ただし、Arrayでdeleteを使うとempty valueとして残ってしまい、完全には消えないようなので
v[key] = v[key].filter(e => e !== null && e !== undefined);
で消しています。
なお、この関数を書くにあたっては、mongo-sanitize(https://github.com/vkarpov15/mongo-sanitize)を参考にしました。