2010-12-02 16 views
9

He siguiendo el modeloCómo hacer cumplir documento incrustado único en MongoId

class Person 
    include Mongoid::Document 
    embeds_many :tasks 
end 

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

¿Cómo puedo asegurar lo siguiente?

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 

es decir, nombres de las tareas son únicos dentro de una persona en particular


Habiendo leído los documentos en los índices pensé que el siguiente podría funcionar:

class Person 
    include Mongoid::Document 
    embeds_many :tasks 

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

pero

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

todavía prod hace un duplicado.


El índice de configuración se muestra arriba en persona se traduciría en mongodb para

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

Respuesta

1

Los índices no son únicos por defecto. Si nos fijamos en el Mongo Docs en esto, la singularidad es una bandera adicional.

No sé la traducción exacta Mongoid, pero estás buscando algo como esto:

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

+0

He añadido detalles a la cuestión de cuál sería la configuración de índice en persona traducido en mongodb. No incluye dropDups: verdadero, así que echaré un vistazo a eso. – opsb

+0

Parece que dropDups solo se usa cuando se crea un nuevo índice por primera vez, por lo que no va a resolver mi problema. – opsb

4

no puedes poner un validador en la tarea?

validates :name, :uniqueness => true 

Esto debería garantizar la exclusividad en el documento original.

+1

Básicamente estás diciendo que hagas validates_uniqueness_of: name. Que no es cómo los documentos incrustados se pueden mantener únicos, con la misma instancia del modelo. –

+7

En realidad, validates_uniqueness_of en un documento incrustado tiene un alcance en el documento principal. – aNoble

0

No creo que esto sea posible con documentos incrustados. Me encontré con el mismo problema que usted y la única solución alternativa que encontré fue utilizar un documento referenciado, en lugar de un documento incrustado y luego crear un índice compuesto en el documento referenciado.

Obviamente, una validación de unicidad no es suficiente, ya que no protege contra las condiciones de carrera. Otro problema que enfrenté con índices únicos fue que el comportamiento predeterminado de mongoid es no generar ningún error si la validación pasa y la base de datos se niega a aceptar el documento. Tuve que cambiar la opción de configuración siguiente en mongoid.yml:

persist_in_safe_mode: true 

Esto está documentado en http://mongoid.org/docs/installation/configuration.html

Finalmente, después de hacer este cambio, el guardar/crear métodos comenzarán a lanzar un error si la base de datos se niega para almacenar el documento. Por lo tanto, necesitará algo como esto para poder informar a los usuarios acerca de lo sucedido:

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 

Incluso esto no es realmente una gran opción porque estás que decidir cuáles eran los campos que realmente la causa del error (y por qué). Una mejor solución sería mirar dentro de MongoidError y crear un mensaje de error adecuado en consecuencia. Lo anterior se adecuaba a mi aplicación, así que no llegué tan lejos.

0

Agregue una comprobación de validación, comparando el recuento de la matriz de ID de las tareas incrustadas, con el recuento de otra matriz con ID únicos de la misma.

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 

Funcionó para mí.

-1

tiene que ejecutar:

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

directamente en la base de datos

Parece que está incluido un "mandato de creación de índice" dentro de su "registro activo" (es decir, la clase Persona)

0

Puede definir un validates_uniqueness_of en su modelo de tarea para garantizar esto, de acuerdo con la documentación de Mongoid en http://mongoid.org/docs/validation.html esta validación se aplica al alcance del documento principal y debe hacer lo que desee.

Su técnica de índice debería funcionar también, pero debe generar los índices antes de que entren en vigor. Con Rails puedes hacer esto con una tarea de rake (en la versión actual de Mongoid se llama db: mongoid: create_indexes). Tenga en cuenta que no obtendrá errores al guardar algo que infringe la restricción de índice porque Mongoid (consulte http://mongoid.org/docs/persistence/safe_mode.html para obtener más información).

0

También puede especificar el índice en la clase del modelo:

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

y utilizar la tarea rake

rake db:mongoid:create_indexes 
Cuestiones relacionadas