-
Type: Bug
-
Resolution: Done
-
Priority: Major - P3
-
None
-
Affects Version/s: None
-
Component/s: None
-
Labels:
I've been observing some weird behavior in Mongoid involving the creation of multiple embedded documents. In this vast majority of cases this does not occur, but it does happen often enough to be a cause for concern. Here are the details of the latest case that I've found.
The parent document is called Membership:
class Membership
include Mongoid::Document
belongs_to :user
belongs_to :pool
embeds_many :picks
validates_presence_of :user_id, :pool_id
validates_uniqueness_of :user_id, scope: :pool_id
...
The embedded document is called Pick:
class Pick include Mongoid::Document include Mongoid::BackboneSerialization field :game_id, type: BSON::ObjectId field :team_id, type: BSON::ObjectId field :points, type: Integer # Cached value embedded_in :membership validates_presence_of :game, :team validates_uniqueness_of :game_id validate :enforce_immutability ... private def enforce_immutability if locked? and team_id_changed? errors.add(:team, 'cannot be changed because the pick is locked') end end end
That Mongoid::BackboneSerialization mixin is simply the following:
module Mongoid module BackboneSerialization extend ActiveSupport::Concern module InstanceMethods def serializable_hash(options = nil) persisted? ? super.merge('id' => _id) : super end end end end
So, as for the occurrence of the behavior I'm current observing:
ruby-1.9.2-p290 :001 > membership = Membership.find('4e68f7607d2c290001000147') => #<Membership _id: 4e68f7607d2c290001000147, _type: nil, user_id: BSON::ObjectId('4e68f72a7d2c290001000142'), pool_id: BSON::ObjectId('4e682217749462000100009d')> ruby-1.9.2-p290 :002 > picks = membership.picks.where(_id: '4e73e54f9775e50001000003') => #<Mongoid::Criteria selector: {:_id=>BSON::ObjectId('4e73e54f9775e50001000003')}, options: {}, class: Pick, embedded: true> ruby-1.9.2-p290 :003 > picks.length => 6 ruby-1.9.2-p290 :004 > picks.first => #<Pick _id: 4e73e54f9775e50001000003, _type: nil, game_id: BSON::ObjectId('4e5d08ae7c0b5f0001000064'), team_id: BSON::ObjectId('4e5d01517c0b5f0001000009'), points: nil> ruby-1.9.2-p290 :005 > picks.first.valid? => true ruby-1.9.2-p290 :006 > membership.picks.create!(game_id: '4e5d08ae7c0b5f0001000064', team_id: '4e5d01517c0b5f0001000009') Mongoid::Errors::Validations: Validation failed - Game is already taken.
Also, I confirmed through the mongo shell the there are indeed 6 embedded documents with the same _id
All interaction in the application with the memberships collection and the embedded picks (and, for that matter, MongoDB in general) is through documented Mongoid APIs (#save, #build, #create, #update_attributes). The application is quite simple, and I'm not doing anything that would be considered even slightly exotic.
I'm running Ruby 1.9.2-p290, Rails 3.1, Mongo (gem) 1.3.1, Mongoid 2.2.0, MongoDB 1.8.2.
I've only encountered this in the Heroku production environment (Cedar stack), where I'm using MongoHQ (also on 1.8.2).
Any help would be greatly appreciated. Thanks!