Javascript 存在引用时不调用Mongoose模型填充回调

Javascript 存在引用时不调用Mongoose模型填充回调,javascript,node.js,mongodb,mongodb-query,aggregation-framework,Javascript,Node.js,Mongodb,Mongodb Query,Aggregation Framework,我试图使用Mongoose的Model.populate()将用户ID转换为用户,该用户ID位于通过聚合和展开获取的文档的子结构中。我猜可能是我的模式出了问题,或者可能是解压中断了与子模式的连接 问题是:当存在有效引用时,不会调用填充回调函数。当没有子结构时,调用回调,原始文档保持不变 结构: 我有一些文章,每个用户可以没有或有很多文章评级 我在ArticleRating中使用了两个参考,与文章和进行评级的用户相关 过程: 这个过程实际上是将文章导出为(传统的)CSV格式,并将结构扁平化为具有用

我试图使用Mongoose的Model.populate()将用户ID转换为用户,该用户ID位于通过聚合和展开获取的文档的子结构中。我猜可能是我的模式出了问题,或者可能是解压中断了与子模式的连接

问题是:当存在有效引用时,不会调用填充回调函数。当没有子结构时,调用回调,原始文档保持不变

结构:

我有一些文章,每个用户可以没有或有很多文章评级

我在ArticleRating中使用了两个参考,与文章和进行评级的用户相关

过程:

这个过程实际上是将文章导出为(传统的)CSV格式,并将结构扁平化为具有用户特定评级的重复文章行。“展开”非常适合此操作,而“保留空值”将保留没有分级的项目

调试:

我尝试深入了解Model.populate代码。所有的承诺和回调包装都非常复杂,但是我可以看到底层的填充调用也没有调用内部回调。我没有使用promise变体,但不能100%确定是否应该使用?(Mongoose文档在回调和承诺之间的用例上有点模糊)

我已经仔细检查了我的模式,尝试将模型显式地添加到populate调用中(这不应该是必需的,因为它在模式中)。没有错误或异常,它不会崩溃

在Chrome调试器中单步执行代码显示的模型与我预期的一样:前几篇文章有一个带有有效ObjectId的rating.userId,但在这些情况下,不会调用populate回调函数。接下来的一百篇文章没有“评级”集,所有文章都会可靠地调用回调

所以我猜我做错了什么,是把Model.populate引入了一个错误不正常的路径

注意:我知道我可以重写代码以使用聚合$lookup或其他嵌入结构,而不是外部引用,但我已经完成了功能拼图的最后一部分,希望它能够正常工作

这是简化的模式:

const ArticleRatingSchema = new Schema({
    articleId: {type: Schema.Types.ObjectId, ref:'Article'},
    userId: {type: Schema.Types.ObjectId, ref:'User'},                          
    rating: String,                                                                                                             
    comment: String,                                                                                                        
});

const ArticleSchema = new Schema({
    title: String,
    rating: ArticleRatingSchema,
});
这就是查找

    // Find all articles relating to this project, and their ratings.
    // Unwind does the duplicate per-user, and preserve keeps un-rated articles.
    articleModel.aggregate([
            {$match: {projectId: projectId}},
            {$lookup:{from:'articleratings', localField:'_id', foreignField:'articleId', as:'rating' }},
            {$unwind: {path:'$rating', preserveNullAndEmptyArrays:true}}
        ], (err, models) =>
    {
        if (!err) {

            models.map((article) => {

                articleModel.populate(article, {path:'rating.userId', model:'User'}, (err, article)=> {
                    // Process the article...
                    // this callback only gets called where there is NO rating in the article.
                });

            });
        }

我意识到这是因为我在同步map()循环中处理集合,这两种情况必须有所不同,因为不匹配的填充是同步回调,而匹配的替换稍后回调

如果在回调中输入console.log(),我会发现这四个匹配的案例是在CSV格式化和下载之后最后处理的

所以答案是:正在调用populate,但是是异步的


我需要重新设计map()循环,以适应通常的异步模式。

我个人对您认为要在聚合管道中使用什么,然后需要
.populate()
结果感到有点困惑。因为要求使用
.populate()
本质上意味着向服务器发出额外的查询以“模拟连接”

因此,既然实际上是“在服务器上加入”,那么您真的应该使用它

您可以使用
.populate()
,我将展示一些代码,以表明它可以完成。但这确实是多余的,因为您也可以在服务器上完成所有工作

因此,我对结构的最佳“近似”是:

文章

{
        "_id" : ObjectId("5962104312246235cdcceb16"),
        "title" : "New News",
        "ratings" : [ ],
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb17"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb13"),
        "rating" : "5",
        "comment" : "Great!",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb18"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb14"),
        "rating" : "3",
        "comment" : "Okay I guess ;)",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb19"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb15"),
        "rating" : "1",
        "comment" : "Hated it :<",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb13"),
        "name" : "Bill",
        "email" : "bill@example.com",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb14"),
        "name" : "Fred",
        "email" : "fred@example.com",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb15"),
        "name" : "Ted",
        "email" : "ted@example.com",
        "__v" : 0
}
文章评级

