【问题标题】:MongooseJS - Insert Subdocument without Validation on DocumentMongooseJS - 插入子文档而不验证文档
【发布时间】:2021-03-08 19:06:42
【问题描述】:

我正在使用 Mongoose 和 Javascript (NodeJS) 来读取/写入 MongoDB。我有一个文档(Parent),里面有一堆子文档(Children)。我的文档和子文档都在其模型中定义了验证(required: true 和一个验证用户是否将文本放入字段中的函数)。

当尝试将新的子文档推送到数据库时,Mongoose 拒绝了我的推送,因为对文档的验证失败。这让我感到困惑,因为我没有尝试使用子文档创建新文档,我只是尝试将新的子文档推送到现有文档中。

这是我的(示例)猫鼬模型:

const mongoose = require('mongoose');

const requiredStringValidator = [
  (val) => {
    const testVal = val.trim();
    return testVal.length > 0;
  },
  // Custom error text
  'Please supply a value for {PATH}',
];
const childrenSchema = new mongoose.Schema({
  childId: {
    type: mongoose.Schema.Types.ObjectId,
  },
  firstName: {
    type: String,
    required: true,
    validate: requiredStringValidator,
  },
  lastName: {
    type: String,
    required: true,
    validate: requiredStringValidator,
  },
  birthday: {
    type: Date,
    required: true,
  },
});
const parentSchema = new mongoose.Schema(
  {
    parentId: {
      type: mongoose.Schema.Types.ObjectId,
    },
    firstName: {
      type: String,
      required: true,
      validate: requiredStringValidator,
    },
    lastName: {
      type: String,
      required: true,
      validate: requiredStringValidator,
    },
    children: [childrenSchema],
  },
  { collection: 'parentsjustdontunderstand' },
);
const mongooseModels = {
  Parent: mongoose.model('Parent', parentSchema),
  Children: mongoose.model('Children', childrenSchema),
};
module.exports = mongooseModels;

我可以通过以下 MongoDB 命令成功地将新的 Child 子文档推送到 Parent 文档中:

db.parentsjustdontunderstand.update({
    firstName: 'Willard'
}, {
    $push: {
        children: {
    "firstName": "Will",
    "lastName": "Smith",
    "birthday": "9/25/1968"        }
    }
});

但是,当我按照 Mongoose 文档 Adding Subdocs to Arrays 并尝试通过 Mongoose 添加它时,它失败了。

出于测试目的,我正在使用 Postman 并对端点执行 PUT 请求。 以下为req.body

{
    "firstName": "Will",
    "lastName": "Smith",
    "birthday": "9/25/1968"
}

我的代码是:

const { Parent } = require('parentsModel');
const parent = new Parent();
parent.children.push(req.body);
parent.save();

我得到的是:

