Mongoose Уникальный индекс не работает! - PullRequest
72 голосов
/ 04 апреля 2011

Я пытаюсь позволить MongoDB обнаружить дублирующееся значение на основе его индекса. Я думаю, что это возможно в MongoDB, но через оболочку Mongoose все кажется сломанным. Так что-то вроде этого:

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

Я могу сохранить 2 пользователей с одним и тем же адресом электронной почты. Чёрт.

Та же проблема была высказана здесь: https://github.com/LearnBoost/mongoose/issues/56, но этот поток старый и ведет в никуда.

Пока я вручную звоню в БД, чтобы найти пользователя. Этот звонок не дорогой, так как «электронная почта» проиндексирована. Но все равно было бы неплохо разрешить исходную обработку.

У кого-нибудь есть решение этого вопроса?

Ответы [ 17 ]

76 голосов
/ 04 апреля 2011

Oops!Вам просто нужно перезапустить Монго.

40 голосов
/ 29 октября 2012

Oops! Вам просто нужно перезапустить Монго.

И переиндексировать тоже!

В тестировании я просто сделал:

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

Хотя, если у вас есть важные данные, вам, вероятно, просто нужно:

mongo <db-name>
> db.<collection-name>.reIndex()
17 голосов
/ 13 октября 2016

Я столкнулся с той же проблемой: я добавил уникальное ограничение для поля email в нашу UserSchema после того, как уже добавил пользователей в базу данных, и все еще мог сохранять пользователей с ложными электронными письмами. Я решил это, выполнив следующее:

1) Удалить все документы из коллекции пользователей.

2) Из оболочки mongo выполните команду: db.users.createIndex({email: 1}, {unique: true})

Относительно шага 1, обратите внимание, что из документов Монго:

MongoDB не может создать уникальный индекс для указанных полей индекса, если коллекция уже содержит данные, которые нарушают ограничение уникальности для индекса.

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

11 голосов
/ 04 января 2014

Такое поведение также происходит, если вы оставили несколько дубликатов в Монго.Mongoose попытается создать их в Mongo при запуске приложения.

Чтобы предотвратить это, вы можете обработать эту ошибку следующим образом:

yourModel.on('index', function(err) {
  if (err?) {
    console.error err
  }
);
9 голосов
/ 11 декабря 2014

Хорошо, я смог решить эту проблему из 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\" }"
    }
})
5 голосов
/ 26 ноября 2017

Mongoose немного неэффективен при применении уникальных индексов на уровне приложений; поэтому желательно либо принудительно применять ваши уникальные индексы из самой базы данных, используя mongo cli, либо явно сказать mongoose, что вы серьезно относитесь к индексу unique, написав следующую строку кода сразу после вашей UserSchema:

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

Это обеспечит применение уникального индекса в полях username и email в вашей пользовательской схеме. Приветствия.

4 голосов
/ 04 августа 2016

Согласно документации: https://docs.mongodb.com/v2.6/tutorial/modify-an-index/

Чтобы изменить существующий индекс, вам нужно удалить и заново создать индекс.

НЕ ПЕРЕЗАПУСКАЙТЕ МОНГО!

1 - сбросить коллекцию

db.users.drop()

2 - переиндексировать таблицу

db.users.ensureIndex({email: 1, type: 1}, {unique: true})
4 голосов
/ 19 сентября 2018

Я сделал что-то вроде этого:

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 вроде бы работает нормально

2 голосов
/ 21 марта 2017

мангуст уникальный-валидатор

Как использовать этот плагин:

1) npm install --save mongoose-unique-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. без учета регистра

    используйте параметр uniqueCaseInsensitive в вашей схеме

    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.

За исключением автоматической блокировки коллекции или форсирования одного соединения, реального решения не существует.

Для большинства наших пользователей это не будет проблемой, но это крайний случай, о котором следует знать.

1 голос
/ 20 октября 2017

Самый новый ответ: вообще не нужно перезапускать mongodb, если у colleciton уже есть такие же индексы имен, mongoose больше не будет воссоздавать ваши индексы, поэтому сначала удалите существующие индексы colleciton, а теперь, когда вы запустите mongoose, он будетсоздать новый индекс, выше процесс решил мою проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...