猫鼬唯一索引无效!

我试图让 MongoDB 根据它的索引检测重复的值。我认为这在 MongoDB 中是可能的,但是通过 Mongoose 包装器,事情似乎被破坏了。所以像这样的东西:

User = new Schema ({
email: {type: String, index: {unique: true, dropDups: true}}
})

我可以用同样的电子邮件拯救2个用户。

同样的问题已经在这里表达: https://github.com/LearnBoost/mongoose/issues/56,但这个线程是旧的,并导致无处。

现在,我手动调用 db 来查找用户。这个调用并不昂贵,因为“电子邮件”是索引的。但是让它被本地化处理还是很好的。

有人能解决这个问题吗?

98112 次浏览

哎呀! 你只需要重新启动蒙戈。

哎呀! 你只需要重新启动蒙戈。

还要重新索引:

mongo <db-name>
> db.<collection-name>.reIndex()

在测试中,由于我没有重要的数据,你也可以这样做:

mongo <db-name>
> db.dropDatabase()

如果您在 Mongo 中留下了一些副本,也会发生这种行为。当您的应用程序启动时,Mongoose 将尝试在 Mongo 中创建它们。

为了防止这种情况,您可以这样处理这个错误:

yourModel.on('index', function(err) {
if (err?) {
console.error(err)
}
);

好的,我可以通过在字段上添加索引并设置惟一属性来解决 mongoshell 中的这个问题:

db.<collectionName>.ensureIndex({fieldName: 1}, {unique: true});

壳牌应该这样回应:

{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}

现在让我们快速测试一下蒙古贝壳:

var doc = {fieldName: 'abc'};
db.<collectionName>.insert(doc)

应给予: WriteResult ({“ nInserted”: 1})

但当我再次重复时:

db.<collectionName>.insert(doc)

将给予:

WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "insertDocument :: caused by :: 11000 E11000 duplicate key error index: fuelConsumption.users.$email_1  dup key: { : \"martyna@martycud.com\" }"
}
})

还可以通过删除索引来解决此问题;

假设您想从集合 users和字段 username中删除唯一索引,键入以下内容:

Droindex (‘ username _ 1’) ;

根据文件: https://docs.mongodb.com/v2.6/tutorial/modify-an-index/

要修改现有索引,需要删除并重新创建索引。

不要重启芒果!

1-放下收藏品

db.users.drop()

2-重新索引表

db.users.ensureIndex({email: 1, type: 1}, {unique: true})

我遇到了同样的问题: 在已经将用户添加到数据库之后,我将 email字段的唯一约束添加到了我们的 UserSchema,并且仍然能够用欺骗的电子邮件来保存用户。我通过以下方式解决了这个问题:

1)从用户集合中删除所有文档。

2)在 mongo shell 中执行以下命令: db.users.createIndex({email: 1}, {unique: true})

关于步骤1,请注意来自 Mongo 的文档:

如果集合已经包含违反索引的唯一约束的数据,MongoDB 无法在指定的索引字段上创建唯一索引。

Https://docs.mongodb.com/manual/core/index-unique/

猫鼬唯一验证器

如何使用这个插件:

1) npm install —— save monose-only-validator

2)在你的模式中遵循以下指导:

// declare this at the top
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');


// exampleSchema = mongoose.Schema({}) etc...


exampleSchema.plugin(uniqueValidator);


// module.exports = mongoose.model(...) etc....

3)猫鼬方法

当使用像 findOneAndUpdate这样的方法时,您需要传递这个配置对象:

{ runValidators: true, context: 'query' }

ie. User.findOneAndUpdate(
{ email: 'old-email@example.com' },
{ email: 'new-email@example.com' },
{ runValidators: true, context: 'query' },
function(err) {
// ...
}

4)其他选择

  1. 大小写不敏感

    在架构中使用 uniqueCaseInsense 选项

    ie. email: { type: String, index: true, unique: true, required: true, uniqueCaseInsensitive: true }

  2. 自定义错误消息

    ie. exampleSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });

