Mongoose - 子文档验证不起作用

9

我有一个如下所示的模式:

var minderSchema = mongoose.Schema({
  phones: {type: [{
    details: {
      type: {
        country_code: { type: String, required: true },
        region_code: { type: String, required: true },
        number: { type: Number, required: true }
      },
      required: true
    },
  }], required: true},
})

这是关于IT技术的内容。一个 minder 是由一系列电话组成(必须有)。每个电话必须有国家代码、地区代码和号码。但验证似乎不起作用。也就是说,我可以创建以下内容:

var minder = new Minder({
  "phones": [{
     details: {
        number: "3343434"
     }
   }]
});

这应该无法正常工作,因为缺少国家代码和地区代码。事实上,我可以创建这样一个文档:

var minder = new Minder({
  "phones": [{
    details: {
      "sdf":"sdf"
    }
  }]
});

并且它是有效的。

我缺少什么概念?


文档将在插入时进行检查,而不是在创建时进行检查。您是否成功将这些文档插入数据库? - Andrei Beziazychnyi
是的,我做了。这很奇怪。 - Dominic Bou-Samra
2个回答

14

这里的问题主要在于您如何构建“details”入口。所以尽管您认为自己已经完成了,实际上这里存在的条目类型是一个简单的子文档或者通常被称为“哈希/映射或字典”的东西,具体由您熟悉哪个术语而定。

它们在任何情况下都不是严格意义上的“类型化”,因此对于您放置在其中的“键”没有真正的控制。因此,您可能需要的是一些可以以严格类型化方式结构化的内容,例如:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');

var phonesSchema = new Schema({
  country_code: { type: String, required: true },
  region_code: { type: String, required: true },
  number: { type: String, required: true }
});

var minderSchema = new Schema({

  phones:[phonesSchema]

});


var Minder = mongoose.model( 'Minder', minderSchema );

var minder = new Minder({
  "phones": [{ "number": "12345", "bill": "45678" }]
});

console.log( JSON.stringify( minder, undefined, 2 ) );


minder.save();

这不仅可以将模式定义(更方便和清晰),而且现在您拥有一个明确定义的“子类型”,可以在其中执行条目的验证。如有需要,您可以进行扩展,但我通常认为这是更干净的形式。

这里最后一个重要的观点是要意识到“验证”实际上发生在哪里。因此,从您的示例中,您只是创建实例,但这并不是验证发生的地方。这实际上只有在对象实例被“保存”并持久化到存储时才会发生。这使您能够“构建”对象,但不是传统“类”意义上的严格的“验证器”。

运行上面的片段,您会得到此输出:

{ _id: 537d7c71d4d04b65174d0c00,
  phones: [ { number: '12345', _id: 537d7c71d4d04b65174d0c01 } ] }

events.js:72
        throw er; // Unhandled 'error' event
          ^
No listeners detected, throwing. Consider adding an error listener to your connection.
ValidationError: Path `region_code` is required., Path `country_code` is required.
    at model.Document.invalidate (/home/neillunn/node_modules/mongoose/lib/document.js:1009:32)
    at EmbeddedDocument.invalidate (/home/neillunn/node_modules/mongoose/lib/types/embedded.js:178:19)
    at /home/neillunn/node_modules/mongoose/lib/document.js:958:16
    at validate (/home/neillunn/node_modules/mongoose/lib/schematype.js:610:7)
    at /home/neillunn/node_modules/mongoose/lib/schematype.js:627:9
    at Array.forEach (native)
    at SchemaString.SchemaType.doValidate     (/home/neillunn/node_modules/mongoose/lib/schematype.js:614:19)
    at /home/neillunn/node_modules/mongoose/lib/document.js:956:9
    at process._tickCallback (node.js:419:13)
    at Function.Module.runMain (module.js:499:11)

注意到这里的“日志”输出保留了“有效”的条目,但丢弃了未定义的字段,然后“验证”只在对象实际尝试“保存”时针对所需字段进行。

因此,请考虑您的结构以及实际发生的验证情况。尝试添加未定义的字段不会出错,它只会被丢弃。省略“必需”的字段仅在对象持久化时检查,这使您有时间构建它。这些不是必需的“类构造函数”类型参数,而是为其他目的而设计的。


如果您真的想要嵌套,则可将“type”声明删除,如下:

var phonesSchema = new Schema({
  details: {
    country_code: { type: String, required: true },
    region_code: { type: String, required: true },
    number: { type: String, required: true }
  }
});

验证将对您起作用:

{
  "_id": "537d9e6d5b433f8745547f52",
  "phones": [
    {
      "_id": "537d9e6d5b433f8745547f53",
      "details": {
        "number": "12345"
      }
    }
  ]
}

events.js:72
    throw er; // Unhandled 'error' event
          ^
No listeners detected, throwing. Consider adding an error listener to your connection.
ValidationError: Path `details.region_code` is required., Path `details.country_code` is required.

非常好的答案。感谢您抽出时间来回答。我知道我可以做你上面的例子,但它与我想要的不同。它只是一个数组中的子文档模式,即phones:[phonesSchema]。我想要的是phonesSchema也包含不在数组内的子文档。目前,我已经决定将国家代码等放在与号码相同的级别上。 - Dominic Bou-Samra
@DominicBou-Samra 可能最初在答案中应该对此进行更详细的解释。请参见末尾的附加细节。 - Neil Lunn

0
在我的情况下,我使用了mongoose-unique-validator模块来处理嵌套文档,但您也可以使用SchemaName.path("key.nestedKey").validate(argument => {}))。问题在于在update操作时,Mongoose仅解析查询并运行而无需验证,要进行验证,您可以将第三个参数传递为{runValidators:true}

验证器在更新时存在一些注意事项。盲目地打开它并不是一个好主意。此外,这个问题与“唯一”值无关。 - Jack_Hu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接