{
        "_id" : ObjectId("5962104312246235cdcceb16"),
        "title" : "New News",
        "ratings" : [ ],
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb17"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb13"),
        "rating" : "5",
        "comment" : "Great!",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb18"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb14"),
        "rating" : "3",
        "comment" : "Okay I guess ;)",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb19"),
        "articleId" : ObjectId("5962104312246235cdcceb16"),
        "userId" : ObjectId("5962104312246235cdcceb15"),
        "rating" : "1",
        "comment" : "Hated it :<",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb13"),
        "name" : "Bill",
        "email" : "bill@example.com",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb14"),
        "name" : "Fred",
        "email" : "fred@example.com",
        "__v" : 0
}
{
        "_id" : ObjectId("5962104312246235cdcceb15"),
        "name" : "Ted",
        "email" : "ted@example.com",
        "__v" : 0
}
然后是聚合语句:

  Article.aggregate(
    [
      { "$lookup": {
        "from": ArticleRating.collection.name,
        "localField": "_id",
        "foreignField": "articleId",
        "as": "ratings"
      }},
      { "$unwind": "$ratings" },
      { "$lookup": {
        "from": User.collection.name,
        "localField": "ratings.userId",
        "foreignField": "_id",
        "as": "ratings.userId",
      }},
      { "$unwind": "$ratings.userId" },
      { "$group": {
        "_id": "$_id",
        "title": { "$first": "$title" },
        "ratings": { "$push": "$ratings" }
      }}
    ],
    (err,articles) => {
      if (err) callback(err);
      log(articles);
      callback();
    }
  )
结果是:

  {
    "_id": "5962126f3ef2fb35efeefd94",
    "title": "New News",
    "ratings": [
      {
        "_id": "5962126f3ef2fb35efeefd95",
        "articleId": "5962126f3ef2fb35efeefd94",
        "userId": {
          "_id": "5962126f3ef2fb35efeefd91",
          "name": "Bill",
          "email": "bill@example.com",
          "__v": 0
        },
        "rating": "5",
        "comment": "Great!",
        "__v": 0
      },
      {
        "_id": "5962126f3ef2fb35efeefd96",
        "articleId": "5962126f3ef2fb35efeefd94",
        "userId": {
          "_id": "5962126f3ef2fb35efeefd92",
          "name": "Fred",
          "email": "fred@example.com",
          "__v": 0
        },
        "rating": "3",
        "comment": "Okay I guess ;)",
        "__v": 0
      },
      {
        "_id": "5962126f3ef2fb35efeefd97",
        "articleId": "5962126f3ef2fb35efeefd94",
        "userId": {
          "_id": "5962126f3ef2fb35efeefd93",
          "name": "Ted",
          "email": "ted@example.com",
          "__v": 0
        },
        "rating": "1",
        "comment": "Hated it :<",
        "__v": 0
      }
    ]
  }
全输出

