Mongoose 模式:验证唯一字段,不区分大小写

Posted

技术标签:

【中文标题】Mongoose 模式:验证唯一字段,不区分大小写【英文标题】:Mongoose schema: Validating unique field, case insensitive 【发布时间】:2012-12-09 02:53:08 【问题描述】:

我有一个像这样的userSchema

var userSchema = new Schema(
    name: 
      type: String
    , required: true
    , validate: [validators.notEmpty, 'Name is empty']
    
  , username: 
      type: String
    , required: true
    , unique: true
    , validate: [validators.notEmpty, 'Username is empty']
    
);

username 字段应该是唯一的。如果数据库中已经存在此用户名,Mongoose 将抛出错误。但是,它不区分大小写,我需要它。

我是否认为实现不区分大小写的唯一检查的唯一方法是编写我自己的验证规则,该规则将对集合执行查询?是否可以编写这样的验证检查,创建更多与集合的连接?我也需要为email 做类似的事情。

【问题讨论】:

【参考方案1】:

...在 NodeJS 上使用 mongoose 进行查询:

const countryName = req.params.country;

 'country': new RegExp(`^$countryName$`, 'i') ;

const countryName = req.params.country;

 'country':  $regex: new RegExp(`^$countryName$`), $options: 'i'  ;

// ^australia$

const countryName = req.params.country;

 'country':  $regex: new RegExp(`^$countryName$`, 'i')  ;

// ^turkey$

一个完整的 Javascript 代码示例,NodeJS 和 Mongoose ORM 在 MongoDB 上

// get all customers that given country name
app.get('/customers/country/:countryName', (req, res) => 
    //res.send(`Got a GET request at /customer/country/$req.params.countryName`);

    const countryName = req.params.countryName;

    // using Regular Expression (case intensitive and equal): ^australia$

    // const query =  'country': new RegExp(`^$countryName$`, 'i') ;
    // const query =  'country':  $regex: new RegExp(`^$countryName$`, 'i')  ;
    const query =  'country':  $regex: new RegExp(`^$countryName$`), $options: 'i'  ;

    Customer.find(query).sort( name: 'asc' )
        .then(customers => 
            res.json(customers);
        )
        .catch(error => 
            // error..
            res.send(error.message);
        );
);

【讨论】:

只是想说正则表达式查询不被任何人推荐,包括 mongodb 自己。【参考方案2】:

我在做一个项目时遇到了同样的问题。我只是用两行代码让它变得简单。将所有传入的值转换为小写字母。

let getUsername = req.body.username;
let username = getUsername.toLowerCase();

【讨论】:

【参考方案3】:

索引上带有strength: 2 的collation 解决了这个问题。

index: 
  unique: true,
  collation: 
    locale: 'en',
    strength: 2
  

将其放置在您的架构创建代码中,如下所示:

var userSchema = new Schema(
  ...
  username: 
    type: String,
    required: true,
    index: 
      unique: true,
      collation:  locale: 'en', strength: 2 
    
);

注意:确保模型上的索引得到更新 - 您可能需要手动执行此操作。

【讨论】:

如果您使用 MongoDB 3.4+ 运行 Mongoose,这是正确的方法。 您能否编辑您的答案以解释猫鼬模型中的“排序规则”和“索引”是什么? @ArturTagisow 排序规则是 MongoDB 中的一项功能,它允许一些普遍接受的方法将不同的字符视为同一个字符。例如 e 和 é。强度是 5 分中的一个数字,它决定了比较的“严格性”。任何超过 3 的数字都适用于特定语言的罕见情况。语言环境是定义哪些字符被视为相同的规则集。您可以在这里了解更多信息:thecodebarbarian.com/…【参考方案4】:

我用mongoose-unique-validator

例子:

const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const  Schema  = mongoose;

const UserSchema = new Schema(
  name: 
    type: String,
    required: true,
    unique: true,
    index: true,
    maxlength: 100,
    trim: true,
    uniqueCaseInsensitive: true
  ,
  username: 
    type: String,
    required: true,
    unique: true,
    index: true,
    maxlength: 100,
    trim: true,
    uniqueCaseInsensitive: true
  
);

