简体   繁体   English

在父文档数组猫鼬中填充子文档

[英]Populate subdocument in parent document Array Mongoose

I'm very new to JavaScript and Mongoose. 我是JavaScript和Mongoose的新手。 I'm building a small project using express, mongoose and node.js. 我正在使用express,mongoose和node.js构建一个小项目。 I have a mongoose model - Client that has an Array of Transactions 我有一个猫鼬模型-具有交易数组的客户端

    var Client = mongoose.model('Client', {
 name: {
   type: String,
   required: true,
   minlength: 1
 },
 email: {
   type: String
 },
 phone: {
   type: Number
 },
 createdAt: {
   type: Number,
   default: null
 },
 transactions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Transaction' }],
 _creator: {
   type: mongoose.Schema.Types.ObjectId,
   required: true
 }
});

module.exports = {Client};

This is Transaction model: 这是交易模型:

var Client = require('./client');

var Transaction = mongoose.model('Transaction',{
  _creator : { type: mongoose.Schema.Types.ObjectId, ref: 'Client' },
  amount : {type: Number, min: 0},
  date : {type: Number,default: null},
  total: {type: Number,default: null}
});

module.exports = {Transaction};

When I POST a new Transaction it goes through and saves in db: 当我发布新交易时,它将通过并保存在db中:

 app.post('/clients/:id/transactions', authenticate, (req, res) => {
  var id = req.params.id;
  var transaction = new Transaction({
    amount: req.body.amount,
    date: new Date().getTime(),
    total: req.body.total,
    _creator: req.params.id
  })
  if (!ObjectID.isValid(id)) {
  return res.status(404).send();
  }

  transaction.save().then((doc) => {

    Client.findOneAndUpdate({
      _id: id,
      _creator: req.user._id,
      transactions: req.body.transaction
    });

    res.send(doc);
  }, (e) => {
    res.status(400).send(e);
  });

});

I am also able to GET all the transactions associated with the client: 我还能够获取与客户关联的所有交易:

  app.get('/clients/:id/transactions', authenticate, (req, res) => {
  var id = req.params.id;
  if (!ObjectID.isValid(id)) {
  return res.status(404).send();
 }
 Transaction.find({
   _creator: id
 }).then((transactions) => {
  res.send({transactions});
 }).catch((e) => {
  res.status(400).send();
 });
});

But when I make a GET call to '/clients' - Array of Transactions is empty: 但是当我对'/ clients'进行GET调用时-事务数组为空:

    {
  "clients": [
    {
      "_id": "1095d6de3867001108b803",
      "name": "Peter",
      "email": "peter@gmail.com",
      "phone": 1232321,
      "_creator": "5321df6d57868ec7001108b801",
      "__v": 0,
      "transactions": [],
      "createdAt": null
    } ]
}

And this is the GET call to /clients 这是对/ clients的GET调用

    app.get('/clients', authenticate,  (req, res) => {
  Client.find({
    _creator: req.user._id,
  })
  .populate('transactions.transaction')
  .then((clients) => {
    res.send({clients});
  }, (e) => {
    res.status(400).send(e);
    console.log('Unable to get clients', e);
  })
});

I know that I'm likely doing something completely wrong but I don't know where I need to look for my mistake. 我知道我可能在做完全错误的事情,但是我不知道该在哪里寻找错误。 Please help! 请帮忙!

so first of all i don't exactly know what _creator key in Client model representing, it's probably user identifier who has some clients, but if I'm wrong please correct me. 因此,首先我不完全了解Client模型中代表的_creator键,可能是拥有一些客户端的用户标识符,但是如果我错了,请更正我。

Honestly I don't know why you are making two way document connection, (keeping client in transactions, and also keeping transactions in clients) in my opinion first option is better for mongodb and using that you can easily get transaction's list with find, or mongodb aggregation, but you can't get data using populate . 老实说,我不知道您为什么要进行双向文档连接(将客户保留在事务中,还将客户保留在事务中)对于mongodb来说,第一种选择更好,使用它可以轻松地通过find找到交易的清单,或者mongodb聚合,但是您无法使用populate获取数据。

