2010-12-02 15 views
9

Ich habe das folgende ModellWie einzigartig eingebettetes Dokument in mongoid

class Person 
    include Mongoid::Document 
    embeds_many :tasks 
end 

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

Wie kann ich folgend sicherstellen erzwingen?

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 

dh Aufgabennamen sind eindeutig innerhalb einer bestimmten Person


Nachdem die Dokumentation auf Indizes ausgecheckt dachte ich folgendes funktionieren könnte:

class Person 
    include Mongoid::Document 
    embeds_many :tasks 

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

aber

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

noch Prod benötigt ein Duplikat.


Der Index Config oben in Person gezeigt würde übersetzen in für mongodb

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

Antwort

1

Indizes standardmäßig nicht eindeutig sind. Wenn man sich die Mongo Docs hieran anschaut, ist Einzigartigkeit eine zusätzliche Flagge.

Ich weiß nicht, die genaue Mongoid Übersetzung, aber Sie suchen so etwas wie:

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

+0

Ich habe auf die Frage hinzugefügt Details, was der Index config in Person sein würde, übersetzt in in mongodb. Es enthält nicht die DropDups: Das stimmt, also schaue ich mir das an. – opsb

+0

Es scheint, dass dropDups nur verwendet wird, wenn ich zuerst einen neuen Index erstelle, damit es mein Problem nicht löst. – opsb

4

Kannst du nicht einfach einen Validator auf die Aufgabe setzen?

validates :name, :uniqueness => true 

Das sollte die Eindeutigkeit innerhalb des übergeordneten Dokuments sicherstellen.

+1

Sie sagen im Wesentlichen zu validates_uniqueness_of: name. So können eingebettete Dokumente in derselben Instanz des Modells nicht eindeutig gehalten werden. –

+7

Tatsächlich ist validates_uniqueness_of in einem eingebetteten Dokument auf das übergeordnete Dokument beschränkt. – aNoble

0

Ich glaube nicht, dass dies mit eingebetteten Dokumenten möglich ist. Ich stieß auf das gleiche Problem wie Sie und die einzige Problemumgehung, die ich fand, war ein referenziertes Dokument anstelle eines eingebetteten Dokuments zu verwenden und dann einen zusammengesetzten Index für das referenzierte Dokument zu erstellen.

Offensichtlich ist eine Eindeutigkeitserklärung nicht genug, da sie nicht vor Rassenbedingungen schützt. Ein anderes Problem, dem ich bei eindeutigen Indizes gegenüberstand, war, dass das Standardverhalten von Mongoid darin besteht, keine Fehler zu erzeugen, wenn die Validierung besteht und die Datenbank das Dokument nicht akzeptiert. Ich hatte die folgende Konfigurationsoption in mongoid.yml zu ändern:

persist_in_safe_mode: true 

Die bei http://mongoid.org/docs/installation/configuration.html

Schließlich dokumentiert ist, nach dieser Änderung, die speichern/erstellen Methoden starten wird einen Fehler werfen, wenn die Datenbank verweigert um das Dokument zu speichern. Also, werden Sie so etwas wie dieses müssen in der Lage Benutzer zu erzählen, was passiert ist:

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 

Auch das ist nicht wirklich eine gute Option, weil Sie links wieder das Raten zu welchen Bereichen wirklich den Fehler verursacht hat (und warum). Eine bessere Lösung wäre es, in MongoidError nachzusehen und entsprechend eine entsprechende Fehlermeldung zu erzeugen. Das obige war für meine Bewerbung geeignet, also ging ich nicht so weit.

0

Fügen Sie eine Validierungsprüfung hinzu, indem Sie die Anzahl der IDs eines Arrays von eingebetteten Aufgaben mit der Anzahl eines anderen Arrays mit eindeutigen IDs vergleichen.

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 

Arbeitete für mich.

-1

Sie haben zu laufen:

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

direkt auf der Datenbank

Sie scheinen darunter eine Innenseite des "aktiven Datensatz" (dh Klasse Person)

0

"Befehl create index" Sie können eine validates_uniqueness_of in Ihrem Task-Modell definieren, um sicherzustellen, dass dies gemäß der Mongoid-Dokumentation unter http://mongoid.org/docs/validation.html für den Gültigkeitsbereich des übergeordneten Dokuments gilt und Sie tun sollten, was Sie wollen.

Ihre Index-Technik sollte auch funktionieren, aber Sie müssen die Indizes generieren, bevor sie in Kraft traten. Mit Rails können Sie dies mit einer Rake-Aufgabe tun (in der aktuellen Version von Mongoid heißt sie db: mongoid: create_indexes). Beachten Sie, dass beim Speichern von Objekten, die gegen die Indexeinschränkung wegen Mongoid verstoßen, keine Fehler auftreten (weitere Informationen finden Sie unter http://mongoid.org/docs/persistence/safe_mode.html).

0

Sie auch den Index in der Modellklasse angeben:

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

und verwenden Sie die Rechen Aufgabe

rake db:mongoid:create_indexes 
Verwandte Themen