Как применить уникальный внедренный документ в mongoid - PullRequest
9 голосов
/ 02 декабря 2010

У меня есть следующая модель

class Person 
  include Mongoid::Document
  embeds_many :tasks
end

class Task
  include Mongoid::Document
  embedded_in :commit, :inverse_of => :tasks
  field :name
end

Как я могу обеспечить следующее?

person.tasks.create :name => "create facebook killer"
person.tasks.create :name => "create facebook killer"

person.tasks.count == 1

different_person.tasks.create :name => "create facebook killer"
person.tasks.count == 1
different_person.tasks.count == 1

т.е. имена задач уникальны для конкретного человека


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

class Person 
  include Mongoid::Document
  embeds_many :tasks

  index [
      ["tasks.name", Mongo::ASCENDING], 
      ["_id", Mongo::ASCENDING]
  ], :unique => true
end

но

person.tasks.create :name => "create facebook killer"
person.tasks.create :name => "create facebook killer"

все еще производит дубликат.


Конфигурация индекса, показанная выше в Person, будет преобразована в for mongodb

db.things.ensureIndex({firstname : 1, 'tasks.name' : 1}, {unique : true})

Ответы [ 7 ]

5 голосов
/ 01 марта 2011

Разве вы не можете просто поставить валидатор на задание?

validates :name, :uniqueness => true

Это должно обеспечить уникальность в родительском документе.

1 голос
/ 02 декабря 2010

Индексы не являются уникальными по умолчанию. Если вы посмотрите на Mongo Docs , уникальность - это дополнительный флаг.

Я не знаю точного перевода на Mongoid, но вы ищете что-то вроде этого:

db.things.ensureIndex({firstname : 1}, {unique : true, dropDups : true})

0 голосов
/ 21 марта 2013

Вы также можете указать индекс в классе вашей модели:

index({ 'firstname' => 1, 'tasks.name' => 1}, {unique : true, drop_dups: true })

и использовать задачу rake

rake db:mongoid:create_indexes
0 голосов
/ 12 марта 2012

Вы можете определить validates_uniqueness_of в своей модели задач, чтобы обеспечить это, в соответствии с документацией Mongoid по адресу http://mongoid.org/docs/validation.html эта проверка применяется к области действия родительского документа и должна выполнять то, что вы хотите.

Ваша методика индексирования также должна работать, но вы должны сгенерировать индексы, прежде чем они вступят в силу.С помощью Rails вы можете сделать это с помощью задачи rake (в текущей версии Mongoid она называется db: mongoid: create_indexes).Обратите внимание, что вы не получите ошибок при сохранении чего-либо, что нарушает ограничение индекса из-за Mongoid (см. http://mongoid.org/docs/persistence/safe_mode.html для получения дополнительной информации).

0 голосов
/ 08 декабря 2011

Вы должны выполнить:

db.things.ensureIndex({firstname : 1, 'tasks.name' : 1}, {unique : true})

непосредственно в базе данных

Вы, похоже, включили "команду создания индекса" внутри своей "активной записи" (то есть класса Person)

0 голосов
/ 06 декабря 2011

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

validates_each :tasks do |record, attr, tasks|
  ids = tasks.map { |t| t._id }
  record.errors.add :tasks, "Cannot have the same task more than once." unless ids.count == ids.uniq.count
end

работал для меня.

0 голосов
/ 09 ноября 2011

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

Очевидно, что проверка уникальности не выполняется 'достаточно, поскольку это не защищает от условий гонки.Еще одна проблема, с которой я столкнулся с уникальными индексами, заключалась в том, что стандартное поведение mongoid - не вызывать ошибок, если проверка прошла успешно, а база данных отказалась принять документ.Мне пришлось изменить следующий параметр конфигурации в mongoid.yml:

persist_in_safe_mode: true

Это задокументировано в http://mongoid.org/docs/installation/configuration.html

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

alias_method :explosive_save, :save

def save
  begin
    explosive_save
  rescue Exception => e
    logger.warn("Unable to save record: #{self.to_yaml}. Error: #{e}")
    errors[:base] << "Please correct the errors in your form"
    false
  end
end

Даже это не очень хороший вариант, потому что вам остается угадывать, какие поля действительно вызвалиошибка (и почему).Лучшим решением было бы заглянуть внутрь MongoidError и создать соответствующее сообщение об ошибке соответственно.Вышесказанное подходит для моего заявления, поэтому я не зашел так далеко.

...