如何在使用Mongoose的populate()时跳过已删除的引用?

huangapple go评论59阅读模式
英文:

How to skip removed ref while using populate() in Mongoose?

问题

以下是翻译好的内容:

有一个名为MyCollection的模式,其中包括:

...
author: {
    type: mongoose.Schema.Types.ObjectId,
    required: true, // 与false相同
    ref: 'Author'
},
...

以及以下代码:

MyCollection
    .find(filter, '_id author text')
    .populate('author', '_id name')
    .exec(),

如果从Authors集合中删除了带有author的文档,上述的populate方法会抛出以下错误:

无法读取null的属性(读取'_id')

问题是:如何配置populate()以忽略已删除的引用并简单地返回不带author字段的文档?

英文:

There is a MyCollection schema with:

...
author: {
    type: mongoose.Schema.Types.ObjectId,
    required: true, // the same with false
    ref: 'Author'
},
...

And the code:

MyCollection
    .find(filter, '_id author text')
    .populate('author', '_id name')
    .exec(),

If the document with author was removed from Authors collection, the populate method above throws the following error:

> Cannot read properties of null (reading '_id')

The question is: How can populate() be configured to ignore removed ref's and simply return the document without the author field?

答案1

得分: 1

我认为忽略已删除的引用并不是一个好主意。如果子文档 author 已被删除,最好的做法是删除引用。Mongoose 的默认行为是在没有文档时返回 null,这就是您收到的错误消息的原因。Mongoose 为您提供了两个辅助方法来检查字段是否已填充。您可以尝试包含以下内容:

// 如果已填充 author,则返回一个真值
MyCollection.populated('author');

// 取消填充 author
MyCollection.depopulate('author');

然而,我建议您重构您的代码,以便在从作者集合中删除文档时传递已删除文档的 author._id,以便可以从 MyCollection 中删除它。类似这样:

const deletedAuthor = await authorModel.findByIdAndDelete(authorid);
const myCollection = await myCollectionModel.findById(parentId);
myCollection.author.pull(authorid);
await myCollection.save();

另外,您不需要在 .populate('author', '_id name') 投影中包括 _id,因为根据文档_id 字段默认返回匹配的文档。

您可以在查询后使用基本 JavaScript 删除 null 字段,但您需要将 exec() 更改为 lean() 方法,以便返回 POJO,像这样:

const myCollection = await MyCollection
  .find(filter, '_id author text')
  .populate('author', '_id name')
  .lean();

const myCollectionFiltered = myCollection.map((doc) => {
   if (doc.author === null) {
      delete doc.author;
   }
   return doc;
});

现在 `myCollectionFiltered` 不包含作者的 null 
英文:

I don't think ignoring removed refs is a good idea. It would be better to remove the ref if the subdocument author has been deleted. The Mongoose default behaviour is to return null when there's no document, hence the error message you received. Mongoose provides you with two helper methods to check if the field is populated. You could try to incorporate:

// Returns a truthy value if author populated   
MyCollection.populated('author'); 


// Make author not populated anymore
MyCollection.depopulate('author');

However, I would suggest you refactor your code so that when a document from author collection is deleted you pass the author._id of the deleted document so that it can be pulled from the MyCollection. Something like:

const deletedAuthor = await authorModel.findByIdAndDelete(authorid);
const myCollection = await myCollectionModel.findById(parentId);
myCollection.author.pull(authorid);
await myCollection.save();

Also, you don't need to include _id in your .populate('author', '_id name') projection because according to the docs the _id field is returned as default on matching documents.

You can remove null fields after your query using basic JavaScript but you need to change exec() to the lean() method so that POJO are returned like so:

const myCollection = await MyCollection
.find(filter, '_id author text')
.populate('author', '_id name')
.lean();

const myCollectionFiltered = myCollection.map((doc)=>{
   if(doc.author === null){
      delete doc.author;
   }
   return doc;
});

Now myCollectionFiltered contains no null values for author.

huangapple
  • 本文由 发表于 2023年7月17日 20:44:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76704580.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定