find()方法返回具有不需要的属性的对象
mongoose .find() method returns object with unwanted properties
所以,我和猫鼬合作了一段时间,我发现了一些非常奇怪的事情。如果有人能启发我,那就太好了。
问题是,当使用mongoose的.find()方法时,我作为响应得到的对象充满了我不知道它来自哪里的属性(我猜它们是内置属性,但不管怎样),我只想迭代我的.select()属性。明白了吗?不好的…解释得更好:
我已经声明了我的模式和模型:
var mySchema = mongoose.Schema({
name: String,
prop1: String,
prop2: String,
prop3: String
})
var myModel = DB.model('myDataBase', mySchema)
然后我想找到一个名为John的文档,并检索除"name"字段外的所有字段,所以我选择:
myModel.find({name: 'John'}, '-name', function(err, results){
log(results[0])
}
和log(results[0])logs
{ prop1: 'one',
prop2: 'two',
prop3: 'three' }
到目前为止,一切都很好。但问题是,现在我想迭代这些属性并逐一检查,我不确定每个结果会有多少"道具",所以我想做一些类似的事情:
for(var key in results[0]){
log(key)
}
所以,我希望它会记录"prop1"、"prop2"answers"prop3",但不会!好吧,我得到了道具1、2和3,但我也得到了很多其他属性和函数,如:isNew、error、_maxListeners、_doc等。不仅这些额外的属性,我还得到了"name"属性,即我从选择中排除的属性(它被排除了,如第一个日志中所示)。奇怪吧?
但是等一下!还有更多!我在网上搜索过,发现有人说"老兄,当迭代对象属性时,使用hasOwnProperty方法!"。所以我去了:
for (var key in results[0]){
if (results[0].hasOwnProperty(key)) log(key)
}
日志结果是一些属性(具体来说:$__、isNew、error、_maxListeners、_doc、_pres、_posts、save、_events),并且不包括我最初想要的任何道具。
我的问题是,除了这些我不知道的内置属性和我在参数中明确排除的属性,我如何只遍历道具1、2和3?(ps:如果可能的话,我正在考虑一个不需要将我的对象转换为数组的解决方案)
此外,这本身不是一个问题,但出于好奇,这些特性来自哪里?为什么它们出现在for循环中,而不是在我记录对象时?为什么我排除的属性("-name")也出现在for循环中?如果hasOwnProperty不识别刚刚记录的属性,它到底是干什么的?
感谢您的时间和帮助!再见
除了Kevin B的答案之外,您还可以将{lean: true}
作为选项传递:
myModel.find({name: 'John'}, '-name', {lean: true}, function(err, results){
log(results[0])
}
在MongoDB中,文档被简单地保存为对象。当Mongoose检索它们时,它将它们转换为Mongoose文档。在这样做的过程中,它会添加for
循环中包含的所有密钥。这就是允许您使用所有文档方法的原因。如果您不打算使用其中任何一个,lean
是一个很好的选择,因为它跳过了整个过程,提高了查询速度。可能快3倍。
在这种情况下,.toObject足以让您的循环按预期方式工作。
myModel.find({name: 'John'}, '-name', function(err, results){
log(results[0].toObject())
}
您最初获得的额外属性是因为results
是一个模型实例的集合,它附带了普通对象上不可用的额外属性和方法。这些属性和方法将出现在您的循环中。通过使用toObject
,您可以获得一个没有所有这些附加属性和方法的普通对象。
对mongo查询使用lean()或传递{lean:true}参数例如myModel.find().lean()
TLDR:toObject()和lean()获取JavaScript对象所需的两种方法,前面的答案已经指出了这一点。我的答案有一个完整的例子,说明了这个概念以及如何使用它们
当您使用Mongoose API查询数据(find,findOne,findById..)时,Mongoose会在响应中为您提供Mongoose Document类的实例,这与您的Javascript对象不同。
您可以选择一些选项来获取Javascript对象,如文档中所述:
- 使用lean()方法:在此处查看文档
- 使用toObject()方法:在此处查看文档
我创建了一个测试项目来演示这些方法,可以自由测试:
const mongoose = require('mongoose');
// connect to database
mongoose.connect('mongodb://localhost/test', { useNewUrlParser: true, useUnifiedTopology: true });
// define the schema
const kittySchema = new mongoose.Schema({
name: String
// this flag indicate that the shema we defined is not fixed,
// document in database can have some fields that are not defined in the schema
// which is very likely
}, { strict: false });
// compile schema to model
const Kitten = mongoose.model('Kitten', kittySchema);
test();
async function test() {
// test data
const dataObject = { name: "Kitty 1", color: "red" };
const firstKitty = new Kitten(dataObject); // attribute color is not defined in the schema
// save in database
firstKitty.save();
// find the kitty from database
// mongoose return a document object, which is different from our data object
const firstKittyDocument = await Kitten.findOne({ name: "Kitty 1" });
console.log("Mongoose document. _id :", firstKittyDocument._id); // _id of document
console.log("Mongoose document. name :", firstKittyDocument.name); // "Kitty 1"
console.log("Mongoose document. color :", firstKittyDocument.color); // undefined
// --> the document contains _id and other fields that we defined in the schema
// we can call the method .toObject to get the plain object
console.log("Using .toObject() method. _id :", firstKittyDocument.toObject()._id); // _id of document
console.log("Using .toObject() method. name :", firstKittyDocument.toObject().name); // "Kitty 1"
console.log("Using .toObject() method. color :", firstKittyDocument.toObject().color); // "red"
// --> Using .toObject() method, we get all the fields we have in the dataObject
// or we can use lean method to get the plain old javascript object
const firstKittyPOJO = await Kitten.findOne({ name: "Kitty 1" }).lean();
console.log("Using .lean() method. _id :", firstKittyPOJO._id); // _id of document
console.log("Using .lean() method. name :", firstKittyPOJO.name); // "Kitty 1"
console.log("Using .lean() method. color :", firstKittyPOJO.color); //"red"
// --> Using .lean() method, we get all the fields we have in the dataObject
}
需要注意的是,当您使用lean()
方法时,Mongoose会跳过将JavaScript对象转换为Mongoose文档的步骤,这将为您的查询带来更好的性能。
答案很好,我想添加一个我添加到dbUtils类中的小typescript实用程序。
getCleanObjectFromObjectOrDocument<T>(obj: T): T {
return ((obj as unknown) as Document)?.toObject?.() ?? obj;
}
你可以在这里传递mongoose文档/子文档或任何普通的JS对象,它会返回相应的JS对象。
- 对象不支持属性或方法“自动完成”
- 如何在不需要单击按钮的情况下获取选项的值
- Javascript不需要的关闭行为
- 删除客户端浏览器上不需要的内容
- 不允许在字段中输入不需要的字符
- 不需要的JSON响应
- 如何使图像适合TD,而不需要包装在不同的屏幕分辨率上
- 使用一个Javascript函数提交多个表单可以在FF中工作,而不需要其他浏览器
- jQuery自动完成显示不需要的动态html标记
- 在Knockout js中创建一个包含多行的表,而不需要推送
- AngularJs正在解码HTML字符(不需要)
- find()方法返回具有不需要的属性的对象
- 在grunt中设置一个属性就是创建一个不需要的json对象
- 如何将控制器's content属性设置为模型数组,而不需要相应的路由
- 一个方法需要HttpGet属性,而另一个方法不需要
- 视图控制器需要:属性不工作
- 可以将对象的属性传递给函数,而不需要定义参数,并通过对象的键来使用它们
- 查找属性的第一次出现(不需要精确匹配)
- 可引导的数据-url属性,但不需要的查询参数!(
- MVC不需要ViewModel上的双重属性导致问题