现在您可以向模式添加/删除惟一属性,而无需担心重新启动 mongo、删除数据库或创建索引。

注意事项(来自医生) :

因为我们依赖于异步操作来验证数据库中是否存在文档,所以可以同时执行两个查询,它们都返回0,然后都插入 MongoDB。

除了自动锁定集合或强制执行单个连接之外,没有真正的解决方案。

对于我们的大多数用户来说,这不是一个问题,而是一个需要注意的边缘情况。

如果表/集合为空,则为字段创建惟一索引:

db.<collection_name>.createIndex({'field':1}, {unique: true})

如果表/集合不为空,则删除该集合并创建索引:

db.<collection_name>.drop()
db.<collection_name>.createIndex({'field':1}, {unique: true})

现在重启 mongoDB。

最新的答案是: 根本没有必要重启蒙戈德布, 如果 Colleciton 已经有相同的名称索引,猫鼬将不会重新创建 你的索引,所以,先删除 Colleciton 的现有索引, 现在,当你运行猫鼬时它会创建新的索引, 上述过程解决了我的问题。

当从应用程序级别强制执行独特的索引时,Mongoose 有点松散; 因此,它更喜欢要么使用 mongo cli 强制执行数据库本身的独特索引,要么通过在 UserSchema 之后写下下面一行代码来明确告诉猫鼬你对 unique索引是认真的:

UserSchema.index({ username: 1, email: 1 }, { unique: true});

这将在您的 UserSchema 中的 usernameemail字段上强制执行唯一索引。

如果在连接方法中使用如下 autoIndex: false选项:

mongoose.connect(CONNECTION_STRING, { autoIndex: false });

尝试删除它。如果这不起作用,尝试 重新启动 Mongodb,就像这个线程中建议的那样。

我做过这样的事:

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


const FooSchema = new Schema({
name: { type: String, required: true, index: true, unique: true }
});


const Foo = mongoose.model('Foo', FooSchema);


Foo.createIndexes();


module.exports = Foo

在代码运行时,我添加了 Foo.createIndexes()代码行 B.c. ,我得到了以下弃用警告:

(node:21553) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.

我不确定 Foo.createIndexes()是否是异步的,但 AFAIK 的东西似乎工作得很好

检查模式中的 autoIndex 是否为 true,当您使用 mongoose.connect 选项时,它可能设置为 false (默认为 true)

您可以将模式定义为

User = new Schema ({
email: {
type: String,
unique: true
}
})

但是,当已经有一个文档并且在此之后,您已经更改了 User 的模式时,这可能无法工作。如果不想删除此 User 集合,可以为该集合创建电子邮件索引。使用此命令,您可以在电子邮件上创建索引。

db.User.createIndex({email:1},{unique: true})

或者您可以删除该集合并再次添加用户。
若要删除集合,可以输入以下内容:

db.User.drop()

我面临同样的问题了一段时间,做了大量的搜索和解决方案为我是 createIndexes()的功能。

我希望这能有所帮助。

所以代码就是这样的。

User = new Schema ({
email: {type: String, unique: true}
});
User.createIndexes();

当我遇到这个问题时,我尝试删除数据库,多次重新启动服务器(非恶魔) ,没有一个技巧不起作用。我通过机器人3T 找到了以下工作:

  1. 在 Robo3T 中,双击数据库以打开集合
  2. 打开集合以显示有问题的集合。首先确保您的集合是空的
  3. 右键单击索引文件夹。默认情况下,您将看到默认的 _id_。现在,选择添加索引
  4. 选择一个名称,例如电子邮件字段的 email
  5. 提供密钥作为 JSON

    {
    "email": 1
    }
    
  6. Click on the Unique checkbox

  7. Save

This will make sure no duplicate emails are saved in the MongoDB.

