查詢轉換 (Query Casting)

傳遞給 Model.find()Query#find()Model.findOne() 等方法的第一個參數稱為 filter。在較舊的內容中,此參數有時稱為 queryconditions。例如:

const query = Character.find({ name: 'Jean-Luc Picard' });
query.getFilter(); // `{ name: 'Jean-Luc Picard' }`

// Subsequent chained calls merge new properties into the filter
query.find({ age: { $gt: 50 } });
query.getFilter(); // `{ name: 'Jean-Luc Picard', age: { $gt: 50 } }`

當你使用 Query#exec()Query#then() 執行查詢時,Mongoose 會轉換篩選器以符合你的綱要。

// Note that `_id` and `age` are strings. Mongoose will cast `_id` to
// a MongoDB ObjectId and `age.$gt` to a number.
const query = Character.findOne({
  _id: '5cdc267dd56b5662b7b7cc0c',
  age: { $gt: '50' }
});

// `{ _id: '5cdc267dd56b5662b7b7cc0c', age: { $gt: '50' } }`
// Query hasn't been executed yet, so Mongoose hasn't casted the filter.
query.getFilter();

const doc = await query.exec();
doc.name; // "Jean-Luc Picard"

// Mongoose casted the filter, so `_id` became an ObjectId and `age.$gt`
// became a number.
query.getFilter()._id instanceof mongoose.Types.ObjectId; // true
typeof query.getFilter().age.$gt === 'number'; // true

如果 Mongoose 無法將篩選器轉換為你的綱要,你的查詢將會拋出 CastError

const query = Character.findOne({ age: { $lt: 'not a number' } });

const err = await query.exec().then(() => null, err => err);
err instanceof mongoose.CastError; // true
// Cast to number failed for value "not a number" at path "age" for
// model "Character"
err.message;

strictQuery 選項

預設情況下,Mongoose 不會轉換綱要中不存在的篩選器屬性。

const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });

// No error because `notInSchema` is not defined in the schema
await query.exec();

你可以使用綱要的 strictQuery 選項來配置此行為。此選項類似於 strict 選項。將 strictQuery 設定為 true 會從篩選器中移除非綱要屬性。

mongoose.deleteModel('Character');
const schema = new mongoose.Schema({ name: String, age: Number }, {
  strictQuery: true
});
Character = mongoose.model('Character', schema);

const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });

await query.exec();
query.getFilter(); // Empty object `{}`, Mongoose removes `notInSchema`

如果你的 filter 具有綱要中不存在的屬性,要讓 Mongoose 拋出錯誤,請將 strictQuery 設定為 'throw'

mongoose.deleteModel('Character');
const schema = new mongoose.Schema({ name: String, age: Number }, {
  strictQuery: 'throw'
});
Character = mongoose.model('Character', schema);

const query = Character.findOne({ notInSchema: { $lt: 'not a number' } });

const err = await query.exec().then(() => null, err => err);
err.name; // 'StrictModeError'
// Path "notInSchema" is not in schema and strictQuery is 'throw'.
err.message;

隱含的 $in

由於綱要,Mongoose 知道欄位應該是什麼類型,因此它可以提供一些簡潔的語法糖。例如,如果你忘記在非陣列欄位上加上 $in,Mongoose 會為你加上 $in

// Normally wouldn't find anything because `name` is a string, but
// Mongoose automatically inserts `$in`
const query = Character.findOne({ name: ['Jean-Luc Picard', 'Will Riker'] });

const doc = await query.exec();
doc.name; // "Jean-Luc Picard"

// `{ name: { $in: ['Jean-Luc Picard', 'Will Riker'] } }`
query.getFilter();