【问题标题】:Mongoose populate virtual field with a getter function is not workingMongoose 使用 getter 函数填充虚拟字段不起作用
【发布时间】:2019-09-22 18:37:07
【问题描述】:

我正在使用 getter 函数填充 mongoose 模式中的虚拟字段。 它应该在没有任何错误的情况下填充该字段,但它会抛出错误

MongooseError: 如果要填充虚拟,则必须设置 localField 和 foreignField 选项

当我们使用 getter 填充字段时,不需要 localfield 和 foriegn 字段

const DashboardSchema = new Schema({
    userId: {
        type: SchemaTypes.ObjectId
    }
}, { toJSON: { virtuals: true } });

DashboardSchema.virtual('TopReports').get(function () {
   return TopReports.find({ userId: this.userId }).sort('-date').limit(10);
})

【问题讨论】:

    标签: mongodb mongoose


    【解决方案1】:

    如果您想要一个带有sortlimit 的“虚拟填充”,那么您实际上并不是这样做的。您创建的只是一个“虚拟吸气剂”,它实际上是返回异步函数的结果。您可以使用它,但管理解析返回的Promise 会很棘手,而且它与populate() 无关,这是您引发错误的地方。

    也有不同的选择。

    Mongoose 虚拟填充

    对于这个最接近你尝试的东西,你想要这样的东西:

    const dashboardSchema = new Schema({
      userId: Schema.Types.ObjectId
    },
    {
      toJSON: { virtuals: true },
      toObject: { virtuals: true }
    });
    
    dashboardSchema.virtual('TopReports', {
      ref: 'Report',
      localField: 'userId',
      foreignField: 'userId',
      options: { sort: { date: -1 }, limit: 10 } // optional - could add with populate
    });
    
    const reportSchema = new Schema({
      userId: Schema.Types.ObjectId,
      seq: Number,
      date: Date
    });
    
    const Dashboard = mongoose.model('Dashboard', dashboardSchema);
    const Report = mongoose.model('Report', reportSchema);
    

    这实际上与"Populate Virtuals" 上的文档示例几乎相同,因为那里的给定示例还包括options,这与将选项传递给填充方法本身相同。当您在 virtual 中设置 options 时,您只需要像这样调用:

    let result = await Dashboard.findOne().populate('TopReports');
    

    sortlimit 的默认设置会在执行 populate() 时自动应用于此“虚拟”字段。如果您选择不包含 options,您只需手动添加选项:

    let result2 = await Dashboard.findOne().populate({
      path: 'TopReports',
      options: { sort: '-date', limit: 5 }
    });
    log(result2);
    

    重要 - 在virtual 中设置options始终覆盖任何options 传递给populate(),如上所示。如果你想在不同的请求上使用不同的options,那么你调用上面的方法而不是定义附加到架构的virtual方法。

    这就是你真正需要做的。当然,定义包括localFieldforeignField 以及ref,因此populate() 调用知道从哪里获取数据以及与哪些字段相关。还有一个可选 justOne 用于区分单数和Array 结果,以及一些其他选项。

    MongoDB $查找

    这里的另一个选项是 MongoDB 基本上内置了相同的功能,除了这是 单个请求 而不是 populate() 实际上是 多个请求 为了从不同的集合返回数据:

       let result = await Dashboard.aggregate([
          { "$lookup": {
            "from": Report.collection.name,
            "let": { "userId": "$userId" },
            "pipeline": [
              { "$match": { "$expr": { "$eq": [ "$userId", "$$userId" ] } } },
              { "$sort": { "date": -1 } },
              { "$limit": 10 }
            ],
            "as": "TopReports"
          }}
        ]);
    

    populate() 实际发出的“许多”find() 请求相比,这是一个具有单一响应的请求。结果相同,只是使用了$lookup 的“子管道”形式,以便将$sort$limit 应用于返回的相关项数组。

    通过一些工作,您甚至可以从 mongoose 模式中检查模式定义(包括定义的 virtual )并构造相同的 $lookup 语句。在Querying after populate in Mongoose 上有这个“模式检查”的基本演示。


    所以这取决于最适合您的需求。我建议同时尝试,甚至对应用程序性能进行基准测试。

    作为一个完整的演示,这里是一个示例清单。它插入了 20 个东西,只返回数组中最近的 10 个结果:

    const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
    
    const uri = 'mongodb://localhost:27017/test';
    const opts = { useNewUrlParser: true };
    
    mongoose.set('useFindAndModify', false);
    mongoose.set('useCreateIndex', true);
    mongoose.set('debug', true);
    
    const dashboardSchema = new Schema({
      userId: Schema.Types.ObjectId
    },
    {
      toJSON: { virtuals: true },
      toObject: { virtuals: true }
    });
    
    dashboardSchema.virtual('TopReports', {
      ref: 'Report',
      localField: 'userId',
      foreignField: 'userId',
      options: { sort: { date: -1 }, limit: 10 } // optional - could add with populate
    });
    
    const reportSchema = new Schema({
      userId: Schema.Types.ObjectId,
      seq: Number,
      date: Date
    });
    
    const Dashboard = mongoose.model('Dashboard', dashboardSchema);
    const Report = mongoose.model('Report', reportSchema);
    
    const log = data => console.log(JSON.stringify(data, undefined, 2));
    
    (async function() {
    
      try {
    
        const conn = await mongoose.connect(uri, opts);
    
        await Promise.all(
          Object.entries(conn.models).map(([k,m]) => m.deleteMany())
        );
    
        // Insert some things
        let { userId } = await Dashboard.create({ userId: new ObjectId() });
    
        const oneDay = ( 1000 * 60 * 60 * 24 );
        const baseDate = Date.now() - (oneDay * 30); // 30 days ago
    
        await Report.insertMany(
          [ ...Array(20)]
            .map((e,i) => ({ userId, seq: i+1, date: baseDate + ( oneDay * i ) }))
        );
    
        // Virtual populate
        let popresult = await Dashboard.findOne().populate('TopReports');
        log(popresult);
    
    
        // Aggregate $lookup
        let result = await Dashboard.aggregate([
          { "$lookup": {
            "from": Report.collection.name,
            "let": { "userId": "$userId" },
            "pipeline": [
              { "$match": { "$expr": { "$eq": [ "$userId", "$$userId" ] } } },
              { "$sort": { "date": -1 } },
              { "$limit": 10 }
            ],
            "as": "TopReports"
          }}
        ]);
    
        log(result);
    
    
      } catch (e) {
        console.error(e)
      } finally {
        mongoose.disconnect()
      }
    
    })()
    

    还有输出:

    Mongoose: dashboards.deleteMany({}, {})
    Mongoose: reports.deleteMany({}, {})
    Mongoose: dashboards.insertOne({ _id: ObjectId("5cce3d9e16302f32acb5c572"), userId: ObjectId("5cce3d9e16302f32acb5c571"), __v: 0 })
    Mongoose: reports.insertMany([ { _id: 5cce3d9e16302f32acb5c573, userId: 5cce3d9e16302f32acb5c571, seq: 1, date: 2019-04-05T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c574, userId: 5cce3d9e16302f32acb5c571, seq: 2, date: 2019-04-06T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c575, userId: 5cce3d9e16302f32acb5c571, seq: 3, date: 2019-04-07T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c576, userId: 5cce3d9e16302f32acb5c571, seq: 4, date: 2019-04-08T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c577, userId: 5cce3d9e16302f32acb5c571, seq: 5, date: 2019-04-09T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c578, userId: 5cce3d9e16302f32acb5c571, seq: 6, date: 2019-04-10T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c579, userId: 5cce3d9e16302f32acb5c571, seq: 7, date: 2019-04-11T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57a, userId: 5cce3d9e16302f32acb5c571, seq: 8, date: 2019-04-12T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57b, userId: 5cce3d9e16302f32acb5c571, seq: 9, date: 2019-04-13T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57c, userId: 5cce3d9e16302f32acb5c571, seq: 10, date: 2019-04-14T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57d, userId: 5cce3d9e16302f32acb5c571, seq: 11, date: 2019-04-15T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57e, userId: 5cce3d9e16302f32acb5c571, seq: 12, date: 2019-04-16T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c57f, userId: 5cce3d9e16302f32acb5c571, seq: 13, date: 2019-04-17T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c580, userId: 5cce3d9e16302f32acb5c571, seq: 14, date: 2019-04-18T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c581, userId: 5cce3d9e16302f32acb5c571, seq: 15, date: 2019-04-19T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c582, userId: 5cce3d9e16302f32acb5c571, seq: 16, date: 2019-04-20T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c583, userId: 5cce3d9e16302f32acb5c571, seq: 17, date: 2019-04-21T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c584, userId: 5cce3d9e16302f32acb5c571, seq: 18, date: 2019-04-22T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c585, userId: 5cce3d9e16302f32acb5c571, seq: 19, date: 2019-04-23T01:34:22.554Z, __v: 0 }, { _id: 5cce3d9e16302f32acb5c586, userId: 5cce3d9e16302f32acb5c571, seq: 20, date: 2019-04-24T01:34:22.554Z, __v: 0 } ], {})
    Mongoose: dashboards.findOne({}, { projection: {} })
    Mongoose: reports.find({ userId: { '$in': [ ObjectId("5cce3d9e16302f32acb5c571") ] } }, { sort: { date: -1 }, limit: 10, projection: {} })
    {
      "_id": "5cce3d9e16302f32acb5c572",
      "userId": "5cce3d9e16302f32acb5c571",
      "__v": 0,
      "TopReports": [
        {
          "_id": "5cce3d9e16302f32acb5c586",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 20,
          "date": "2019-04-24T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c585",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 19,
          "date": "2019-04-23T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c584",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 18,
          "date": "2019-04-22T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c583",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 17,
          "date": "2019-04-21T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c582",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 16,
          "date": "2019-04-20T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c581",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 15,
          "date": "2019-04-19T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c580",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 14,
          "date": "2019-04-18T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c57f",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 13,
          "date": "2019-04-17T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c57e",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 12,
          "date": "2019-04-16T01:34:22.554Z",
          "__v": 0
        },
        {
          "_id": "5cce3d9e16302f32acb5c57d",
          "userId": "5cce3d9e16302f32acb5c571",
          "seq": 11,
          "date": "2019-04-15T01:34:22.554Z",
          "__v": 0
        }
      ],
      "id": "5cce3d9e16302f32acb5c572"
    }
    Mongoose: dashboards.aggregate([ { '$lookup': { from: 'reports', let: { userId: '$userId' }, pipeline: [ { '$match': { '$expr': { '$eq': [ '$userId', '$$userId' ] } } }, { '$sort': { date: -1 } }, { '$limit': 10 } ], as: 'TopReports' } } ], {})
    [
      {
        "_id": "5cce3d9e16302f32acb5c572",
        "userId": "5cce3d9e16302f32acb5c571",
        "__v": 0,
        "TopReports": [
          {
            "_id": "5cce3d9e16302f32acb5c586",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 20,
            "date": "2019-04-24T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c585",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 19,
            "date": "2019-04-23T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c584",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 18,
            "date": "2019-04-22T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c583",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 17,
            "date": "2019-04-21T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c582",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 16,
            "date": "2019-04-20T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c581",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 15,
            "date": "2019-04-19T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c580",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 14,
            "date": "2019-04-18T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c57f",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 13,
            "date": "2019-04-17T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c57e",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 12,
            "date": "2019-04-16T01:34:22.554Z",
            "__v": 0
          },
          {
            "_id": "5cce3d9e16302f32acb5c57d",
            "userId": "5cce3d9e16302f32acb5c571",
            "seq": 11,
            "date": "2019-04-15T01:34:22.554Z",
            "__v": 0
          }
        ]
      }
    ]
    

    错误的方式

    只是为了演示“getter”方法有什么问题,下面是一个示例清单,显示了实际解决每个返回对象上返回的Promise

    const { Schema, Types: { ObjectId } } = mongoose = require('mongoose');
    
    const uri = 'mongodb://localhost:27017/test';
    const opts = { useNewUrlParser: true };
    
    mongoose.set('useFindAndModify', false);
    mongoose.set('useCreateIndex', true);
    mongoose.set('debug', true);
    
    const dashboardSchema = new Schema({
      userId: Schema.Types.ObjectId
    },
    {
      toJSON: { virtuals: true },
      toObject: { virtuals: true }
    });
    
    dashboardSchema.virtual('TopReports').get(function() {
      return Report.find({ userId: this.userId }).sort("-date").limit(10);
    });
    
    const reportSchema = new Schema({
      userId: Schema.Types.ObjectId,
      seq: Number,
      date: Date
    });
    
    const Dashboard = mongoose.model('Dashboard', dashboardSchema);
    const Report = mongoose.model('Report', reportSchema);
    
    const log = data => console.log(JSON.stringify(data, undefined, 2));
    
    (async function() {
    
      try {
    
        const conn = await mongoose.connect(uri, opts);
    
        await Promise.all(
          Object.entries(conn.models).map(([k,m]) => m.deleteMany())
        );
    
        // Insert some things
        let { userId } = await Dashboard.create({ userId: new ObjectId() });
    
        const oneDay = ( 1000 * 60 * 60 * 24 );
        const baseDate = Date.now() - (oneDay * 30); // 30 days ago
    
        await Report.insertMany(
          [ ...Array(20)]
            .map((e,i) => ({ userId, seq: i+1, date: baseDate + ( oneDay * i ) }))
        );
    
        // Mimic the virtual populate with the getter
        let results = await Dashboard.find();
        for ( let r of results ) {
          let obj = { ...r.toObject() };        // copy the plain object data only
          obj.TopReports = await r.TopReports;  // Resolve the Promise
          log(obj);
        }
    
      } catch (e) {
        console.error(e)
      } finally {
        mongoose.disconnect()
      }
    
    })()
    

    然后输出:

    Mongoose: dashboards.deleteMany({}, {})
    Mongoose: reports.deleteMany({}, {})
    Mongoose: dashboards.insertOne({ _id: ObjectId("5cce45193134aa37e88c4114"), userId: ObjectId("5cce45193134aa37e88c4113"), __v: 0 })
    Mongoose: reports.insertMany([ { _id: 5cce45193134aa37e88c4115, userId: 5cce45193134aa37e88c4113, seq: 1, date: 2019-04-05T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4116, userId: 5cce45193134aa37e88c4113, seq: 2, date: 2019-04-06T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4117, userId: 5cce45193134aa37e88c4113, seq: 3, date: 2019-04-07T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4118, userId: 5cce45193134aa37e88c4113, seq: 4, date: 2019-04-08T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4119, userId: 5cce45193134aa37e88c4113, seq: 5, date: 2019-04-09T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411a, userId: 5cce45193134aa37e88c4113, seq: 6, date: 2019-04-10T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411b, userId: 5cce45193134aa37e88c4113, seq: 7, date: 2019-04-11T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411c, userId: 5cce45193134aa37e88c4113, seq: 8, date: 2019-04-12T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411d, userId: 5cce45193134aa37e88c4113, seq: 9, date: 2019-04-13T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411e, userId: 5cce45193134aa37e88c4113, seq: 10, date: 2019-04-14T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c411f, userId: 5cce45193134aa37e88c4113, seq: 11, date: 2019-04-15T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4120, userId: 5cce45193134aa37e88c4113, seq: 12, date: 2019-04-16T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4121, userId: 5cce45193134aa37e88c4113, seq: 13, date: 2019-04-17T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4122, userId: 5cce45193134aa37e88c4113, seq: 14, date: 2019-04-18T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4123, userId: 5cce45193134aa37e88c4113, seq: 15, date: 2019-04-19T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4124, userId: 5cce45193134aa37e88c4113, seq: 16, date: 2019-04-20T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4125, userId: 5cce45193134aa37e88c4113, seq: 17, date: 2019-04-21T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4126, userId: 5cce45193134aa37e88c4113, seq: 18, date: 2019-04-22T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4127, userId: 5cce45193134aa37e88c4113, seq: 19, date: 2019-04-23T02:06:17.518Z, __v: 0 }, { _id: 5cce45193134aa37e88c4128, userId: 5cce45193134aa37e88c4113, seq: 20, date: 2019-04-24T02:06:17.518Z, __v: 0 } ], {})
    Mongoose: dashboards.find({}, { projection: {} })
    Mongoose: reports.find({ userId: ObjectId("5cce45193134aa37e88c4113") }, { sort: { date: -1 }, limit: 10, projection: {} })
    {
      "_id": "5cce45193134aa37e88c4114",
      "userId": "5cce45193134aa37e88c4113",
      "__v": 0,
      "TopReports": [
        {
          "_id": "5cce45193134aa37e88c4128",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 20,
          "date": "2019-04-24T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4127",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 19,
          "date": "2019-04-23T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4126",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 18,
          "date": "2019-04-22T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4125",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 17,
          "date": "2019-04-21T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4124",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 16,
          "date": "2019-04-20T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4123",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 15,
          "date": "2019-04-19T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4122",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 14,
          "date": "2019-04-18T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4121",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 13,
          "date": "2019-04-17T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c4120",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 12,
          "date": "2019-04-16T02:06:17.518Z",
          "__v": 0
        },
        {
          "_id": "5cce45193134aa37e88c411f",
          "userId": "5cce45193134aa37e88c4113",
          "seq": 11,
          "date": "2019-04-15T02:06:17.518Z",
          "__v": 0
        }
      ],
      "id": "5cce45193134aa37e88c4114"
    }
    

    当然需要为每个返回的文档解析getter返回的Promise。相比之下,populate() 使用$in 以通过单个请求返回find() 中所有结果的匹配条目,这将为每个Dashboard 文档而不是单个@ 发出一个新的find() 987654372@ 基于结果中每个 Dashboard 文档中找到的所有 userId 值。

    基本上,与populate()$lookup 不同,您实际上是将“加入”逻辑拆分为控制流的部分,它确实不属于这些部分,并且变得难以管理以及生成更多请求返回服务器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-22
      • 2020-12-08
      • 1970-01-01
      • 2017-12-06
      • 2019-06-17
      • 2018-05-20
      • 2023-03-29
      • 2012-01-22
      相关资源
      最近更新 更多