确保您的集合没有刚刚放置唯一索引的字段的冗余。

然后重启你的应用程序(猫鼬)。 它只是默默地添加索引失败。

当以下两种情况发生时,Mongoose 将无法添加唯一索引:

  1. 集合已经具有相同名称的索引
  2. 该集合已经包含与索引字段重复的文档

在第一种情况下,使用 db.collection.getIndexes()列出索引,并使用 db.collection.dropIndex("index_name")删除旧索引。当您重新启动 Mongoose 应用程序时,它应该正确地添加新索引。

在第二种情况下,您需要在重新启动 Mongoose 应用程序之前删除重复项。

从集合中删除所有文件:

db.users.remove({})

正如其他人提到的,重新开始对我很有效

如果 MongoDB 是作为一个服务工作的(找到这个的简单方法是如果你不需要连接到数据库而不需要通过终端启动 mongod.exe 文件) ,那么在做了这些改变之后,你可能需要重新启动服务 及/或完全放下你的数据库。

这非常奇怪,因为对于一些用户来说,只要删除一个集合就可以工作。有些人只是需要删除数据库。但那些对我没用。我删除了数据库,然后重新启动 MongoDB 服务器。

要在 Windows 搜索栏上重新启动服务搜索服务,然后找到 MongoDB 服务,双击打开,然后停止并再次启动服务。

如果其他方法对你不起作用,我相信这个会起作用的。

对我来说猫鼬已经过时了,我在 CMD 上运行过时的 npm 来检查它。 和更新的猫鼬。

请告诉我这是否也适合你。

当您将数据库与应用程序连接时,请添加以下选项: “ audoIndex: true” 例如,在我的代码中,我这样做了:

const options = {
// your options go here
...
// this code is the solution
audoIndex: true
}
mongoose.connect(DB_URI, options);

我还放弃了收集,我有问题,并重新创建它,以确保它将工作。 我在 https://dev.to/emmysteven/solved-mongoose-unique-index-not-working-45d5找到了这个解决方案 我也尝试过像“重启 MongoDB”这样的解决方案,但是对我没有用。


解决问题的步骤:

1. 将 unique: true添加到属性中。

let schema = new mongoose.Schema(
{
name: {
type: String,
unique: true,
required: [true, "name required."],
}
}
);


module.exports = mongoose.model("role", schema);

2. 删除集合-例如 role(最后一行)

  • 这是一个简单的修复方法——如果您已经有了重复的值。
  • 还可以删除集合中的所有记录,以便存在唯一列的重复值(上面的 name)

3. 重新启动使用 mongoose库的 Node.js 服务器。


这里的其他一些答案怎么不正确呢?

  • autoIndex选项设置为 true

    • 不需要,默认情况下为真
  • 重启数据库

    • 不需要,只需重新启动 Node.js 服务器即可

  • 遵循以上3个步骤
    • 如果你错过了什么,做2次

在我的例子中,我们需要定义 schema.index 来创建索引。 请检查猫鼬文档索引 https://mongoosejs.com/docs/guide.html#indexes,

  • 注意,在对模式进行更改后,请记住重新启动服务器以进行检查。

现在看看下面的代码来进行测试:

const schemaUser = new mongoose.Schema(
{
username: {
type: String,
required: true,
index: true,
unique: true,
dropDups: true,
},
hash: String,
created: {
type: Date,
default: Date.now,
},
},
{
autoCreate: true, // auto create collection
autoIndex: true, // auto create indexes
}
)
// define indexes to be create
schemaUser.index({ username: 1 })


const User = mongoose.model('Users', schemaUser)
const newUser = new Users({ username: 'wintzer' })
newUser.save(function (err) {
if (err) console.log(err)
})

重新启动和使用插件对我来说不起作用,而且我们都确信 mongo 可以自己完成一些事情,所以使用插件有点过头了。

所以这里是修复。在您的连接函数添加到选项对象(第2参数)

const options = {
autoIndex: true, //this is the code I added that solved it all
}
mongoose.connect(process.env.MONGO_URI, options);

这是一个老问题,但对于仍然存在这个问题的人来说,你可能没有正确地应用索引:

如果你有 autoIndex在连接选项设置为 false,那么一个选项将是使它成为一个 true或删除这个属性,这将恢复到它的默认值是 true,但是,这不建议在生产中,因为它会导致性能的打击,更好的方法是显式调用 createIndexes对您的模型,这将正确地创建索引定义在您的模式。

因此,原问题中示例的语法可以如下:

const userSchema = new mongoose.Schema({
email: { type: String, required: true, index: true, unique: true },
// other fields
});


// methods, statics, hooks... etc


const User = mongoose.model("User", userSchema);


User.createIndexes();


module.exports = User;


在你的连接功能不要忘记提到 useCreateIndex: true

mongoose.connect(url, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
})

你实际上不需要使用‘ only: true’或 monose-only-validator 插件,你可以简单地在你的类型定义中使用一个自定义的使用 CountDocument ()的异步验证() :

在您的模式中... (假设您的模式是针对用户模型的)

email: {type: String, required: true, trim: true, lowercase: true, async validate(value) {
const count = await mongoose.models.User.countDocuments({email: value});
if (count > 0) {
const existing = await  mongoose.models.User.findOne({email: value});
if (!(existing._id.toString() === this._id.toString())) {
throw new Error("Email not unique");
}
}
}}

如果您没有指定自动索引数据 也就是说猫鼬不会这么做

只需在连接到数据库时将它们设置为 true

mongoose.connect('connection url', {
useUnifiedTopology: true,
useNewUrlParser: true,
useCreateIndex: true, //make this true
autoIndex: true, //make this also true
})
.then(() => {
console.log('Connected to mongoDB');
});

对于任何仍然面临这个问题的人来说,以上的答案并没有解决我的问题。真正解决这个问题的方法是使用 mongodb 指南针,然后打开集合,然后进入 index 选项卡并单击 create an index 按钮,然后在选项中勾选 create only index 复选框,然后创建该索引。

在我的情况下,有助于处理索引错误事件

const userSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true,},
password: {
type: String, required: true, trim: true, minLength: 7,
validate(value) {
if (value.includes('password')) {
}
}
},
email: {
type: String, required: true, index: true, unique: true, trim: true, lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid');
}
}
},
age: {
type: Number, default: 1,
validate(value) {
if (value <= 0) {
throw new Error('Age must be a positive number');
}
}
}
}, {
timestamps: true,
get: v => v.toDateString()
})


const User = mongoose.model('User', userSchema);
    

User.on('index', function (err) {
if (err) console.error(err); // error occurred during index creation
})

文档中的详细信息: https://mongoosejs.com/docs/api.html#model_Model.ensureIndexes

什么对我有效,放弃收藏

什么对我不起作用-重启服务器,重启 mongo

所有这些答案对我来说都不起作用(必须手动创建索引) ,这是因为 在后台创建索引和我在建立连接后立即播种数据。这意味着数据被添加到 之前,索引被创建,显然,即使没有重复,一旦发生这种情况,索引也不会被创建(根据文档,这不应该是这样,但这似乎已经发生了)。我知道有两种不用手动创建索引就能解决这个问题的方法:

  1. 第一个(也是最好的一个)方法是在创建连接之后使用 SyncIndexs ()重新构建所有集合的索引以匹配所有模式(它甚至会在过程中为您创建集合) ,并且只有在这个过程完成(。然后()或等待)您是否播种数据或处理请求。

  2. 另一种方法是使用 CreateIndex ()为您希望确保索引的特定模型创建索引; 您还必须在设定种子/处理之前这样做。

显然,还有一个用于此目的的 ensureIndexs ()方法