ValidationError: Parent validation failed: firstName: Path `firstName` is required...`

它列出了所有文档的验证要求。

我可以在我做错的事情上寻求帮助。作为记录,我在 Stackoverflow 上查看了这个答案:Push items into mongo array via mongoose,但我看到的大多数示例都没有在他们的 Mongoose 模型中展示或讨论验证。

编辑 1

根据@jf 的反馈,我将代码修改为以下内容(将正文移出req.body 并在代码中创建它以用于测试目的。当我尝试以推荐的方式推送更新时,记录被插入,但是,我仍然收到向控制台抛出的验证错误:

const parent = await Parent.findOne({firstName: 'Willard'});
const child = {
  children: {
      "firstName": "Will",
      "lastName": "Smith",
      "birthday": "9/25/1968"
  }
}
parent.children.push(child);
parent.save();
ValidationError: Parent validation failed: children.12.firstName: Path `firstName` is required., children.12.lastName: Path `lastName` is required., children.12.birthday: Path `birthday` is required.

【问题讨论】:

  • 您正在创建一个空的 Parent 并尝试保存到数据库中。创建的父对象不需要任何属性(如firstName),是一个空对象,只有属性children,这就是失败。

标签: javascript node.js mongodb mongoose


【解决方案1】:

回答

@J.F 是对的,我错了。

这是不正确

const child = {
  children: {
      "firstName": "Will",
      "lastName": "Smith",
      "birthday": "9/25/1968"
  }
}

这是正确

const child = {
    "firstName": "Will",
    "lastName": "Smith",
    "birthday": "9/25/1968"
}

记录被插入数据库并保存,但由于我将其作为 PUT 请求启动,因此在成功保存后我没有正确响应 HTTP 200 OK。下面是整个解决方案的正确代码,但是请记住,res.status 代码仅在这种情况下是必需的,因为我是通过 PUT 请求模仿代码。

猫鼬模型:

const mongoose = require('mongoose');

const requiredStringValidator = [
  (val) => {
    const testVal = val.trim();
    return testVal.length > 0;
  },
  // Custom error text
  'Please supply a value for {PATH}',
];
const childrenSchema = new mongoose.Schema({
  childId: {
    type: mongoose.Schema.Types.ObjectId,
  },
  firstName: {
    type: String,
    required: true,
    validate: requiredStringValidator,
  },
  lastName: {
    type: String,
    required: true,
    validate: requiredStringValidator,
  },
  birthday: {
    type: Date,
    required: true,
  },
});
const parentSchema = new mongoose.Schema(
  {
    parentId: {
      type: mongoose.Schema.Types.ObjectId,
    },
    firstName: {
      type: String,
      required: true,
      validate: requiredStringValidator,
    },
    lastName: {
      type: String,
      required: true,
      validate: requiredStringValidator,
    },
    children: [childrenSchema],
  },
  { collection: 'parentsjustdontunderstand' },
);
const mongooseModels = {
  Parent: mongoose.model('Parent', parentSchema),
  Children: mongoose.model('Children', childrenSchema),
};
module.exports = mongooseModels;

以下是req.body

{
    "firstName": "Will",
    "lastName": "Smith",
    "birthday": "9/25/1968"
}

代码是:

const { Parent } = require('parentsModel');
const parent = await Parent.findOne({firstName: 'Willard'});
parent.children.push(req.body);
parent.save((err, doc) => {
  if (err) {
    res.status(500).json({
      message: 'Error finding active projects',
      error: err,
    });
  } else {
    res.status(200).json(doc);
  }
});

【讨论】:

    【解决方案2】:

    您可以使用 mongo 查询将子代推送到父代中,因为在 update 中,第一个目标是找到执行推送的文档。

    语法类似于:update({query},{update},{options})。因此,您正在寻找带有firstName: 'Willard' 的文档并将子代添加到其中。

    这里一切正常,所有字段都存在,父级存在于集合中,所以没有问题。

    但是使用

    const parent = new Parent();
    parent.children.push(req.body);
    parent.save();
    

    你的父对象是空的(除非构造函数填充所有字段,但我认为这不是一个好主意)。

    如果你试试这个:

    var parent = await model.findOne({firstName: 'Willard'})
    parent.children.push(req.body);
    parent.save();
    

    那么应该可以了。

    在这种情况下,对象parent 是从集合中检索的,因此它包含所有必要的字段。

    我将进行编辑以更好地解释为什么这两个查询不一样。

    基本上,您要保存的子对象与db.collection.update 的结构不同。请注意,您创建并插入到集合中的对象child 只有一个名为children 的属性。它没有像firstName这样的必要属性...

    我将使用纯 JS 来查看 console.log() 的输出是什么,并查看差异。

    你的mongo查询推送一个像这样的对象(翻译成js语言):

    var array = []
    array.push(
        children = {
            "firstName": "Will",
            "lastName": "Smith",
            "birthday": "9/25/1968"
        }
    )
    
    console.log(array)

    但是您正在以这种方式创建对象:

    const child = {
      children: {
          "firstName": "Will",
          "lastName": "Smith",
          "birthday": "9/25/1968"
      }
    }
    
    console.log(child)

    你现在看到区别了吗?一个对象是子对象本身,另一个对象具有属性 children 以及必要的字段。

    那么让我们把这两段代码组合起来吧:

    const child = {
      children: {
          "firstName": "Will",
          "lastName": "Smith",
          "birthday": "9/25/1968"
      }
    }
    
    const children = {
            "firstName": "Will",
            "lastName": "Smith",
            "birthday": "9/25/1968"
        }
    
    var array = [child,children]
    console.log(array)

    因此,对于您的代码,如果您使用:

    parent.children.push(child.children);
    parent.save();
    

    应该有效。但是,最好的方法不是在const child 内创建对象children

    尝试使用:

    const child = {
      "firstName": "Will",
      "lastName": "Smith",
      "birthday": "9/25/1968"
    }
    parent.children.push(child);
    parent.save();
    

    【讨论】:

    • 我根据您的反馈修改了上面的问题。记录将被插入,但我在控制台中收到一个关于验证仍然失败的错误。
    • 您正在将对象child 创建为children 对象,但您的架构不是这样。属性应为children。没有必要在里面创建对象child
    • 我正在以与“db.collection.update”命令中的结构相同的方式构建“子”对象。我尝试在没有根“children”对象的情况下构建“child”。代码坐在那里,什么也不做。没有错误,也没有超时,只是坐着。
    • 你是对的,J.F. 我是如何启动代码的,我没有以 200K 响应,这就是代码似乎“卡住”的原因。我提供了包含该信息的答案,并将您的答案标记为已接受/正确(因为它是)。感谢您的帮助!
    • 不客气!很高兴知道问题解决了
    猜你喜欢
    • 2020-06-30
    • 2017-08-12
    • 2012-12-07
    • 2017-07-25
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    相关资源
    最近更新 更多