Mongoose: users.remove({}, {})
Mongoose: articles.remove({}, {})
Mongoose: articleratings.remove({}, {})
Mongoose: users.insert({ name: 'Bill', email: 'bill@example.com', _id: ObjectId("596219ff6f73ed36d868ed40"), __v: 0 })
Mongoose: users.insert({ name: 'Fred', email: 'fred@example.com', _id: ObjectId("596219ff6f73ed36d868ed41"), __v: 0 })
Mongoose: users.insert({ name: 'Ted', email: 'ted@example.com', _id: ObjectId("596219ff6f73ed36d868ed42"), __v: 0 })
Mongoose: articles.insert({ title: 'New News', _id: ObjectId("596219ff6f73ed36d868ed43"), ratings: [], __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed40"), rating: '5', comment: 'Great!', _id: ObjectId("596219ff6f73ed36d868ed44"), __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed41"), rating: '3', comment: 'Okay I guess ;)', _id: ObjectId("596219ff6f73ed36d868ed45"), __v: 0 })
Mongoose: articleratings.insert({ articleId: ObjectId("596219ff6f73ed36d868ed43"), userId: ObjectId("596219ff6f73ed36d868ed42"), rating: '1', comment: 'Hated it :<', _id: ObjectId("596219ff6f73ed36d868ed46"), __v: 0 })
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } } ], {})
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed40") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed41") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed42") ] } }, { fields: {} })
[
  {
    "_id": "596219ff6f73ed36d868ed43",
    "title": "New News",
    "__v": 0,
    "ratings": [
      {
        "_id": "596219ff6f73ed36d868ed44",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed40",
          "name": "Bill",
          "email": "bill@example.com",
          "__v": 0
        },
        "rating": "5",
        "comment": "Great!",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed45",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed41",
          "name": "Fred",
          "email": "fred@example.com",
          "__v": 0
        },
        "rating": "3",
        "comment": "Okay I guess ;)",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed46",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed42",
          "name": "Ted",
          "email": "ted@example.com",
          "__v": 0
        },
        "rating": "1",
        "comment": "Hated it :<",
        "__v": 0
      }
    ]
  }
]
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } } ], {})
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed40") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed41") ] } }, { fields: {} })
Mongoose: users.find({ _id: { '$in': [ ObjectId("596219ff6f73ed36d868ed42") ] } }, { fields: {} })
[
  {
    "_id": "596219ff6f73ed36d868ed43",
    "title": "New News",
    "__v": 0,
    "ratings": [
      {
        "_id": "596219ff6f73ed36d868ed44",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed40",
          "name": "Bill",
          "email": "bill@example.com",
          "__v": 0
        },
        "rating": "5",
        "comment": "Great!",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed45",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed41",
          "name": "Fred",
          "email": "fred@example.com",
          "__v": 0
        },
        "rating": "3",
        "comment": "Okay I guess ;)",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed46",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed42",
          "name": "Ted",
          "email": "ted@example.com",
          "__v": 0
        },
        "rating": "1",
        "comment": "Hated it :<",
        "__v": 0
      }
    ]
  }
]
Mongoose: articles.aggregate([ { '$lookup': { from: 'articleratings', localField: '_id', foreignField: 'articleId', as: 'ratings' } }, { '$unwind': '$ratings' }, { '$lookup': { from: 'users', localField: 'ratings.userId', foreignField: '_id', as: 'ratings.userId' } }, { '$unwind': '$ratings.userId' }, { '$group': { _id: '$_id', title: { '$first': '$title' }, ratings: { '$push': '$ratings' } } } ], {})
[
  {
    "_id": "596219ff6f73ed36d868ed43",
    "title": "New News",
    "ratings": [
      {
        "_id": "596219ff6f73ed36d868ed44",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed40",
          "name": "Bill",
          "email": "bill@example.com",
          "__v": 0
        },
        "rating": "5",
        "comment": "Great!",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed45",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed41",
          "name": "Fred",
          "email": "fred@example.com",
          "__v": 0
        },
        "rating": "3",
        "comment": "Okay I guess ;)",
        "__v": 0
      },
      {
        "_id": "596219ff6f73ed36d868ed46",
        "articleId": "596219ff6f73ed36d868ed43",
        "userId": {
          "_id": "596219ff6f73ed36d868ed42",
          "name": "Ted",
          "email": "ted@example.com",
          "__v": 0
        },
        "rating": "1",
        "comment": "Hated it :<",
        "__v": 0
      }
    ]
  }
]
Mongoose:users.remove({},{})
Mongoose:articles.remove({},{})
Mongoose:articleratings.remove({},{})
Mongoose:users.insert({name:'Bill',email:'bill@example.com“,”id:ObjectId(“596219ff6f73ed36d868ed40”),”v:0})
Mongoose:users.insert({name:'Fred',email:'fred@example.com“,”id:ObjectId(“596219ff6f73ed36d868ed41”),”v:0})
Mongoose:users.insert({name:'Ted',email:'ted@example.com“,”id:ObjectId(“596219ff6f73ed36d868ed42”),”v:0})
Mongoose:articles.insert({标题:'新新闻',_id:ObjectId(“596219ff6f73ed36d868ed43”),评级:[],_v:0})
Mongoose:articleratings.insert({articleId:ObjectId(“596219ff6f73ed36d868ed43”)、userId:ObjectId(“596219ff6f73ed36d868ed40”)、rating:'5',comment:'Great!'、id:ObjectId(“596219ff6f73ed36d868ed44”)、\uu v:0})
Mongoose:articleratings.insert({articleId:ObjectId(“596219ff6f73ed36d868ed43”)、userId:ObjectId(“596219ff6f73ed36d868ed41”)、rating:'3',comment:'Okay I guess;),(id:ObjectId(“596219ff6f73ed36d868ed45”),u v:0})
Mongoose:articleratings.insert({articleId:ObjectId(“596219ff6f73ed36d868ed43”)、userId:ObjectId(“596219ff6f73ed36d868ed42”)、评级:“1”、注释:“它: