MongoDB: уникальный индекс свойства элемента массива - PullRequest
22 голосов
/ 19 июля 2011

У меня есть структура, подобная этой:

class Cat {
  int id;
  List<Kitten> kittens;
}

class Kitten {
  int id;
}

Я бы хотел запретить пользователям создавать кошку с более чем одним котенком с одинаковым идентификатором. Я попытался создать индекс следующим образом:

db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})

Но когда я пытаюсь вставить плохо отформатированного кота, Монго принимает его.

Я что-то упустил? это вообще можно сделать?

Ответы [ 4 ]

29 голосов
/ 19 июля 2011

Насколько я знаю, уникальные индексы обеспечивают уникальность только для разных документов, поэтому это приведет к ошибке дублированного ключа:

db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )

Но это разрешено:

db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )

Я не уверен, есть ли какой-нибудь способ принудительно применить необходимое ограничение на уровне Mongo, может быть, это то, что вы могли бы проверить в логике приложения при вставке обновления?

26 голосов
/ 17 ноября 2013

Обеспечение уникальности отдельных значений в поле массива

В дополнение к примеру выше, в MongoDB есть функция, обеспечивающаядобавив новый объект / значение в поле массива, он будет выполнять обновление только в том случае, если значение / объект еще не существует.

Итак, если у вас есть документ, который выглядит следующим образом:

{ _id: 123, kittens: [456] }

Это будет разрешено:

db.cats.update({_id:123}, {$push: {kittens:456}})

, что приведет к

{ _id: 123, kittens: [456, 456] }

, однако с использованием функции $ addToSet (в отличие от $push ) проверит, если значение уже существует, перед его добавлением.Итак, начиная с:

{ _id: 123, kittens: [456] }

, затем выполняя:

db.cats.update({_id:123}, {$addToSet: {kittens:456}})

Не будет никакого эффекта.

Итак, короче говоря, уникальные ограничения не проверяютсяуникальность в элементах значений поля массива, только два документа не могут иметь одинаковые значения в индексированных полях.

4 голосов
/ 17 ноября 2016

Существует эквивалент вставки с уникальностью в атрибуте массива.Следующая команда, по сути, вставляет, обеспечивая уникальность котят (upsert создает ее для вас, если объект с 123 еще не существует).

db.cats.update(
  { id: 123 },
  { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
  { upsert: true}
)

Результирующее значение объекта будет

{
    "id": 123,
    "kittens": [456],
    "otherfields": "extraval",
    "field2": "value2"
}
0 голосов
/ 28 декабря 2018

В этом случае вы можете написать собственный метод проверки Mongoose.Вы можете подключиться к проверке сообщений.Mongoose имеет проверку, и вы можете реализовать ловушку до (до) или после (после) проверки.В этом случае вы можете использовать после проверки, чтобы убедиться, что массив является действительным.Затем просто убедитесь, что массив не имеет дубликатов.Там могут быть улучшения эффективности, которые вы можете сделать на основе ваших данных.Например, если у вас есть только «_id», вы можете просто использовать функцию JS include.

catSchema.post('validate',function(next) {
    return new Promise((resolve,reject) => {
        for(var i = 0; i < this.kittens.length; i++) {
           let kitten = this.kittens[i];
           for(var p = 0; p < this.kittens.length; p++) {
              if (p == i) {
                  continue;
              }
              if (kitten._id == this.kittens[p]._id) {
                  return reject('Duplicate Kitten Ids not allowed');
              }
           }
        }
        return resolve();
    });
});

Мне нравится использовать обещания при проверке, потому что ошибки легче указывать.

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