mongoose .find() 方法返回具有不需要的属性的对象

IT技术 javascript node.js mongoose properties
2021-03-01 17:16:28

所以,我已经和mongoose一起工作了一段时间,我发现了一些非常奇怪的事情。如果有人能启发我,那就太好了。

问题是,当使用 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]) 日志

{ prop1: 'one',
  prop2: 'two',
  prop3: 'three' }

到现在为止还挺好。但问题是,现在我想遍历这些属性并一一检查,但我不确定每个结果会有多少个“props”,所以我想做一些类似的事情:

for(var key in results[0]){
  log(key)
}

所以,我希望它会记录“prop1”、“prop2”和“prop3”,但不会!好的,我得到了 props 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)并且不包括我首先想要的任何props。

我的问题是,如何仅迭代 prop 1、2 和 3,排除这些(我不知道)内置属性和我在参数中明确排除的属性?(ps:如果可能的话,我正在考虑一个不需要将我的对象转换为数组的解决方案)

此外,这本身不是一个问题,但出于好奇,这些属性从何而来?为什么它们出现在 for 循环中而不是在我记录对象时出现?为什么我排除的属性 ('-name') 也出现在 for 循环中?如果 hasOwnProperty 无法识别刚刚记录的属性,它到底是干什么的?

感谢您的时间和帮助!再见!

4个回答

{lean: true}除了凯文 B 的回答,您可以作为一个选项传递

myModel.find({name: 'John'}, '-name', {lean: true}, function(err, results){
  log(results[0])
}

在 MongoDB 中,文档被简单地保存为对象。当 Mongoose 检索它们时,它会将它们转换为 Mongoose 文档。这样做时,它会添加for循环中包含的所有键这就是允许您使用所有文档方法的原因。如果您不会使用其中任何一个,这lean是一个不错的选择,因为它会跳过整个过程,从而提高查询速度。可能快 3 倍。

是的,我也遇到了这个。 await Model.find({status:'requested'}, {},{lean:true})
2021-04-23 17:16:28
passsing的{lean:true}解决了类似的问题对我来说。
2021-05-05 17:16:28
我试着跑,await myModel.find({status: 'requested'}, {lean: true});但没有用。我必须运行它await myModel.find({status: 'requested'}).lean().exec();才能让它工作。知道为什么吗?是因为{lean: true}只能作为find()调用的第三个参数吗?
2021-05-07 17:16:28

在这种情况下, .toObject 足以让您的循环按您期望的方式工作。

myModel.find({name: 'John'}, '-name', function(err, results){
  log(results[0].toObject())
}

您最初获得的额外属性是因为它results是一个模型实例的集合,这些实例带有在普通对象上不可用的其他属性和方法。这些属性和方法是您的循环中出现的内容。通过使用toObject,您可以获得一个没有所有这些附加属性和方法的普通对象。

太感谢了。我不得不JSON.parse(JSON.stringify(results))对每一个结果。使用.toObject()在各方面都更加清洁和更好。
2021-04-23 17:16:28
转换为 JSON 并返回的原因也是如此,因为文档对象的.toJSON()方法只是该.toObject()方法的别名,因此序列化格式仅包含精益对象。
2021-05-03 17:16:28
那么我的下一个问题 - 我怎样才能欺骗这些“隐藏”属性进行测试?
2021-05-15 17:16:28

答案很好,我想添加一个小typescript实用程序,我已将其添加到 dbUtils 类中。

getCleanObjectFromObjectOrDocument<T>(obj: T): T {
        return ((obj as unknown) as Document)?.toObject?.() ?? obj;
}

您可以在此处传递 mongoose 文档/子文档或任何普通 JS 对象,它会返回相应的 JS 对象。

在 mongo 查询上使用 Lean() 或传递 {lean:true} 参数,例如 myModel.find().lean()