Mongodb 猫鼬在聚集后繁殖

Mongodb 猫鼬在聚集后繁殖,mongodb,mongoose,mongodb-query,aggregation-framework,mongoose-populate,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,Mongoose Populate,在运行聚合管道之后,我试图获得一个特定的数据模型,然后再进行填充,但我还没有达到这个目标 最终的预期结果如下: [ { _accountId: "5beee0966d17bc42501f1234", name: "Company Name 1", contactEmail: "email1@email.com", contactName: "contact Name 1" reason: "Warranties", total: 1152,

在运行聚合管道之后,我试图获得一个特定的数据模型,然后再进行填充,但我还没有达到这个目标

最终的预期结果如下:

[
  {
    _accountId: "5beee0966d17bc42501f1234",
    name: "Company Name 1",
    contactEmail: "email1@email.com",
    contactName: "contact Name 1"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _accountId: "5beee0966d17bc42501f1235",
    name: "Company Name 2",
    contactEmail: "email2@email.com",
    contactName: "contact Name 2"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
Warranty.aggregate([
    {
      $match: {
        payStatus: "Invoiced Next Billing Cycle"
      }
    },
    {
      $group: {
        _id: "$_accountId",
        total: {
          $sum: "$warrantyFee"
        },
        lineItems: {
          $push: {
            _id: "$_id",
            jobsiteAddress: {
              $concat: [
                "$jobsiteAddressStreet",
                " ",
                "$jobsiteAddressCity",
                ", ",
                "$jobsiteAddressState",
                " ",
                "$jobsiteAddressZip"
              ]
            },
            warrantyFee: "$warrantyFee"
          }
        }
      }
    },
    {
      $project: {
        reason: "Warranties",
        total: "$total",
        lineItems: "$lineItems"
      }
    }
  ])
    .then(warranties => {
      console.log(warranties);
      Account.populate(warranties, {
        path: "_id",
        select: "contactName contactEmail name"
      })
        .then(warranties => {
          res.send(warranties);
        })
        .catch(err => {
          res.status(422).send(err);
          throw err;
        });
    })
    .catch(err => {
      res.status(422).send(err);
      throw err;
    });
[
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 1",
      contactEmail: "email1@email.com",
      contactName: "Contact Name 1"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 2",
      contactEmail: "email2@email.com",
      contactName: "Contact Name 2"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
我从以下两个模型中收集这些数据:

保修

{
  _id: "5beee0966d17bc42501f5086",
  jobsiteAddressStreet: String,
  jobsiteAddressCity: String,
  jobsiteAddressState" String,
  jobsiteAddressZip: Number,
  warrantyFee: Number,
  _accountId: {
    type: Schema.Types.ObjectId,
    ref: "accounts"
  },
  payStatus: String
}
账户

{
  _id: "5beee0966d17bc42501f1235",
  name: String,
  contactName: String,
  contactEmail: String
}
我当前的查询如下:

[
  {
    _accountId: "5beee0966d17bc42501f1234",
    name: "Company Name 1",
    contactEmail: "email1@email.com",
    contactName: "contact Name 1"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _accountId: "5beee0966d17bc42501f1235",
    name: "Company Name 2",
    contactEmail: "email2@email.com",
    contactName: "contact Name 2"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
Warranty.aggregate([
    {
      $match: {
        payStatus: "Invoiced Next Billing Cycle"
      }
    },
    {
      $group: {
        _id: "$_accountId",
        total: {
          $sum: "$warrantyFee"
        },
        lineItems: {
          $push: {
            _id: "$_id",
            jobsiteAddress: {
              $concat: [
                "$jobsiteAddressStreet",
                " ",
                "$jobsiteAddressCity",
                ", ",
                "$jobsiteAddressState",
                " ",
                "$jobsiteAddressZip"
              ]
            },
            warrantyFee: "$warrantyFee"
          }
        }
      }
    },
    {
      $project: {
        reason: "Warranties",
        total: "$total",
        lineItems: "$lineItems"
      }
    }
  ])
    .then(warranties => {
      console.log(warranties);
      Account.populate(warranties, {
        path: "_id",
        select: "contactName contactEmail name"
      })
        .then(warranties => {
          res.send(warranties);
        })
        .catch(err => {
          res.status(422).send(err);
          throw err;
        });
    })
    .catch(err => {
      res.status(422).send(err);
      throw err;
    });
[
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 1",
      contactEmail: "email1@email.com",
      contactName: "Contact Name 1"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 2",
      contactEmail: "email2@email.com",
      contactName: "Contact Name 2"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
其结果如下:

[
  {
    _accountId: "5beee0966d17bc42501f1234",
    name: "Company Name 1",
    contactEmail: "email1@email.com",
    contactName: "contact Name 1"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _accountId: "5beee0966d17bc42501f1235",
    name: "Company Name 2",
    contactEmail: "email2@email.com",
    contactName: "contact Name 2"
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
Warranty.aggregate([
    {
      $match: {
        payStatus: "Invoiced Next Billing Cycle"
      }
    },
    {
      $group: {
        _id: "$_accountId",
        total: {
          $sum: "$warrantyFee"
        },
        lineItems: {
          $push: {
            _id: "$_id",
            jobsiteAddress: {
              $concat: [
                "$jobsiteAddressStreet",
                " ",
                "$jobsiteAddressCity",
                ", ",
                "$jobsiteAddressState",
                " ",
                "$jobsiteAddressZip"
              ]
            },
            warrantyFee: "$warrantyFee"
          }
        }
      }
    },
    {
      $project: {
        reason: "Warranties",
        total: "$total",
        lineItems: "$lineItems"
      }
    }
  ])
    .then(warranties => {
      console.log(warranties);
      Account.populate(warranties, {
        path: "_id",
        select: "contactName contactEmail name"
      })
        .then(warranties => {
          res.send(warranties);
        })
        .catch(err => {
          res.status(422).send(err);
          throw err;
        });
    })
    .catch(err => {
      res.status(422).send(err);
      throw err;
    });
[
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 1",
      contactEmail: "email1@email.com",
      contactName: "Contact Name 1"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  },
  {
    _id: {
      _id: "5bc39dfa331c0e2cb897b61e",
      name: "Company Name 2",
      contactEmail: "email2@email.com",
      contactName: "Contact Name 2"
    },
    reason: "Warranties",
    total: 1152,
    lineItems: [
      {
        _id: "5beee0966d17bc42501f5086",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf43929e7179a56e21382bc",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      },
      {
        _id: "5bf4392fe7179a56e21382bd",
        jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
        warrantyFee: 384
      }
    ]
  }
]
正如你所看到的,这与一些小问题非常接近

  • 它显示的是_id而不是_accountId。我默认使用此选项,因为每当我尝试在$group中返回_accountId时,它会将其标记为非累加器字段,而当我在$project中执行此操作时,它就不会显示。数据集必须按照保修模型中的_accountId进行分组
  • 如果可能的话,我更愿意向顶层对象添加额外的字段(contactName、contactEmail、name),而不是创建子文档。这可能是简单的,也可能是不可能的,因为我对populate不太熟悉,但找不到任何东西可以直接回答我的问题
  • 最后的目标是获取返回的对象,并使用对象数组向另一个集合批量创建文档

    --对我的特定用例的回答--

    这给了我一个结果:

    [
      {
        lineItems: [
          {
            _id: "5be203eb3afd8098d4988152",
            jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
            warrantyFee: 384
          }
        ],
        reason: "Warranties",
        total: 384,
        type: "Invoice",
        date: "2018-11-21T14:08:15.052Z",
        company: "Company Name 1",
        contactName: "Contact Name 1",
        contactEmail: "email1@email.com",
        _accountId: "5be203eb3afd8098d4988152",
        referenceNumber: 1542809296615
      },
      {
        lineItems: [
          {
            _id: "5beee0966d17bc42501f5086",
            jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
            warrantyFee: 384
          },
          {
            _id: "5bf43929e7179a56e21382bc",
            jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
            warrantyFee: 384
          },
          {
            _id: "5bf4392fe7179a56e21382bd",
            jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
            warrantyFee: 384
          }
        ],
        reason: "Warranties",
        total: 1152,
        type: "Invoice",
        date: "2018-11-21T14:08:15.052Z",
        company: "Company Name 2",
        contactName: "Contact Name 2",
        contactEmail: "email2@email.com",
        _accountId: "5bc39dfa331c0e2cb897b61e",
        referenceNumber: 1542809295680
      }
    ]
    
  • 它在_accountId的后面显示_id,因为当您使用$group时 结果按指定的_accountId分组,因此它成为 文档的新id
  • 有两种可能的解决方案可以将contactName、contactEmail和name移动到顶层:
    • 一种是在它被填充后用javascript处理它。要实现这一点,可以使用函数“map()”
    • 另一种解决方案是在聚合管道中使用$lookup来填充同一mongoDB查询中的文档,在$lookup之后,必须再次使用$project来根据需要构建输出文档

  • 因此,当您要求对聚合结果进行“填充”时,实际上您缺少了一些概念。通常,这不是您实际要做的,而是为了解释以下要点:

  • aggregate()
    的输出不同于
    Model.find()
    或类似操作,因为此处的目的是“重塑结果”。这基本上意味着作为聚合源使用的模型不再被视为输出上的模型。即使在输出时仍然保持完全相同的文档结构,这也是正确的,但在您的情况下,输出显然与源文档不同

    无论如何,它不再是您所采购的
    保修
    模型的实例,而只是一个普通对象。我们可以在稍后讨论时解决这个问题

  • 这里的要点可能是,
    populate()
    无论如何都有点“老掉牙”。这实际上只是在Mongoose实现的早期添加到Mongoose中的一个方便功能。它真正做的只是对单独集合中的相关数据执行“另一个查询”,然后将内存中的结果合并到原始集合输出

    由于很多原因,在大多数情况下,这并不是真正有效的,甚至是不可取的。与流行的误解相反,这不是实际上的“加入”

    对于真正的“连接”,您实际上使用了聚合管道阶段,MongoDB使用该阶段从另一个集合返回匹配项。与
    populate()。这避免了网络开销,通常更快,因为“真正的连接”允许您执行
    populate()
    无法执行的操作

  • 改用$lookup 这里缺少的是一个非常快速的版本,它不是在
    中尝试
    填充()
    。然后()
    返回结果后,您要做的是将添加到管道中:

      { "$lookup": {
        "from": Account.collection.name,
        "localField": "_id",
        "foreignField": "_id",
        "as": "accounts"
      }},
      { "$unwind": "$accounts" },
      { "$project": {
        "_id": "$accounts",
        "total": 1,
        "lineItems": 1
      }}
    
    注意,这里有一个约束,即数组的输出总是一个数组。只有一个或多个相关项作为输出获取并不重要。管道阶段将从当前文档中查找
    “localField”
    的值,并使用该值匹配指定的
    “foreignField”
    中的值。在这种情况下,它是从聚合目标到外部集合的
    \u id
    \u id

    由于输出始终是前面提到的一个数组,因此在这个实例中使用该方法的最有效方法是直接在后面添加一个stage。所有这些都将为目标数组中返回的每个项返回一个新文档,在本例中,您希望它是一个。如果
    \u id
    在外部集合中不匹配,则将删除不匹配的结果

    请注意,这实际上是一个优化模式,如核心文档中所述。这里发生了一件特殊的事情,指令实际上以一种有效的方式合并到操作中。你可以在那里了解更多

    使用填充 从以上内容中,您应该能够基本了解为什么
    populate()
    这里是错误的做法。除了输出不再由
    保修
    模型对象组成这一基本事实之外,该模型实际上只知道
    \u accountId
    属性中描述的外来项,而这些外来项在输出中并不存在

    现在您可以实际定义一个模型,该模型可用于显式地将输出对象强制转换为已定义的输出类型。一个简短的演示将涉及向您的应用程序中添加以下代码:

    // Special models
    
    const outputSchema = new Schema({
      _id: { type: Schema.Types.ObjectId, ref: "Account" },
      total: Number,
      lineItems: [{ address: String }]
    });
    
    const Output = mongoose.model('Output', outputSchema, 'dontuseme');
    
    然后可以使用这个新的
    输出
    模型,将生成的普通JavaScript对象“强制转换”到Mongoose文档中,这样就可以实际调用以下方法:

    // excerpt
    result2 = result2.map(r => new Output(r));   // Cast to Output Mongoose Documents
    
    // Call populate on the list of documents
    result2 = await Output.populate(result2, { path: '_id' })
    log(result2);
    
    因为
    Output
    定义了一个模式,该模式知道“ref