UserSchema.plugin(uniqueValidator, 
  message: 'Error, expected PATH to be unique.'
);

module.exports = mongoose.model('User', UserSchema);

【讨论】:

【参考方案5】:

最好的方法是使用下面共享的现有 npm 包。 https://www.npmjs.com/package/mongoose-unique-validator

为了区分大小写,您可以在同一页面中关注uniqueCaseInsensitive。

当已经有可用的包时,无需编写自己的验证逻辑(也可以关注Avinash's post)。

【讨论】:

【参考方案6】:

非常简单的解决方案

username : 
        trim:true,
        //lowercase:true,

        type:String,
        required:[true, 'PATH is required.'],
        match : [
            new RegExp('^[a-z0-9_.-]+$', 'i'),
            'PATH \'VALUE\' is not valid. Use only letters, numbers, underscore or dot.'
        ],
        minlength:5,
        maxlength:30,
        //unique:true

        validate : [
            function(un, cb)
                console.log(v);
                student.findOne(username:/^un$/i, function(err, doc)
                    if(err) return console.log(err);
                    if(!_.isEmpty(doc)) return cb(false);
                    return cb(true);
                );
            ,
            'Username already exists.'
        ]
    ,

在这里,我使用异步验证并检查我的模型student(如果存在相同的字段)。如果你愿意,显然可以使用正则表达式。

但我不推荐这种方法,它不适合我的想法。

而是坚持使用 type: String, lowercase: true, trim: true, unique:true 方法并将原始用户名复制到其他字段以备不时之需。

【讨论】:

【参考方案7】:

我不知道你是否在 node.js 中这样做。但是您可以使用这样的 npm:https://github.com/blakehaswell/mongoose-unique-validator 来检查集合字段的唯一验证。其他方式可能是每次有新请求到来时检查集合。 http://timstermatic.github.io/blog/2013/08/06/async-unique-validation-with-expressjs-and-mongoose/ 您可以参考这里的材料并根据您的情况使用它。

【讨论】:

虽然您提供的链接可能会回答问题,但最好将解决方案的基本部分直接放在 Stack Overflow 答案中,以防链接将来过期。 mongoose-unique-validator 和 timstermatic 博客中的技术都存在根本缺陷,因为它们在插入/更新之前执行单独的查询来检查唯一性,因此如果在验证器运行后运行另一个插入/更新,两者都会被插入。见github.com/blakehaswell/mongoose-unique-validator#caveats。保证唯一性的唯一方法是在数据库中使用唯一索引。【参考方案8】:

使用正则表达式怎么样?

var pattern = [ /some pattern/, "VALUE is not a valid user name!" ];

 type: String, match: pattern 

更多参考:http://mongoosejs.com/docs/api.html#schematype_SchemaType-required

【讨论】:

【参考方案9】:

如何使用:

 type: String, lowercase: true, trim: true 

达到你的目的?

【讨论】:

不会强制用户名为小写吗?我希望用户名检查不区分大小写,而不强制用户名使用小写用户名。 这个技巧将使您免于对数据库进行额外的查询。但要将用户名保留在原始大小写中,您可以有一个单独的字段,您不用于唯一性。 很好的建议,但似乎有点老套。做更多的额外查询是否昂贵?请记住,我需要对其他字段进行类似检查。 这是迄今为止我能找到的最佳解决方案:f***osoriani.wordpress.com/2012/03/22/…。 真的没有办法解决它(如果你要使用 MongoDB)。如果您希望唯一性不区分大小写,但希望用户能够使用他们想要的 WhatTeVER 大小写,则需要两个字段。我为用户名、电子邮件地址、URL 执行此操作。

以上是关于Mongoose 模式:验证唯一字段,不区分大小写的主要内容,如果未能解决你的问题,请参考以下文章

如何使字段不区分大小写且唯一?

mongoose 查询 $in 不区分大小写的正则表达式不起作用

Ruby Sequel.migration 中不区分大小写的唯一性验证

如何在 C# 中添加不区分大小写的表单字段验证?

从 Mongoose 获取模式中设置为唯一的字段

如何实现 Django 不区分大小写的模型字段?