In second option you need to remember that one document could have maximum 16MB. 在第二个选项中,您需要记住一个文档最大可以有16MB。 And also keeping thousands of transactions in one array is not well for performance. 而且将成千上万个事务保存在一个阵列中对性能而言也不是很好。 Think about example that you have 5000 transaction and you want to show list with pagination (50 records per page), with array option you have to get whole document, and splice array to 50 records. 考虑一个示例,您有5000个事务,并且想要显示带有分页的列表(每页50条记录),使用数组选项必须获取整个文档,并将数组拼接为50条记录。 In first option you could use mongodb skip and limit. 在第一个选项中,您可以使用mongodb跳过和限制。 Please think about it. 请考虑一下。

Returning to question, mistake you are doing is here: transaction.save().then((doc) => { 回到问题,您正在做的错误在这里:transaction.save()。then((doc)=> {

Client.findOneAndUpdate({
  _id: id,
  _creator: req.user._id,
  transactions: req.body.transaction
});

res.send(doc);

Here you don't exactly say how this document should have to updated about. 在这里,您并没有完全说出应如何更新本文档。

Mongoose in method findOneAndUpdate using mongodb findAndModify method. 使用mongodb findAndModify方法的findOneAndUpdate方法中的猫鼬。 But params are used from update. 但是参数是从更新开始使用的。 https://docs.mongodb.com/manual/reference/method/db.collection.update/ https://docs.mongodb.com/manual/reference/method/db.collection.update/

And also documentation says that you what params like: Query#findOneAndUpdate([query], [doc], [options], [options.passRawResult], [options.strict], [callback]) 并且文档还说您喜欢什么参数:Query#findOneAndUpdate([query],[doc],[options],[options.passRawResult],[options.strict],[callback])

So first query param is mongo query to find one document in database, next param is object with updating query, and after that you could send some additional options in third param. 因此,第一个query参数是mongo查询,用于在数据库中查找一个文档,第二个参数是具有更新查询的对象,此后,您可以在第三个参数中发送一些其他选项。 So your code should looks like this: 因此,您的代码应如下所示:

transaction.save().then((doc) => {

Client.findOneAndUpdate({
  _id: id,
  _creator: req.user._id,
}, {
    $addToSet:  {
        transactions: doc._id,
    }
});

res.send(doc);

You could use addToSet or push both are putting element into array, but addToSet avoiding duplicates in array. 您可以使用addToSet或同时push两者都放入数组中,但是addToSet避免在数组中重复。 And as you se we push new transaction identifier into this array. 正如您所了解的,我们将新的交易标识符推入该数组。 And after all you only populate transaction key. 毕竟,您只填充transaction密钥。

I hope I helped. 希望我能帮上忙。 If you have any questions please ask. 如果您有任何问题,请询问。

I would check if the client exist before adding a transaction. 在添加事务之前,我将检查客户端是否存在。 A transaction needs a client first. 交易首先需要客户。

Forewarn, I'm not a fan of then and catch so this answer does not use it. 预先警告,我不是一个球迷thencatch所以这个答案不使用它。 I normally use async.js when dealing with multiple asynchronous operations. 我通常在处理多个异步操作时使用async.js。

Anyways, I would do it like 无论如何,我会这样做

app.post('/clients/:id/transactions', authenticate, (req, res) => {


    Client.findOne({ _id: req.params.id }, (err, client) => {
        if (err)
            return res.status(400).send(err); 
        if (!client)
            return res.status(400).send(new Error('No client'));


        Transaction.create({
            amount: req.body.amount,
            date: new Date(), // I don't think you need .getTime()
            total: req.body.total,
            _creator: client._id
        }, (err, transaction) => {
            if (err)
                return res.status(400).send(err);


            client.transactions.push(transaction._id);
            client.save(err => {
                if (err)
                    return res.status(400).send(err);

                res.json(transaction);
            });
        });
    });


});

Good idea to also turn on debugging mode to see your queries: mongoose.set('debug', true) . 最好还打开调试模式以查看查询: mongoose.set('debug', true)

You might also find using timestamps option for Transaction schema more useful than explicitly using date field 您可能还会发现,对事务模式使用timestamps选项比显式使用date字段更有用

To get clients with their transactions 获得客户的交易

app.get('/clients', authenticate,  (req, res) => {
    Client.find({ _creator: req.user._id }).populate('transactions').exec((err, clients) => {
        if (err)
            return res.status(400).send(err);
        res.json(clients);
    });
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM