Mongoose唯一索引不起作用!

134

我希望MongoDB可以基于其索引检测重复值。我认为这在MongoDB中是可能的,但通过Mongoose包装器似乎存在问题。所以对于像这样的东西:

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

我的问题是我可以保存两个具有相同电子邮件的用户,这很糟糕。

这个问题在这里也有提到:https://github.com/LearnBoost/mongoose/issues/56,但是那个帖子已经很旧了,并没有得到解决。

目前,我手动调用数据库来查找用户。由于“电子邮件”是索引的,所以该调用并不昂贵。但是,让它被原生处理仍然是好的选择。

有人有解决方法吗?


1
坏消息是,mongod v2.4.3和mongoose v3.6.20仍然存在问题。 - damphat
Unique 在我的一个主机上运行良好,但在另一个主机上使用完全相同的 node/mongoose 代码无法强制唯一性。正常工作的主机运行单个 mongod 3.4.10,而不正常的主机则运行具有 mongod 3.2.17 的副本集。在两个主机上,我都是从头开始创建集合,因此现有的重复项不是问题。我尝试了此页面上的大多数解决方案,其中有效的是来自 @Isaac Pak 的 mongoose-unique-validator。 - Maksym
如果您在现有的模型中添加新的唯一字段,请查看此帖子 - https://dev59.com/4GAf5IYBdhLWcg3woz9n - Nick
而解决这个问题的简单方法是重新启动 mongod 服务。sudo systemctl stop mongodsudo systemctl start mongod :) - user20816962
37个回答

139

糟糕!你需要重新启动Mongo。


4
我遇到了同样的问题,但是我找不到在OSX上如何重新启动MongoDB。当我杀死进程后,它会自动重新生成,而唯一索引仍然无法正常工作……有什么想法吗? - ragulka
12
您的意思是“哎呀,您只需要重新启动Mongo”?!您是在说没有关闭数据库就不能添加索引吗? - andrewrk
8
不对。你不需要重新启动mongo来添加索引。 - JohnnyHK
1
针对这个独特的问题,我不得不重新启动Mongo,然后它就可以工作了。谢谢! - Muhammad Ahsan Ayaz
3
我尝试了重启和重新索引,但不得不删除数据库才能解决这个问题。我猜想问题在于添加索引之前已经存在重复项,因此在生产数据库中,我需要先移除重复项,然后再重启吗? - isimmons
显示剩余3条评论

46

糟糕!你只需重新启动Mongo。

并使用以下命令重新索引:

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

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

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

27
db.dropDatabase()会清空你的数据库! - Isaac Pak
@IsaacPak 他随口说了一句,好像那是一个很普通的操作。 - ImportError

38

我遇到了同样的问题:在已经向数据库添加用户之后,将email字段的唯一约束添加到我们的UserSchema中,仍然能够保存重复的邮件地址。我通过以下步骤解决了这个问题:

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

2)从Mongo shell中执行命令:db.users.createIndex({email: 1}, {unique: true})

关于步骤1,需要注意的是根据Mongo文档的说明:

 

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

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


如何删除特定的索引? - Ali Sherafat

23

我已经做了类似这样的事情:

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()行。

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

我不确定Foo.createIndexes()是否是异步的,但据我所知,一切似乎都运行良好。


唯一的答案是解决问题而不是绕着它转。 - Saksham Gupta
这个答案帮了我很多。我所缺少的只是在我的唯一索引中加上 index: true - elcid
由于某些原因,传递unique: true可以工作,但是indexes: {...unique:true}不行。谢谢。 - darren z
对我来说最相关的解决方案。我试图用包含重复数据的数据填充数据库,尽管添加了唯一索引,但Mongoose从未阻止我这样做。现在我明白了,在填充重复数据之前必须显式创建索引,否则mongoose会跳过创建索引。记得将其异步化以便索引完全完成:async Foo.createIndexes() - Rajath Kedilaya

17

修复问题的步骤:

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

    • 不是必需的,默认情况下为true
  • 重新启动数据库

    • 不是必需的,您只需要重新启动Node.js服务器

  • 按照上述3个步骤顺序执行
  • 如果您错过了任何步骤,请重复执行2次

只是想指出,您不必删除或丢弃受影响的集合。 在第2步的位置,我运行了一个更新操作,以更新所有受影响的条目中的字段,以某种方式避免重复。这对我很有效。 - Victory Ifebhor
1
类型、唯一、必填,顺序更重要。谢谢 - undefined

14

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

为了防止这种情况发生,您可以通过以下方式处理此错误:

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

11

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

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

Shell应该以以下方式响应:

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

现在我们可以从mongoshell快速进行测试:

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\" }"
    }
})

7

它说的是删除索引而不是集合。 - Zero0Ho

7

Mongoose在应用程序级别执行唯一索引时有些宽松;因此,最好通过mongo cli从数据库本身执行您的唯一索引,或者在UserSchema之后显式告诉mongoose您对unique索引非常重视,方法是写入以下代码行:

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

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


6
来晚了,但是一个“在强制执行唯一索引时有点宽松”的ORM有什么用呢? - Imre_G
1
那些毫无帮助的贬低性评论有什么好处呢?这个解决方案是可靠且可以投入生产使用的。 - Isaac Pak
1
这实际上非常有用,谢谢。 - Brandalf

7

当以下情况出现时,Mongoose将无声地无法添加唯一索引:

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

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

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


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