Asynchronous 如何在Mongoose虚拟属性中使用异步代码?

Asynchronous 如何在Mongoose虚拟属性中使用异步代码?,asynchronous,node.js,mongoose,Asynchronous,Node.js,Mongoose,我正在尝试将不同集合中的文档(不是嵌入文档)关联起来,虽然Mongoose中存在漏洞,但我现在正在尝试通过延迟加载关联文档和文档中的虚拟属性来解决这个问题 问题在于,虚拟的getter将函数作为参数,并使用虚拟属性的返回值。当虚拟机不需要任何异步调用来计算其值时,这非常好,但当我需要进行异步调用来加载另一个文档时,这不起作用。下面是我正在使用的示例代码: TransactionSchema.virtual('笔记本') .get(function(){/您可以定义一个虚拟方法,可以为其定义回调

我正在尝试将不同集合中的文档(不是嵌入文档)关联起来,虽然Mongoose中存在漏洞,但我现在正在尝试通过延迟加载关联文档和文档中的虚拟属性来解决这个问题

问题在于,虚拟的getter将函数作为参数,并使用虚拟属性的返回值。当虚拟机不需要任何异步调用来计算其值时,这非常好,但当我需要进行异步调用来加载另一个文档时,这不起作用。下面是我正在使用的示例代码:

TransactionSchema.virtual('笔记本')

.get(function(){/您可以定义一个虚拟方法,可以为其定义回调

以您的例子:

TransactionSchema.method('getNotebook', function(cb) {
  Notebook.findById(this.notebookId, function(err, notebook) {
    cb(notebook);
  })
});
虽然唯一的评论者似乎是那些迂腐的类型之一,但你也不应该害怕嵌入文档。据我所知,这是mongos的优点之一

其中一个使用上述代码,如下所示:

instance.getNotebook(function(nootebook){
    // hey man, I have my notebook and stuff
});

Josh的方法对于单文档查找非常有效,但我的情况稍微复杂一些。我需要对整个对象数组的嵌套属性进行查找。例如,我的模型更像这样:

var TransactionSchema = new Schema({
  ...
  , notebooks: {type: [Notebook]}
});

var NotebookSchema = new Schema({
  ...
  , authorName: String  // this should not necessarily persist to db because it may get stale
  , authorId: String
});

var AuthorSchema = new Schema({
  firstName: String
  , lastName: String
});
然后,在我的应用程序代码(我使用的是Express)中,当我得到一笔交易时,我想要所有带有作者姓氏的笔记本:

...
TransactionSchema.findById(someTransactionId, function(err, trans) {
  ...
  if (trans) {
    var authorIds = trans.notebooks.map(function(tx) {
        return notebook.authorId;
      });

    Author.find({_id: {$in: authorIds}, [], function(err2, authors) {
      for (var a in authors) {
        for (var n in trans.notebooks {
          if (authors[a].id == trans.notebooks[n].authorId) {
            trans.notebooks[n].authorLastName = authors[a].lastName;
            break;
          }
        }
      }
      ...
    });

这似乎非常低效和粗糙,但我无法找到另一种方法来实现这一点。最后,我不熟悉node.js、mongoose和stackoverflow,所以请原谅我,如果这不是扩展讨论的最合适的地方。只是Josh的解决方案对我最终的“解决方案”最有帮助

虽然这解决了更广泛的问题,而不是具体的问题,但我仍然认为值得提交:

通过使用Mongoose的查询功能,您可以轻松地从另一个集合加载关联文档(与定义虚拟集合的结果几乎相同)。使用上面的示例,这需要在
事务
架构中指定ObjectID的ref(指向笔记本集合),然后调用
填充(NotebookId)
构造查询时。链接的Mongoose文档非常彻底地解决了这一问题


我不熟悉猫鼬的历史,但我猜在提交这些早期答案时,
populate
并不存在。

由于这是一个老问题,我想它可能需要更新


要实现异步虚拟字段,您可以使用,如mongoose的github问题中所述:

谢谢,我已经更新了代码示例,希望现在更清楚。我看到Josh设法为您提供了异步代码,请参阅。您遇到的问题只是一个“限制”与Mongoose为虚拟对象编写的
get
方法相结合,JS是同步的。它期望一个返回值的函数,并且没有流控制库能够让您在其中放入async并使其按预期工作。如果您尝试在下划线/l中执行async,则会遇到同样的问题odash回调。因此,需要Josh的解决方案来滚动它自己的异步方法并绕过Mongoose的同步
get
。您还可以在本地缓存Notebook对象以防止从DB多次获取,类似这样:
TransactionSchema.method('getNotebook',函数(cb){if(this.\u Notebook)返回cb(notebook);notebook.findById(this.notebookId,function(err,notebook){this._notebook=notebook;cb(notebook);});
但是是否可以在虚拟中使用此模式,以便TransactionSchema.virtual('notebook').get(function())调用方法“getNotebook”并返回结果?回调似乎在该上下文中不起作用。@Greg,没错。虽然这是一个很好的问题解决方案,但并不是你真正需要的是一个异步虚拟字段。但我相信异步getter/setter在JS中是不可能的。imhe这对我来说总是最合适的。simplest要实现和维护,您可以创建一个{u id:model}的映射,避免额外的loop.Object.keys(map)然后返回您的id。